BTrace 指南

BTrace使用简介

http://rdc.taobao.com/team/jm/archives/509

BTrace 指南  

http://linmingren2003.blog.163.com/blog/static/56751003201121871725139/


BTrace使用简介

很多时候在online的应用出现问题时,很多时候我们需要知道更多的程序的运行细节,但又不可能在开发的时候就把程序中所有的运行细节都打印到日志上,通常这个时候能采取的就是修改代码,重新部署,然后再观察,但这种方法对于online应用来说不是很好,另外一方面如果碰到不好改的代码,例如引用的其他的外部的包什么的,就很麻烦了,BTrace就是一个可以在不改代码、不重启应用的情况下,动态的查看程序运行细节的工具,其官方网站在此:http://kenai.com/projects/btrace/ ,在这篇blog中,就来看看如何用BTrace来动态的监测方法的一些运行细节状况。
BTrace通过动态的挂接用java写的代码到运行时上来获取到一些运行细节,例如典型的使用btrace的方法为:
btrace -cp [btrace的jar所在的路径,默认为btrace/build下] [pid] [需要运行的java代码]
例如一段这样的代码:

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);
      }
   }
 
}
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;
   }
 
}

如在程序运行的情况下,想知道调用CaseObject的execute方法的以下几种情况,在BTrace中可以这么做:
1、调用此方法时传入的是什么参数,返回的是什么值,当时sleepTotalTime是多少?
BTrace脚本如下:

import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
 
@BTrace public class TraceMethodArgsAndReturn{
   @OnMethod(
      clazz="CaseObject",
      method="execute",
      location=@Location(Kind.RETURN)
   )
   public static void traceExecute(@Self CaseObject instance,int sleepTime,@Returnboolean result){
     println("call CaseObject.execute");
     println(strcat("sleepTime is:",str(sleepTime)));
     println(strcat("sleepTotalTime is:",str(get(field("CaseObject","sleepTotalTime"),instance))));
     println(strcat("return value is:",str(result)));
   }
}

然后直接执行btrace -cp btrace/build [pid] TraceMethodArgsAndReturn.java就可以了。
当程序中调用到caseobject的execute方法时,就会在btrace的console中输出相应的信息。
2、execute方法执行耗时是多久?
BTrace脚本如下:

    import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
 
@BTrace public class TraceMethodExecuteTime{
 
   @TLS static long beginTime;
 
   @OnMethod(
      clazz="CaseObject",
      method="execute"
   )
   public static void traceExecuteBegin(){
     beginTime=timeMillis();
   }
 
   @OnMethod(
      clazz="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"));
   }
}

3、谁调用了execute方法?
BTrace脚本如下:

import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
 
@BTrace public class TraceMethodCallee{
   @OnMethod(
      clazz="CaseObject",
      method="execute"
   )
   public static void traceExecute(){
     println("who call CaseObject.execute :");
     jstack();
   }
}

4、有没有人调用CaseObject中的哪一行代码?
BTrace脚本如下:

import static com.sun.btrace.BTraceUtils.*;
import com.sun.btrace.annotations.*;
 
@BTrace public class TraceMethodLine{
   @OnMethod(
      clazz="CaseObject",
      location=@Location(value=Kind.LINE,line=5)
   )
   public static void traceExecute(@ProbeClassName String pcn,@ProbeMethodNameString pmn,int line){
     println(strcat(strcat(strcat("call ",pcn),"."),pmn));
   }
}

从上面可看出,在有了BTrace后,要动态的跟踪代码的运行细节还是非常爽的,更多的细节的操作请大家查看BTrace的User Guide



BTrace 指南  

2011-03-18 19:18:11|  分类: java|字号 订阅

我是从淘宝开发人员的博客里了解到有BTrace这样一个工具的。最近自己也用它来解决了一个小问题,确实觉得有用,没事干,就把官方的用法指南翻译出来。

版本 1.2 (20101020)

BTrace 是一个可靠的,用来动态跟踪Java程序的工具。它通过动态对运行中的Java程序进行字节码生成来工作。BTrace会对运行中的Java程序的类插入一些跟踪操作 来对被跟踪的程序进行热替换。

BTrace 名词
探测点 (Probe Point)
就是一系列的跟踪语句被执行的“地方”或者“事件”。探测点就是我们想要执行一些跟踪语句的地方或者事件。

跟踪动作或简称动作 (Trace Actions)
就是那些当探测点被触发时所执行的跟踪语句。

动作方法
BTrace的跟踪语句是必须定义在一个类的某个静态方法里的,这个静态方法就叫“动作”方法。

