Btrace 線上調試代碼利器

Btrace 簡介

BTrace是sun公司推出的一款Java 動態、安全追蹤(監控)工具,可以在不用重啓的情況下監控系統運行情況,方便的獲取程序運行時的數據信息,如方法參數、返回值、全局變量和堆棧信息等,並且做到最少的侵入,佔用最少的系統資源。

由於Btrace會把腳本邏輯直接侵入到運行的代碼中,所以在使用上做很多限制:

  • 不能創建對象
  • 不能使用數組
  • 不能拋出或捕獲異常
  • 不能使用循環
  • 不能使用synchronized關鍵字
  • 屬性和方法必須使用static修飾

根據官方聲明,不恰當的使用BTrace可能導致JVM崩潰,如在BTrace腳本使用錯誤的class文件,所以在上生產環境之前,務必在本地充分的驗證腳本的正確性。

 

Btrace可以做什麼?

  • 接口性能變慢,分析每個方法的耗時情況;
  • 當在Map中插入大量數據,分析其擴容情況;
  • 分析哪個方法調用了System.gc(),調用棧如何;
  • 執行某個方法拋出異常時,分析運行時參數;

 

下載

安裝Btrace

# 下載安裝包[https://bintray.com/btraceio/releases/btrace/v2.0.0]
https://bintray.com/btraceio/releases/download_file?file_path=btrace-bin.tar.gz

# 解壓 
tar -xvf btrace-bin.tar.gz

# 設置環境變量, windows: %BTRACE_HOME%\bin
export BTRACE_HOME=/home/soft/btrace  
export PATH=$PATH:$BTRACE_HOME/bin  

Btrace 入門案例

添加 jar包依賴

<dependency>
    <groupId>com.sun.tools.btrace</groupId>
    <artifactId>btrace-agent</artifactId>
    <version>2.0.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/btrace-agent.jar</systemPath>
</dependency>
<dependency>
    <groupId>com.sun.tools.btrace</groupId>
    <artifactId>btrace-boot</artifactId>
    <version>2.0.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/btrace-boot.jar</systemPath>
</dependency>
<dependency>
    <groupId>com.sun.tools.btrace</groupId>
    <artifactId>btrace-client</artifactId>
    <version>2.0.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/btrace-client.jar</systemPath>
</dependency>

Btrace 相關 jar 包在: $BTRACE_HOME/libs 下

BTraceDemo

public class BTraceDemo {
    public static Random random = new Random();
    public int size;

    public static void main(String[] args) throws Exception {
        new BTraceDemo().run();
    }

    public void run() throws Exception {
        while (true) {
            add(random.nextInt(10), random.nextInt(10));
        }
    }

    public int add(int a, int b) throws Exception {
        Thread.sleep(random.nextInt(10) * 100);
        return a + b;
    }
}

Debug

@BTrace
public class Debug {
    @Export
    static long counter;

    @OnMethod(clazz = "com.answer.btrace.BTraceDemo", method = "add", location = @Location(Kind.RETURN))
    public static void run(@Self Object self, int a, int b, @Return int result, @Duration long time) {
        BTraceUtils.println("parameter a=" + a + ", b=" + b);
        BTraceUtils.println("cost time=" + time);
        BTraceUtils.println();
        counter++;
    }

    @OnTimer(1000)
    public static void run() {
        BTraceUtils.println("execute counter:" + counter);
        BTraceUtils.println();
    }

}

進入 BTrace 腳本所在目錄, 即: Debug.java 所在目錄, 執行btrace <PID> Debug.java

 

參數說明

@OnMethod

Btrace 使用 @OnMethod 註解定義需要分析的方法入口

在@OnMethod註解中,需要指定class、method以及location等,class表明需要監控的類,method表明需要監控的方法,指定方式如下:

  • 使用全限定名:clazz="com.metty.rpc.common.BtraceCase", method="add"
  • 使用正則表達式:clazz="/javax\\.swing\\..*/", method="/.*/"
  • 使用接口:clazz="+com.ctrip.demo.Filter", method="doFilter"
  • 使用註解:clazz="@javax.jws.WebService", method=""@javax.jws.WebMethod"
  • 如果需要分析構造方法,需要指定method=""

@Location

定義Btrace對方法的攔截位置,通過@Location註解指定,默認爲Kind.ENTRY

  • Kind.ENTRY:在進入方法時,調用Btrace腳本
  • Kind.RETURN:方法執行完時,調用Btrace腳本,只有把攔截位置定義爲Kind.RETURN,才能獲取方法的返回結果@Return和執行時間@Duration
  • Kind.CALL:分析方法中調用其它方法的執行情況,比如在execute方法中,想獲取add方法的執行耗時,必須把where設置成Where.AFTER
  • Kind.LINE:通過設置line,可以監控代碼是否執行到指定的位置
  • Kind.ERROR, Kind.THROW, Kind.CATCH
    用於對某些異常情況的跟蹤,包括異常拋出,異常被捕獲,異常未捕獲被拋出方法之外

 

