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例子
例子的簡短說明:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章