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