註解說明

  • 類註解
    • @BTrace指定該java類爲一個btrace腳本文件。
  • 屬性註解
    • @TLS標註的屬性可以在追蹤腳本的方法中通訊
  • 方法註解
    • @OnMethod:指定該方法在什麼情況下被執行,clazz屬性指定要跟蹤的類的全限定類名,也可以用正則表達式,“/類名的Pattern/”匹配,如/javax\.swing\…*/;用”+類名”追蹤所有子類,如+java.lang.Runnable;用”@xxx”追蹤用該註解註解過的類,如@javax.jws.WebService。method屬性指定要追蹤的方法名稱,也可以用正則表達式。location屬性用@Location來指定該方法在目標方法執行前(後、異常、某行、某個方法調用)被執行。
    • @OnTimer:定時執行該方法。
    • @OnExit:當腳本運行Sys.exit(code)時執行該方法。
    • @OnError:當腳本運行拋出異常時執行該方法。
    • @OnEvent:腳本運行時Ctrl+C可以發送事件。
    • @OnLowMemory:指定一個內存閥值,低於閥值值執行該方法。
    • @OnProbe:指定一個xml文件來描述在什麼時候執行該方法。
  • 方法參數註解
    • @Self:指目標對象本身。
    • @Retrun:指目標程序方法返回值(需要配合Kind.RETURN)。
    • @ProbeClassName:指目標類名。
    • @ProbeMethodName:指目標方法名。
    • @targetInstance:指@Location指定的clazz和method的目標(需要配合Kind.CALL)。
    • @targetMethodOrField:指@Location指定的clazz和method的目標的方法或字段(需要配合Kind.CALL)
    • @Duration:指目標方法執行時間,單位是納秒(需要需要配合Kind.RETURN或Kind.ERROR一起使用)。
    • @AnyType:獲取對應請求的參數,泛指任意類型。
  • 追蹤時機參數
      - Kind.Entry:開始進入目標方法時,默認值。
    • Kind.Return:目標方法返回時。
    • Kind.Error:異常沒被捕獲被拋出目標方法之外時。
    • Kind.Throw:異常拋出時。
    • Kind.Catch:異常被捕獲時。
    • Kind.Call:被調用時。
    • Kind.Line:執行到某行時。
  • 其它
      - 追蹤構造函數:@OnMethod(clazz=“java.net.ServerSocket”,method="”)。
    • 追蹤靜態內部類:在類與內部類之間加上"",例如@OnMethod(clazz="com.vip.MyServer ",例如@OnMethod(clazz=“com.vip.MyServer”,例如@OnMethod(clazz=“com.vip.MyServerMyInnerClass”, method="hello”)。
    • 追蹤同名函數:如果有多個同名的函數,可以在攔截函數上定義不同的參數列表。
    • 追蹤結果輸出可以使用>將結果輸出到指定文件。

 

如何使用Btrace定位問題

分析哪個方法調用了System.gc(),調用棧如何?

@OnMethod(clazz = "java.lang.System", method = "gc", location = @Location(Kind.ENTRY))
public static void onSystemGC() {
    BTraceUtils.println("System GC");
    BTraceUtils.jstack();
}

統計方法的調用次數,且每隔1分鐘打印調用次數

@Export
static AtomicInteger counter = new AtomicInteger();

@OnMethod(clazz = "com.answer.btrace.BTraceDemo", method = "add")
public static void run(@Self Object self) {
    counter.getAndIncrement();
}

// @OnTimer註解可以實現定時執行腳本中的一個方法
@OnTimer(1000 * 60)
public static void run() {
    BTraceUtils.println("counter: " + counter.get());
    counter.set(0);
}

方法執行時,查看對象的實例屬性值

@OnMethod(clazz = "com.answer.btrace.BTraceDemo", method = "add")
public static void getField(@Self Object self) {
    Field field = BTraceUtils.field("com.answer.btrace.BTraceDemo", "size");
    int size = (int) BTraceUtils.get(field, self);
    BTraceUtils.println(size);
}

 

實戰

@BTrace
public class Debuger {

    @Export
    static long counter;

    @OnMethod(clazz = "com.answer.controller.SystemController", method = "queryDicts", location = @Location(Kind.RETURN))
    public static void run(
            @ProbeClassName String className,
            @ProbeMethodName String methodName,
            @Self Object self,
            String[] dictCodes,
            @Return AnyType result,
            @Duration long time) {
        BTraceUtils.println("className=" + className);
        BTraceUtils.printArray(dictCodes);
        BTraceUtils.println("methodName=" + methodName);
        BTraceUtils.println("cost time=" + time);
        if (dictCodes != null) {
            BTraceUtils.println("dictCodes=" + dictCodes);
        }

        BTraceUtils.println("result=" + result);
        BTraceUtils.println();
        counter++;
    }

    @OnTimer(10 * 1000)
    public static void run() {
        BTraceUtils.println("execute counter:" + counter);
        counter = 0;
        BTraceUtils.println();
    }

}

 

Reference

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