BTrace 程序结构

一个BTrace程序就是一个普通的Java类,这个类至少有一个这样的方法:
public static void ***
其次这个方法还需要加上BTrace相关的注解。这些注解用来指明被跟踪程序的“位置”(也就是前面提到的探测点)。跟踪动作需要在这个静态方法的方法体里指定。这些(注意,可以有多个)静态方法就是所谓的“动作”方法。

BTrace的限制

为了保证跟踪动作是“只读”的(也就是这些动作不可以修改被跟踪程序的状态)和有限度的(比如在固定时间里结束)。一个BTrace程序只允许完成一些指定的动作。下面是BTrace一些不可以完成的事情:

  • 不能创建新的对象
  • 不能创建新的数组
  • 不能抛出异常
  • 不能捕获异常
  • 不能进行任何的实例函数或者静态函数 -- 只有com.sun.btrace.BTraceUtils类中的静态函数或者BTrace程序自己声明的
  • 函数才可以被BTrace调用
  • 对1.2版本以前的程序,不能由实例级别的field和函数。只有静态公开的并且无返回值的函数才允许在BTrace类中使用。所有
的field必须是静态的。
  • 不可以在目标程序的类,或者对象的静态或者实例级别的field进行赋值。但是,BTrace自身的类是可以给它的静态field进行赋值的。
  • (也就是意味着跟踪的状态时可以更改的)
  • 不能有outer,inner,嵌套的或者本地类。
  • 不能有同步代码块或者同步的函数
  • 不能有循环语句(for,while, do..while)
  • 不能继承其它类(父类只能是java.lang.Object)
  • 不能实现接口
  • 不能包含断言(assert)语句
  • can NOT use class literals (这个我也没搞明白是啥意思)

一个简单的BTrace程序(适用于版本1.2)

一个简单的Brace程序 (适用于1.2以前的版本)

以上的程序都可以对一个正在运行的Java进行跟踪。这个程序会在目标程序调用Thread.start()函数来启动一个线程时打印出“about to start a thread!".还有其他一些有趣的探测点,比如,我们可以一个函数返回的时候插入一段探测动作,或者是一个函数碰到异常时也插入一段,在函数里用到的field,对象和数组的创建,代码行号等等。参考 @OnMethod和其它注解来了解更多细节。

运行BTrace的步骤
  1. 找到你要跟踪的Java进程的ID。可以用JDK自带的jps程序找到你要的进程id。
  2.  编写一段BTrace程序 -- 你也可以修改我们的样例程序
  3.  运行btrace工具 (到官方网站下载最新的压缩包解压,在bin目录就有这个文件,注意如果你是在windows环境下,那么这个文件的名字应该是btrace.bat):
  btrace <pid> <btrace-script>
 
注意: BTrace只支持JDK 6以上的环境

BTrace 命令行

BTrace 通过使用下面的btrace命令行工具来运行:

btrace [-I <include-path>] [-p <port>] [-cp <classpath>] <pid> <btrace-script> [<args>]

其中各个参数的含义如下:

include-path : 是一些用来查找头文件的目录。BTrace

  • port: BTrace代理程序所侦听的端口,这是可选的选项。默认是2020
  • classpath: 是一些用来查找jar文件的目录。默认是".",也就是当前目录
  • btrace-script: 就是跟踪程序本身。如果这是个java文件,那么在执行前会进行编译。否则就被认为是已经编译好的
  • 程序(比如可能是个.class文件),而直接运行
  • args: 这是传递给BTrace程序的参数。BTrace程序可以通过内置的$符号来引用这些参数,$length是这些参数的个数。

编译BTrace脚本

使用btracec脚本,我们可以对BTrace程序进行编译。btracec就是类似javac那样的程序,输入是一个BTrace程序,输出时一个.class文件。

btracec [-I <include-path>] [-cp <classpath>] [-d <directory>] <one-or-more-BTrace-.java-files>

各个参数的含义如下:
  • include-path
  • classpath:
  • directory:

使用BTrace代理来启动一个目标程序

到目前为止,我们已经知道如何跟踪一个正在运行的Java程序。我们甚至可以通过BTrace代理来启动目标程序。如果你想跟踪目标程序开始的时候作了什么事情,你就需要通过BTrace代理来启动它,并制定对应的跟踪脚本。下面的命令就是叫你如何作到这点的。需要注意的是:这里制定的跟踪脚本必须是已经编译好的(就是.class文件)。

