BTrace工具簡介
Java進程診斷分析工具
安全的工具
無侵入性
不修改應用任何應用資料
限制跟蹤行為,沒能有迴圈
相依元件
使用OjbectWeb ASM組件來完成位元組碼層面上的跟蹤分析
開源組件
項目首頁:http://btrace.dev.java.net
GPLv2 + CLASSPATH Exception
VisualVM 外掛程式和開發時外掛程式
BTrace應用較為廣泛的原因應該是其安全性和無侵入性,已經熱互動技術,使得我們無需啟動Agent的情況下動態跟蹤分析,其安全性不會導致對目標Java進程的任何破壞性影響,使得BTrace成為我們線上產品問題定位的利器。無侵入性無需我們對原有代碼做任何修改,降低上線風險和測試成本,並且無需重啟啟動目標Java進程進行Agent載入即可動態分析和跟蹤目標程式,可以說BTrace可以滿足大部分的應用情境。
通過註解來完成跟蹤類型的支援
l 方法調用
l 方法返回
l 捕捉方法異常
l 行號
l 欄位get/set
l 方法調用/返回(在指定的方法中)
l 異常拋出前後
l 同步進入/退出
l 定時器
BTrace指令碼
為了保證trace語句唯讀, BTrace對trace指令碼有一些限制(比如不能改變被trace代碼中的狀態),跟蹤類中必須要有一個靜態方法,主要是通過調用BTraceUtils類來完成一些動作,並且這些動作是安全的,是BTrace規定的一個子集,詳見如下描述:
l BTrace禁止new類、數組,、拋異常、捕獲異常
l 禁止調用除com.sun.btrace.BTraceUtil類的其他執行個體方法以及靜態方法
l BTrace1.2前不能有執行個體欄位和方法,只能有無傳回值的靜態方法,所有欄位也都必須是靜態。
l 禁止定義外部、內部、匿名, 本地類
l 禁止有同步塊和同步方法
l 禁止有迴圈(for, while, do..while)
l 禁止實現介面, 不能擴充類,直接超類必須是java.lang.Object
l 禁止使用assert語句, 不能使用class字面值
l 禁止使用class位元組碼
方法上的註解
l @ OnMethod 用來指定trace的目標類和方法以及具體位置, 被註解的方法在匹配的方法執行到指定的位置會被調用。"clazz"屬性用來指定目標類名, 可以指定全限定類名, 比如"java.awt.Component", 也可以是Regex(運算式必須寫在"//"中, 比如"/java\\.awt\\..+/")。"method"屬性用來指定被trace的方法. 運算式可以參考內建的例子(NewComponent.java 和 Classload.java, 關於方法的註解可以參考MultiClass.java).
有時候被trace的類和方法可能也使用了註解. 用法參考內建例子WebServiceTracker.java. 針對註解也是可以使用Regex, 比如像這個"@/com\\.acme\\..+/ ",也可以通過指定超類來匹配多個類, 比如"+java.lang.Runnable"可以匹配所有實現了java.lang.Runnable介面的類. 具體參考內建例子SubtypeTracer.java。
l @OnTimer定時觸發Trace,時間可以指定,單位為毫秒,具體參考內建例子 Histogram.java。
l @OnError 當trace代碼拋異常或者錯誤時,該註解的方法會被執行. 如果同一個trace指令碼中其他方法拋異常, 該註解方法也會被執行。
l @OnExit 當trace方法調用內建exit(int)方法(用來結束整個trace程式)時, 該註解的方法會被執行. 參考內建例子ProbeExit.java。
l @OnEvent 用來截獲"外部"btrace client觸發的事件, 比如按Ctrl-C 中斷btrace執行時,並且選擇2,或者輸入事件名稱,將執行使用了該註解的方法, 該註解的value值為具體事件名稱。具體參考例子HistoOnEvent.java
l @OnLowMemory 當記憶體超過某個設定值將觸發該註解的方法, 具體參考MemAlerter.java
l @OnProbe 使用外部檔案XML來定義trace方法以及具體的位置,具體參考樣本SocketTracker1.java和java.net.socket.xml。
參數上的註解
l @Self 用來指定被trace方法的this,可參考例子AWTEventTracer.java 和 AllCalls1.java
l @Return 用來指定被trace方法的傳回值,可參考例子Classload.java
l @ProbeClassName (since 1.1) 用來指定被trace的類名, 可參考例子AllMethods.java
l @ProbeMethodName (since 1.1) 用來指定被trace的方法名, 可參考例子WebServiceTracker.java。
l @TargetInstance (since 1.1) 用來指定被trace方法內部被調用到的執行個體, 可參考例子AllCalls2.java
l @TargetMethodOrField (since 1.1) 用來指定被trace方法內部被調用的方法名, 可參考例子AllCalls1.java 和 AllCalls2.java。
非註解的方法參數
未使用註解的方法參數一般都是用來做方法簽名匹配用的, 他們一般和被trace方法中參數出現的順序一致. 不過他們也可以與註解方法交錯使用, 如果一個參數型別宣告為*AnyType[]*, 則表明它按順序"通吃"方法所有參數. 未註解方法需要與*Location*結合使用:
l Kind.ENTRY-被trace方法參數
l Kind.RETURN- 被trace方法傳回值
l Kind.THROW - 拋異常
l Kind.ARRAY_SET, Kind.ARRAY_GET - 數組索引
l Kind.CATCH - 捕獲異常
l Kind.FIELD_SET - 屬性值
l Kind.LINE - 行號
l Kind.NEW - 類名
l Kind.ERROR - 拋異常
屬性上的註解
l @Export 該註解的靜態屬性主要用來與jvmstat計數器做關聯. 使用該註解之後, btrace程式就可以向jvmstat用戶端(可以用來統計jvm堆中的記憶體使用量量)暴露trace程式的執行次數, 具體可參考例子ThreadCounter.java
l @Property 使用了該註解的trace指令碼將作為MBean的一個屬性, 一旦使用該註解, trace指令碼就會建立一個MBean並向MBean伺服器註冊, 這樣JMX用戶端比如VisualVM, jconsole就可以看到這些BTrace MBean. 如果這些被註解的屬性與被trace程式的屬性關聯, 那麼就可以通過VisualVM 和jconsole來查看這些屬性了. 具體可參考例子ThreadCounterBean.java 和 HistogramBean.java。
l @TLS 用來將一個指令碼變數與一個ThreadLocal變數關聯. 因為ThreadLocal變數是跟線程相關的, 一般用來檢查在同一個線程調用中是否執行到了被trace的方法. 具體可參考例子OnThrow.java 和 WebServiceTracker.java
類上的註解
l @com.sun.btrace.annotations.DTrace 用來指定btrace指令碼與內建在其指令碼中的D語言指令碼關聯, 具體參考例子DTraceInline.java.
l @com.sun.btrace.annotations.DTraceRef 用來指定btrace指令碼與另一個D語言指令檔關聯. 具體參考例子DTraceRefDemo.java.
l @com.sun.btrace.annotations.BTrace 用來指定該java類為一個btrace指令檔.
相關執行個體說明
BTrace內建的sample是學習BTrace的最後資料,熟練使用BTrace中提供的sample並且能夠手動進行驗證,可以快速的熟悉BTrace並載入應用,內建的sample也有很大一部分可以直接或者稍加修改就可以成為我們的定位指令碼,方便使用。
l AWTEventTracer.java - 示範了對EventQueue.dispatchEvent()事件進行trace的做法, 可以通過instanceof來對事件進行過濾, 比如這裡只針對focus事件trace.
l AllLines.java - 示範了如何在被trace的程式到達probe指定的類和指定的行號時執行指定的操作(例子中指定的行號是-1表示任意行).
l AllSync.java - 示範了如何在進入/退出同步塊進行trace.
l ArgArray.java - 示範了列印java.io包下所有類的readXXX方法的輸入參數.
l Classload.java - 示範列印成功載入指定類以及堆棧資訊.
l CommandArg.java - 示範如何擷取btrace命令列參數.
l Deadlock.java - 示範了@OnTimer註解和內建deadlock()方法的用法
l DTraceInline.java - 示範@DTrace註解的用法
l DTraceDemoRef.java - 示範@DTraceRef 註解的用法.
l FileTracker.java - 示範了如何對File{Input/Output}Stream建構函式中初始化開啟檔案的讀寫檔案操作進行trace.
l FinalizeTracker.java - 示範了如何列印一個類所有的屬性, 這個在調試和故障分析中非常有用. 這裡的例子是列印FileInputStream類的close() /finalize() 方法被調用時的資訊.
l Histogram.java - 示範了統計javax.swing.JComponent在一個應用中被建立了多少次.
l HistogramBean.java - 同上例, 只不過示範了如何與JMX整合, 這裡的map屬性通過使用@Property註解被暴露成一個MBean.
l HistoOnEvent.java - 同上例, 只不過示範了如何在通過按ctrl+c中斷當前指令碼時列印出建立次數, 而不是定時列印.
l JdbcQueries.java - 示範了彙總(aggregation)功能. 關於彙總功能可參考DTrace.
l JInfo.java - 示範了內建方法printVmArguments(), printProperties() 和printEnv() 的用法
l JMap.java - 示範了內建方法dumpHeap()的用法. 即將目標應用的堆資訊以二進位的形式dump出來
l JStack.java - 示範了內建方法jstackAll()的用法, 即列印所有線程的堆棧資訊.
l LogTracer.java - 示範了如何深入執行個體方法(Logger.log)並調用內建方法(field() )列印私人屬性內容.
l MemAlerter.java - 示範了使用@OnLowMememory 註解監控記憶體使用量情況. 即堆記憶體中的年老代達到指定值時列印出記憶體資訊.
l Memory.java - 示範每隔4s列印一次記憶體統計資訊.
l MultiClass.java - 示範了通過使用Regex對多個類的多個方法進行trace.
l NewComponent.java - 使用計數器每隔一段時間檢查當前應用中建立java.awt.Component的個數.
l OnThrow.java - 當拋出異常時, 列印出異常堆棧資訊.
l ProbeExit.java - 示範@OnExit註解和內建exit(int)方法的用法
l Profiling.java - 示範了對profile的支援. // 我執行沒成功, BTrace內部有異常
l Sizeof.java - 示範了內建的sizeof方法的使用.
l SocketTracker.java - 示範了對socket的creation/bind方法的trace.
l SocketTracker1.java - 同上, 只不過使用了@OnProbe.
l SysProp.java - 示範了使用內建方法擷取系統屬性, 這裡是對 java.lang.System的getProperty方法進行trace.
l SubtypeTracer.java - 示範了如何對指定超類的所有子類的指定方法進行trace.
l ThreadCounter.java - 示範了在指令碼中如何使用jvmstat 計數器. (jstat -J-Djstat.showUnsupported=true -name btrace.com.sun.btrace.samples.ThreadCounter.count 需要這樣來從外部通過jstat來訪問)
l ThreadCounterBean.java - 同上, 只不過使用了JMX.
l ThreadBean.java - 示範了對先行編譯器的使用(並結合了JMX).
l ThreadStart.java - 示範了指令碼中DTrace的用法.
l Timers.java - 示範了在一個指令碼中同時使用多個@OnTimer
l URLTracker.java - 示範了在每次URL.openConnection成功返回時列印出url. 這裡也使用了D語言指令碼.
l WebServiceTracker.java - 示範了如何根據註解進行trace.
其他相關博文
BTrace系列之一:簡介
BTrace系列之二:簡單樣本
BTrace系列之三:實際案例
BTrace系列之四:破解