BTrace 用户指南

原文: BTrace-usersguide

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

  1. 找到 Java 进程的 ID。可以通过 jps 工具。
  2. 编写 BTrace 程序。
  3. 在命令行运行下面的命令: 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:标记参数持有 thisself 的值。

  • @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

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.