该篇文章为译稿,原稿在:http://kenai.com/projects/btrace/pages/UserGuide#btrace_anno
包括本文提到的sample代码也在原稿中有连接。
Btrace的API在:http://btrace.kenai.com/javadoc/1.2/index.html
BTrace(https://btrace.dev.java.net/ ) 是一个非常不错的 java 诊断工具 , 最近试着用了一下 , 文档比较少 , 主要是看例子吧 .
BTrace 中的 B 表示 bytecode, 表明它是在字节码层面上对代码进行 trace
用来在运行中的 java 类中注入 trace 代码 , 并对运行中的目标程序进行热交换 (hotswap)
btrace还提供了VisualVM Plugin 以及Netbeans Plugin
术语
Probe Point
在何处执行 trace 语句 , 这里的 " 何处 " 可以是具体的跟踪地点和执行事件 , 在 BTrace 中通过各种注解来指定
Trace Actions or Actions
在何时执行 trace 语句
Action Methods
定义在 trace 脚本中的 trace 语句 , 具体来说就是脚本中的无返回值静态方法 (1.2 之后可以是非静态方法 )
BTrace 限制
为了保证 trace 语句只读 , BTrace 对 trace 脚本有一些限制 ( 比如不能改变被 trace 代码中的状态 )
· BTrace class 不能新建类 , 新建数组 , 抛异常 , 捕获异常 ,
· 不能调用实例方法以及静态方法 (com.sun.btrace.BTraceUtils 除外 )
· 不能将目标程序和对象赋值给 BTrace 的实例和静态 field
· 不能定义外部 , 内部 , 匿名 , 本地类
· 不能有同步块和方法
· 不能有循环
· 不能实现接口 , 不能扩展类
· 不能使用 assert 语句 , 不能使用 class 字面值
BTrace步骤
1.取得目标java进程id(pid)
2.编写BTrace脚本
3.执行命令行: btrace <pid> <自己定制的脚本> <输出文件>
eg:btrace 3045PrintExecuteTime.java > time.log
完整的BTrace命令:
预编译BTrace脚本命令
参数和上面大同小异, btracec 是一个类似javac的程序, 使用该程序编译, 将根据BTrace的限制条件进行严格检查
在目标程序中启动BTrace Agent
这个主要针对需要在目标程序启动的时候就需要trace其行为的场景, 此时BTrace agent将与目标程序一起启动(前提是必须对BTrace脚本进行预编译)
命令行:
方法上的注解
参数上的注解
未被注解的方法参数
未使用注解的方法参数一般都是用来做方法签名匹配用的, 他们一般和被trace方法中参数出现的顺序一致. 不过他们也可以与注解方法交错使用, 如果一个参数类型声明为*AnyType[]*, 则表明它按顺序"通吃"方法所有参数. 未注解方法需要与*Location*结合使用:
属性上的注解
类上的注解
脚本举例如下
1.监控方法参数(数组)
- import static com.sun.btrace.BTraceUtils.print;
- import static com.sun.btrace.BTraceUtils.printArray;
- import static com.sun.btrace.BTraceUtils.println;
- import static com.sun.btrace.BTraceUtils.probeClass;
- import static com.sun.btrace.BTraceUtils.probeMethod;
- import com.sun.btrace.annotations.BTrace;
- import com.sun.btrace.annotations.OnMethod;
- @BTrace
- public class PrintArgArray {
- /**
- * 此方法打印出Test类中的mergeArray(Long[] arrayOne, Long[] arrayTwo)方法传入的参数
- * 参数名字一定要和监控对象的方法参数名字一致
- *
- * @param arrayOne 监控参数一
- * @param arrayTwo 监控参数二
- * @author jerry
- */
- @OnMethod (clazz = "com.jerry.test.Test" , method = "mergeArray" )
- // 此处写明要监控的包、类、方法等 可以使用正则匹配
- public static void anyRead(Long[] arrayOne, Long[] arrayTwo) {
- // 打印监控的类名
- print(probeClass());
- print(" [" );
- // 打印监控的方法名
- print(probeMethod());
- println("]" );
- if (arrayOne != null ) {
- printArray(arrayOne);
- } else {
- println("the arguments is null!" );
- }
- if (arrayTwo != null ) {
- printArray(arrayTwo);
- } else {
- println("the arguments is null!" );
- }
- }
- }
2.监控使用时间
- import static com.sun.btrace.BTraceUtils.name;
- import static com.sun.btrace.BTraceUtils.print;
- import static com.sun.btrace.BTraceUtils.println;
- import static com.sun.btrace.BTraceUtils.probeClass;
- import static com.sun.btrace.BTraceUtils.probeMethod;
- import static com.sun.btrace.BTraceUtils.str;
- import static com.sun.btrace.BTraceUtils.strcat;
- import static com.sun.btrace.BTraceUtils.timeMillis;
- import com.sun.btrace.annotations.BTrace;
- import com.sun.btrace.annotations.Kind;
- import com.sun.btrace.annotations.Location;
- import com.sun.btrace.annotations.OnMethod;
- import com.sun.btrace.annotations.TLS;
- /**
- * 监控方法耗时
- *
- * @author jerry
- */
- @BTrace
- public class PrintTimes {
- /**
- * 开始时间
- */
- @TLS
- private static long startTime = 0 ;
- /**
- * 方法开始时调用
- */
- @OnMethod (clazz = "/com\\.jerry\\../" , method = "/.+/" )
- public static void startMethod() {
- startTime = timeMillis();
- }
- /**
- * 方法结束时调用<br>
- * Kind.RETURN这个注解很重要
- */
- @SuppressWarnings ( "deprecation" )
- @OnMethod (clazz = "/com\\.jerry\\../" , method = "/.+/" , location = @Location (Kind.RETURN))
- public static void endMethod() {
- print(strcat(strcat(name(probeClass()), "." ), probeMethod()));
- print(" [" );
- print(strcat("Time taken : " , str(timeMillis() - startTime)));
- println("]" );
- }
- }
3.监控内存
- import static com.sun.btrace.BTraceUtils.*;
- import java.lang.management.MemoryUsage;
- import com.sun.btrace.annotations.BTrace;
- import com.sun.btrace.annotations.OnLowMemory;
- /**
- * 监控内存使用
- *
- * @author jerry
- */
- @BTrace
- public class PrintMemory {
- /*
- * 指定内存区域低于一定的界限的时候才内存使用打印数据<br> 也可以指定时间间隔打印内存使用
- */
- @OnLowMemory (pool = "Tenured Gen" , threshold = 6000000 )
- public static void printMem(MemoryUsage mu) {
- print("MemoryUsage : " );
- println(mu);
- print("FreeMem : " );
- println(freeMemory());
- print("Heap:" );
- println(heapUsage());
- print("Non-Heap:" );
- println(nonHeapUsage());
- }
- }