java -javaagent:btrace-agent.jar=script=<pre-compiled-btrace-script1>[,<pre-compiled-btrace-script1>]* <MainClass> <AppArguments>

以这种方式启动的目标程序,会把跟踪输出到当前目录下一个叫作<btrace-class-file-name>.btrace的文件中。如果你不想这个目标程序给其他的远程BTrace客户端使用,那么可以指定noServer=true这个参数给BTrace代理。BTrace的发布目录下有个叫 btracer的脚本就是专门作上面的事情的:

btracer <pre-compiled-btrace.class> <application-main-class> <application-args>

支持的参数

  • bootClassPath - 启动时用到的classpath
  • systemClassPath - 系统classpath
  • debug - 是否输出详细的调试日志(true则输出,false则不输出)
  • unsafe - 是否不检查是否违反了btrace限制 (true则不检查,false则检查)
  • dumpClasses - 是否把二进制码dump到文件中(true/false)
  • dumpDir - dump出来文件放到这个目录下
  • stdout - 是否把btrace输出重定向到标准输出(true/false)
  • probeDescPath - 存放探测点描述文件的路径
  • script - 脚本的路径,当代理启动时会运行这个脚本
  • scriptdir - 脚本所在的目录,当代理启动时会运行这个目录下的脚本
  • scriptOutputFile -  输出文件的路径,btrace代理会把输出写到这个文件中   

重要的系统属性

btrace.agentname- 用来区分同一台机器上运行着的不同的btrace代理。

BTrace的注解

方法注解

@com.sun.btrace.annotations.OnMethod 这个注解可用来指定目标类,目标方法,以及目标方法里的”位置“。加了这个注解后的操作方法会在对应的方法运行到指定的”地点“时被执行。这这个注解中,目标类用”clazz“属性来指定,而目标方法用”method“属性来指定。"clazz"可以是类的全路径(比如java.awt.Component或者用两个反斜杠中间的正则表达式,参考例子NewComponent.java和Classload.java来看它们的用法,正则表达式可以匹配0个或多个目标类,这个时候多个类都会被进行动态指令更换。如/java\\.awt\\.+/匹配java.awt包下的所有类)。方法名也可以用这样的正则表达式 来匹配零个或者多个多个方法。参考例子MultiClass.java来查看用法。 还有一种方法来指定跟踪类和函数。被跟踪的类和函数可以用注解来指定。比如,如果"clazz"属性是@javax.jws.Webservice.那么BTrace会会把所有注解是这个的函数都进行动态指令更换。类似地,方法级别的注解也可以用来执行方法。参看例子WebServiceTracker.java来了解如何使用。可以把正则表达式和注解放在一起用,比如@/com\\.acme\\..+/可以匹配任何类,只要这个类的注解能跟那段正则表达式匹配即可。可以通过指定父类来匹配多个类名,比如+java.lang.Runnable就可以匹配所有实现了java.lang.Runnable这个接口的类。参考例子SubtypeTracer.java来看它的用法。

@com.sun.btrace.annotations.OnTimer 这个注解可以用来执行那些需要周期性(间隔是毫秒)的跟踪操作。参考Histogram.java来看它的用法。

@com.sun.btrace.annotations.OnError 这个注解可以用来指定当任何异常抛出时需要执行的操作。被这个注解修饰后的BTrace函数会在同一个BTrace类的其他操作方法抛出异常时执行。

@com.sun.btrace.annotations.OnExit 这个注解用来执行党BTrace代码调用了exit(int)结束跟踪会话后需要执行的操作。参考例子ProbeExit.java来了解如何使用。

@com.sun.btrace.annotations.OnEvent 这个注解用来跟踪函数与"外部”的事件关联起来。当BTrace客户端发送了一个“事件”后,这个注解里的操作就会被执行。客户端发送的事件可能是由用户触发的(比如按下Ctrl-C)。事件的名字是个字符串,这样跟踪操作就只会在对应的事件触发后被执行。到目标为止,BTrace命令行客户端会在用户按下Ctrl-C后发送事件,参考例子HistoOnEvent.java来了解用法。

@com.sun.btrace.annotations.OnLowMemory 这个注解可以用来跟踪特定内存阈值被用光的事件。参看例子MemAlerter.java了解用法。

@com.sun.btrace.annotations.OnProbe 这个注解可以用来避免使用BTrace脚本的内部类。@OnProbe探测点被映射到一个或多个@OnMethod上。目前这个映射是通过一个XML探测描述文件类指定的(这个文件会被BTrace代理所使用)。参考例子SocketTracker1.java和对应的描述文件java.net.socket.xml.当运行这个例子时,xml文件需要放在目标JVM所有运行的目录下(或者修改btracer.bat中的probeDescPath选项来指向任意的xml文件)。


