您的位置:首页 > 编程语言 > Java开发

开发工具系列(一):Btrace——线上Debug工具

2017-06-01 17:29 447 查看
原文地址:http://blog.csdn.net/caipeichao2/article/details/42109429

Btrace

Btrace用于调试正在运行的系统,并且在调试时不会暂停系统。特别适用于跟踪线上问题。你可以实时监控一个系统中任何一个方法的调用,你可以知道这些方法的参数、返回值是什么,还可以知道方法调用消耗了多少时间。

Btrace不需要安装,只要下载一个包,解压即可。

Btrace用法为
bin/btrace <pid> <trace-script>
。其中
pid
是正在运行的Java进程,
trace-script
是跟踪脚本,它其实就是一段java代码。

Hello World

首先我们模拟一个正在运行的程序,它仅有一个循环。
package com.caipeichao; public class NullApp {     public static void main(String[] argv) {        new NullApp().run();    }     public void run() {        for (int i = 0; i < 100000; i++) {            sleep(1000);            new MyObj().life(i);        }    }     private static class MyObj {         public void life(int n) {            System.out.println(n);        }    }     private void sleep(int n) {        try {            Thread.sleep(n);        } catch (InterruptedException e) {        }    }}

然后开启这个程序: 
Java com.caipeichao.NullApp


通过jps命令得到这个程序的PID,这里为13348。
> jps3034 RemoteMavenServer2902 Main15147 Jps13348 NullApp

准备工作做完了,现在编写最重要的跟踪脚本。
import static com.sun.btrace.BTraceUtils.*;import com.sun.btrace.annotations.*; @BTracepublic class HelloBtrace {  // 当com.caipeichao.NullApp.sleep方法返回时,执行该方法   @OnMethod(clazz="com.caipeichao.NullApp",    method="sleep",    location=@Location(Kind.RETURN))  public static void onSleep() {    println("Hello world");  }}

运行btrace,得到如下输出。
> btrace 13348 HelloBtrace.javaHello worldHello worldHello worldHello worldHello worldHello world

常用注解

名称作用域作用
@BTrace
声明跟踪脚本
@OnMethod(clazz,method,location)
方法当指定方法被调用时
@OnMethod(method="<init>")
方法当构造函数被调用时
@OnMethod(clazz="/java\\.io\\..*Input/"))
方法方法名称正则匹配
@Location(kind)
@OnMethod
指定监控方法调用前还是调用后
@Location(value=Kind.NEWARRAY, clazz="char")
@OnMethod
监控新增数组
@Self
参数表示被监控的对象
@ProbeMethodName
参数被监控的方法名称
@ProbeClassName
参数被监控的类名
@OnTimer(interval)
方法定时调用某个方法
@OnLowMemory(pool,threshold)
方法当内存不足时
@OnExit
方法当程序退出时
@OnProbe(namespace="java.net.socket",name="bind")
方法监控socket中的bind方法

常用方法

方法作用
println
在本地控制台输出一行
print
在本地控制台输出
printArray
在本地控制台输出数组
jstack
打印远程方法的调用调用栈
jstackAll
输出所有线程的调用栈
exit
退出跟踪脚本
Strings.strcat
连接字符串
Reflactive.name
获取类名
Threads.name
线程名
Threads.currentThread
当前线程
deadlocks
打出死锁线程
sizeof
获取对象的大小,比如
List
对象就返回
List.size()
Sys.Env.property
获取系统变量

原理

BTrace利用了
java.lang.instrument
包实现代码注入。首先通过
VirtualMachine.attach(pid)
连接远程JVM,然后通过
VirtualMachine.loadAgent("*.jar")
加载一个btrace的jar包。这个jar包最重要的代码如下。
public static void premain(String args, Instrumentation inst) {  main(args, inst);} public static void agentmain(String args, Instrumentation inst) {  main(args, inst);} // 将btrace的jar包添加到ClassLoader搜索目录 private static synchronized void main(final String args, final Instrumentation inst) {  ...  inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(path)));  ...  inst.appendToSystemClassLoaderSearch(new JarFile(new File(path)));  ...  startServer();} // 开启服务 private static void startServer() {  ...  while (true) {    try {      ...      handleNewClient(client);    } catch (RuntimeException re) {      if (isDebug()) debugPrint(re);    } catch (IOException ioexp) {      if (isDebug()) debugPrint(ioexp);    }  }} // 修改内存中的类定义 private static void handleNewClient(final Client client) {  ...  inst.addTransformer(client, true);  ...  inst.retransformClasses(classes);} // 用ASM动态生成字节码 abstract class Client implements ClassFileTransformer, CommandListener {  static {    ClassFilter.class.getClass();    ClassReader.class.getClass();    ClassWriter.class.getClass();    ...  }   private byte[] instrument(Class clazz, String cname, byte[] target) {    byte[] instrumentedCode;    try {    ClassWriter writer = InstrumentUtils.newClassWriter(target);    ClassReader reader = new ClassReader(target);    Instrumentor i = new Instrumentor(clazz, className,  btraceCode, onMethods, writer);    ...  }}

一句话总结,btrace利用instrument工具修改JVM内存中的类字节码,达到注入代码的目的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java btrace