BTrace是一个安全动态的跟踪java程序的工具。BTrace通过hotswap技术,动态地注入字节码到运行的java程序中。
术语
- Probe Point
在何处执行我们希望注入的trace语句, 这里的”何处”可以是具体的跟踪地点和执行事件,。在BTrace中通过各种注解来指定 - Trace Actions or Actions
我们希望执行的trace语句 - Action Methods
定义在trace脚本中的trace语句, 具体来说就是脚本中的无返回值静态方法
BTrace 程序结构
BTrace程序是由一个或多个如以下示例一样拥有BTrace注释的方法构成的。
public static void
BTrace注释是用来标识需要跟踪的地方(Probe Point)。跟踪的行为在静态的方法体里面定义,这些方法被称为“action”方法
BTrace限制
为了保证trace语句对目标跟踪程序的侵入是只读的(不能改变目标跟踪程序的状态)且有限的(跟踪脚本代码必须在有限的时间内完成), BTrace对trace脚本有一些限制
BTrace class不能新建类, 新建数组, 抛异常, 捕获异常,
不能调用实例方法以及静态方法(com.sun.btrace.BTraceUtils除外)
不能将目标程序的类和对象赋值
不能定义外部, 内部, 匿名, 本地类
不能有同步块和方法
不能有循环
不能实现接口, 不能扩展类
不能使用assert语句, 不能使用class字面值
简单的BTrace示例
- 下载BTrace包
https://kenai.com/projects/btrace/downloads/directory/releases
我下载的版本是 release-1.2.5.1 解压缩
hsu@ubuntu:~/btrace$ ls bin COPYRIGHT README.txt btrace-bin.tar.gz docs samples build LICENSE.txt THIRDPARTYLICENSEREADME.txt
打开bin文件夹执行如下语句
chmod +x btrace
目标程序内容
package com.btrace.example; import java.util.Random; public class Case1 { public static void main(String[] args) throws Exception { Random random = new Random(); CaseObject object = new CaseObject(); boolean result = true; while (result) { result = object.execute(random.nextInt(1000)); Thread.sleep(1000); } } } package com.btrace.example; public class CaseObject { private static int sleepTotalTime = 0; public boolean execute(int sleepTime) throws Exception { System.out.println("sleep: " + sleepTime); sleepTotalTime += sleepTime; Thread.sleep(sleepTime); return true; } }
btrace脚本
package com.btrace.example; import static com.sun.btrace.BTraceUtils.*; import com.sun.btrace.annotations.*; @BTrace public class TraceMethodArgsAndReturn { @OnMethod(clazz = "com.btrace.example.CaseObject", method = "execute", location = @Location(Kind.RETURN)) public static void traceExecute(@Self CaseObject instance, int sleepTime, @Return boolean result) { println("call CaseObject.execute"); println(strcat("sleepTime is:", str(sleepTime))); println(strcat("sleepTotalTime is:", str(get(field("com.btrace.example.CaseObject", "sleepTotalTime"), instance)))); println(strcat("return value is:", str(result))); } }
package com.btrace.example; import static com.sun.btrace.BTraceUtils.*; import com.sun.btrace.annotations.*; @BTrace public class TraceMethodExecuteTime { @TLS static long beginTime; @OnMethod(clazz = "com.btrace.example.CaseObject", method = "execute") public static void traceExecuteBegin() { beginTime = timeMillis(); } @OnMethod(clazz = "com.btrace.example.CaseObject", method = "execute", location = @Location(Kind.RETURN)) public static void traceExecute(int sleepTime, @Return boolean result) { println(strcat( strcat("CaseObject.execute time is:", str(timeMillis() - beginTime)), "ms")); } }
执行BTrace脚本
首先取得目标java进程id(pid), 然后执行命令行: btrace
完整的BTrace命令:btrace [-I <include-path>] [-p <port>] [-cp <classpath>] <pid> <btrace-script> [<args>]
-I 没有这个表明跳过预编译
include-path: 指定用来编译脚本的头文件路径(关于预编译可参考例子ThreadBean.java)
port : btrace agent端口, 默认是2020
classpath : 编译所需类路径, 一般是指btrace-client.jar等类所在路径
pid : java进程id
btrace-script: btrace脚本, 如果是java文件, 则是未编译, class文件, 则是已编译过的
args: 传递给btrace脚本的参数, 在脚本中可以通过 (), length()来获取这些参数(定义在BTraceUtils中)
编译好后的btrace脚本 不用指定classpath
hsu@ubuntu:~/workspace_btrace$ /home/hsu/btrace/bin/btrace 6786 /home/hsu/workspace_btrace/BtraceExample/bin/com/btrace/example/TraceMethodArgsAndReturn.class
btrace脚本源代码 需要指定classpath
hsu@ubuntu:~/workspace_btrace$ /home/hsu/btrace/bin/btrace -cp /home/hsu/workspace_btrace/BtraceExample/bin/ 6786 /home/hsu/workspace_btrace/BtraceExample/src/com/btrace/example/TraceMethodArgsAndReturn.java
官网上还有很多BTrace的example
有一个很好的实际应用中的例子 http://blog.csdn.net/mgoann/article/details/7268657
实现原理
这篇文章http://agapple.iteye.com/blog/1005918讲的很好,建议大家去看看