参数相关的注解

  @com.sun.btrace.annotations.Self这个注解把一个参数标识为保留了目标函数所指向的this的值。参考例子AWTEventTracer.java和AllCalls1.java.
@com.sun.btrace.annotations.Return 这个注解说明这个参数保存目标函数的返回值。参考例子Classload.java
@com.sun.btrace.annotations.ProbeClassName (1.1以后的版本支持) 这个注解所修饰的参数保留了探测类的类名 (当你的探测类有好几个时这个注解就有用了)。参看AllMethods.java
  @com.sun.btrace.annotations.ProbeMethodName(1.1以后的版本支持)这个注解所修饰的参数保留了探测函数的函数名(当你所跟踪的函数有好几个是,这个注解就有用了)。参考WebServiceTracker.java
1.2版本以后,有一个布尔型的参数fqn可以用来执行是否获取函数的全名称
  @com.sun.btrace.annotations.TargetInstance(1.1版本以后支持)这个注解修饰的参数保留了被调用的实例。参考例子AllCall2.java.
@com.sun.btrace.annotations.TargetMethodOrField(1.1版本以后支持)这个注解修饰的参数保存了调用的函数名。参考AllCalls1.java 和AllCall2.java
1.2版本以后,有一个布尔型的参数fqn可以用来执行是否获取函数得到全名称


无注解的参数

没有注解的BTrace探测函数参数是用来作签名匹配的,因为他们必须必须在固定的位置上出现。然而,它们可以和其他的注解的参数进行交换。如果一个参数的类型是*AnyType[]*,它就会“吃”掉所所有剩下的参数。没有注解的参数的具体含义与他们所在的位置有关:
  • Kind.ENTRY, Kind.RETURN- 探测函数的参数
  • Kind.THROW - the thrown exception
  • Kind.ARRAY_SET, Kind.ARRAY_GET - 数组的索引
  • Kind.CATCH - 被捕获的异常
  • Kind.FIELD_SET - field的值
  • Kind.LINE - 源代码行号
  • Kind.NEW - 类名
  • Kind.ERROR - 抛出去的异常
字段相关的注解

@com.sun.btrace.annotations.Export BTrace字段使用这个注解来说明它已经被映射到一个jvmstat计数器上。使用这个注解,BTrace程序可以把跟踪计数器暴露给外部的jvmstat客户端(比如jstat)。参考例子ThreadCounter.java

  @com.sun.btrace.annotations.Property这个注解可以把一个字段标识为一个MBean属性。如果一个BTrace类至少有一个静态的字段使用了这个注解。那么一个MBean就会被创建并且注册到平台MBean服务器上。JMX客户端比如VisualVM,jconsole可以访问这个字段来查看BTrace的MBean。在把BTrace附加到目标程序上后,你可以把VisualVM或者jconsole也附加到同一个目标程序上来查看刚创建好的MBean属性。通过VisualVM或者jconsole,你可以通过MBeans tab页来查看BTrace相关的域,然后查看它们的值。参考例子ThreadCounterBean.java 和HistogramBean.java来了解用法

  @com.sun.btrace.annotations.TLS  BTrace字段使用这个注解来说明它自己是一个线程本地字段(thread local field).注意你只能在@OnMethod注解后的函数里访问这样的字段。每个Java线程都有一个这个字段的拷贝。为了让这样的方式能够工作,这个字段的类型只能是immutable(比如原始类型) 或者是cloneable (实现了Cloneable接口并且覆盖了clone()函数)的。这些线程本地字段可以被BTrace程序用来识别它是否在同一个线程里执行了多个探测操作。参考例子OnThrow.java和WebServiceTracker.java

类相关的注解

  @com.sun.btrace.annotations.DTrace这个注解用来把一小段D脚本(嵌在BTrace 的java类中)和BTrace程序关联起来。参考例子DTraceInline.java
@com.sun.btrace.annotations.DTraceRef 和上个注解一样,不同的是D脚本是在独立的文件中,不是嵌在java类中。
  @com.sun.btrace.annotations.BTrace必须使用这个注解来指定一个Java类是BTrace程序。BTrace编译器会强制查找这个注解,BTrace代理也会检查这个是否有这个注解。如果没有,则提示错误,并且不会执行。

DTrace集成

现在很少人用Solaris, 所以这段就略过啦,吼吼。

BTrace例子
例子的简短说明:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值