BTrace 是用于 Java 的安全、动态跟踪工具。BTrace 插入跟踪动作到正在运行的 Java 程序类的字节码并热替换被跟踪程序的类。
BTrace 术语
- probe point,探查点:一组位置或事件,跟踪语句执行的地方。位置或事件是我们希望执行一些跟踪语句的感兴趣的地方。
- trace action,跟踪动作:跟踪语句是探查被触发时执行的。
- action method,动作方法:当探查被触发时,被执行的 BTrace 跟踪语句定义在类的一个静态方法里。这样的方法被称为 动作 方法。
BTrace 程序结构
BTrace 程序是一个普通的 Java 类,有一个或多个标记有 BTrace 注解的 public static void
方法。注解用于指定跟踪程序的 位置。跟踪动作指定在静态方法体里。这些静态方法被称为 动作 方法。
BTrace 的限制
为了保证跟踪动作是 只读 且受限的,BTrace 只允许做一些严格受限的动作。通常,BTrace 类:
- 不能 创建新对象。
- 不能 创建新数组。
- 不能 抛出异常。
- 不能 捕获异常。
- 不能 调用任意实例或静态方法。只能调用
com.sun.btrace.BTraceUtils
类的public static
方法。 - 不能 给目标程序的类或变量的静态或实例字段赋值。但是 BTrace 类可以给它自己的静态字段赋值(跟踪状态可以改变)。
- 不能 有实例字段或方法。BTrace 类只能有
public static void
方法,所有的字段只能是静态的。 - 不能 有外部、内部、嵌套类或本地类。
- 不能 有循环(
for
,while
,do ... while
)。 - 不能 继承任意类(父类必须是
java.lang.Object
)。 - 不能 实现接口。
- 不能 包含
assert
语句。 - 不能 使用类字面量。
运行 BTrace
- 找到 Java 进程的 ID。可以通过 jps 工具。
- 编写 BTrace 程序。
- 在命令行运行下面的命令:
btrace <pid> <btrace-script>
BTrace 命令行
BTrace 通过命令行工具 btrace
来运行,如下:
btrace [-I <include-path>] [-p <port>] [-cp <classpath>] <pid> <btrace-script> [<args>]
说明:
include-path
是一组用于搜索头文件的包含目录。BTrace 包含一个简单的预处理,支持#define
,#include
和条件编译。它不完全像 C/C++ 的预处理器 — 但也是有用的子集。参考ThreadBean.java
示例。如果-I
没有指定,BTrace 跳过预处理发起阶段。port
是 BTrace 代理监听的端口。可选参数。classpath
是目录、jar 文件的集合,BTrace 在编译时搜索类。默认是.
。pid
是要跟踪的 Java 程序的进程 ID。trace-script
是跟踪程序。如果是个.java
,它将在提交前被编译。否则,它将被假设为已经编译了的,并提交。
选项:
port
BTrace 代理用于监听客户端的 服务器套接字端口。默认是 2020 。path
用于编译 BTrace 程序的 classpath。默认是.
。args
传递给 BTrace 程序的命令行参数。BTrace 可以通过内置的函数$
和$length
来访问他们。
预编译 BTrace 脚本
可以通过 btracec
脚本预编译 BTrace 程序。btracec 类似于 javac 程序,接收一个 BTrace 程序,生成一个 .class 文件。
btracec [-I <include-path>] [-cp <classpath>] [-d <direcotry>] <one-or-more-BTrace-.java files>
说明:
- direcoryt 是编译生成的 .class 文件的存放路径。默认
.
。
带 BTrace 代理启动应用程序
前面讲的是用跟踪正在运行的 Java 程序,也可以在启动应用程序时带上 BTrace 代理。如果想在应用程序已启动时就进行跟踪,你可能需要带上 BTrace 代理去启动应用,并一起指定跟踪脚本。可以用下面的命令启动应用并指定 BTrace 脚本文件。但这种使用方式需要提前编译 BTrace 脚本。
java -javaagent:btrace-agent.java=script=<pre-compiled-script> <MainClass> <AppArguments>
当以这种方式启动应用时,跟踪输出将输出到当前目录的名为 <btrace-class-file-name>.btrace
的文件。你可以通过给 BTrace 代理指定参数 noServer=true
来避免启动给远程 BTrace 客户端的服务(用于远程提交跟踪脚本)。
下面是完成上述操作的便捷脚本,称为 btracer
:
btracer <pre-compiled-btrace.class> <application-main-class> <application-args>
BTrace 注解
方法注解
-
@com.sun.btrace.annotations.OnMethod
:这个注解可用于指定目标类、目标方法和方法内的位置。当匹配到的方法到达指定的位置时,标记有这个注解的动作方法将被执行。在OnMethod
注解里,跟踪类的名字由clazz
属性指定,方法由method
属性指定。clazz
可以是全路径类名(或正则表达式)。正在表达式用//
来指定,所有匹配的类都将被增强,例如/java\\.wat\\..+/
将匹配所有java.awt
包下的类。同样,方法名也可以通过正则表达式来指定。还有另一种方式可以指定要跟踪的类和方法:被跟踪的类和方法可以指定注解。例如,如果clazz
属性指定为@java.jws.WebService
,BTrace 将对所有标记有WebService
注解的方法进行增强。同样,方法级别也可以用注解来指定。可以组合使用注解和正在表达式,例如@/com\\.acme\\..+/
将匹配标记了匹配注解的任意类。还可以通过指定父类来匹配多个子类,例如+java.lang.Runnable
将匹配所有实现了java.lang.Runnable
接口的类。 -
@com.sun.btrace.annotations.OnTimer
:该注解用于指定跟踪的动作每隔 N 毫秒执行一次。时间间隔通过 long 型的value
属性指定。 -
@com.sun.btrace.annotations.OnError
:该注解用于指定当同一个 BTrace 类的其他探查的动作抛出任意异常时执行的动作。 -
@com.sun.btrace.annotations.OnExit
:当 BTrace 代码调用内建函数exit(int)
以结束跟踪会话时执行的动作。 -
@com.sun.btrace.annotations.OnEvent
: -
@com.sun.btrace.annotations.OnLowMemory
:当跟踪的内存超出指定的阈值时执行的动作。
@BTrace
public class MemAlerter {
@OnLowMemory(
pool = "Tenured Gen",
threshold=6000000
)
public static void onLowMem(MemoryUsage mu) {
println(mu);
}
}
@com.sun.btrace.annotations.OnProbe
:
参数注解
-
@com.sun.btrace.annotations.Self
:标记参数持有this
或self
的值。 -
@com.sun.btrace.annotations.Return
:标记参数持有方法的返回值。 -
@com.sun.btrace.annotations.CallInstance
:标记参数持有被调用实例的引用。 -
@com.sun.btrace.annotations.CalledMethod
:标记参数持有被调用方法的名字。
注:比如 A 类的方法 methodA
调用 B 类的方法 methodB
,那么上述的 Self
引用的是 A 类的实例,CalledInstance
引用的是 B 类的实例,calledMethod
引用的是 methodB
。
字段注解
-
@com.sun.btrace.annotations.Export
注解与 BTrace 字段(静态字段)一起使用,用于指定字段映射到 JVM 状态计数器。通过它,BTrace 程序可以暴露跟踪计数器给外部 JVM 状态客户端(例如jstat
)。 -
@com.sun.btrace.annotations.Property
注解用于标记指定(静态)字段为 MBean 的属性。如果 BTrace 类有至少一个有@Property
属性的静态字段,那么将创建一个 MBean 并注册到平台的 MBean 服务器上。JMX 客户端如 VisualVM, jconsole 可以查看这些 BTrace MBean 。 -
@com.sun.btrace.annotations.TLS
注解用在 BTrace 静态字段上,用于指定线程本地变量。每个 Java 线程得到一个独立的这个字段的拷贝。这些 BTrace 程序使用的线程本地变量可用于标识是否在同一个线程里到达多个探查动作。
类注解
-
@com.sun.btrace.annotations.DTrace
注解用于关联一个简单的单行 D-script (内联到 BTrace 的 Java 类)。 -
@com.sun.btrace.annotations.DTraceRef
注解用于关联一个 D-script (存储在独立的文件里)。 -
@com.sun.btrace.annotations.BTrace
注解必须用于给定的 Java 类以作为 BTrace 程序。这个注解是 BTrace 编译器和 BTrace 代理强制要求的。
DTrace 集成
Solaris DTrace 是动态、安全的 Solaris 程序跟踪工具,可用于内核和用户态程序。Because of the obvious parallels b/w DTrace and BTrace, it is natural to expect integration b/w BTrace and DTrace. There are two ways in which BTrace is integrated with DTrace.
- BTrace program can raise a DTrace probe – by calling dtraceProbe — see BTraceUtils javadoc referred above. For this feature to work, you need to be running on Solaris 10 or beyond. For other platforms (Solaris 9 or below or any other OS), dtraceProbe() will be a no-op.
- BTrace program can associate a D-script with it– by @DTrace annotation (if the D-script is a simple one liner) or by @DTraceRef if the D-script is longer and hence stored outside of the BTrace program. See DTrace integration samples in the BTrace samples section below. This feature works using the DTrace Java API. For this DTrace feature to work (o.e., being able to run associated D-script), you need to be running on Solaris 11 build 35 or beyond. You may want to check whether you have /usr/share/lib/java/dtrace.jar on your machine or not. @DTrace and @DTraceRef annotations are ignored on other platforms (Solaris 10 or below or any other OS).
实例代码
实例代码请查看 samples
欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。