大家在開發過程中總會預告各種個樣的BUG,不是什么時候都可以去debug,也不是什么時候都可以去在代碼中增加需要的日志,這個時候該如何解決呢?這個時候就是BTrace的大展身手的時候時候了,下面通過對於BTrace的介紹,同時會有一些示例代碼希望可以給大家帶來一定的了解
簡介
Btrace (Byte Trace)是sun推出的一款java 動態、安全追蹤工具,可以不停機的情況下監控線上情況,並且做到最少的侵入,占用最少的系統資源。BTrace應用較為廣泛的原因應該是其安全性和無侵入性,其中熱交互技術,使得我們無需啟動Agent的情況下動態跟蹤分析,其安全性不會導致對目標Java進程的任何破壞性影響,使得BTrace成為我們線上產品問題定位的利器。無侵入性無需我們對原有代碼做任何修改,降低上線風險和測試成本,並且無需重啟啟動目標Java進程進行Agent加載即可動態分析和跟蹤目標程序,可以說BTrace可以滿足大部分的應用場景。
安裝
下載BTrace
BTrace已經遷移到GitHub, 最新到版本是v1.3.11 下載 https://github.com/btraceio/btrace/releases/download/v1.3.11/btrace-bin-1.3.11.zip 后解壓到指定目錄
配置BTRACE_HOME
vi .bash_profile
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home BTRACE_HOME=/Users/david/Downloads/btrace-bin-1.3.11 PATH=$PATH:$BTRACE_HOME/bin:$JAVA_HOME/bin:$HOME/bin export JAVA_HOME export BTRACE_HOME export PATH
配置完成后執行 source .bash_profile
在終端中輸入btrace 可以看到如下內容
$ btrace Usage: btrace <options> <pid> <btrace source or .class file> <btrace arguments> where possible options include: --version Show the version -v Run in verbose mode -o <file> The path to store the probe output (will disable showing the output in console) -u Run in trusted mode -d <path> Dump the instrumented classes to the specified path -pd <path> The search path for the probe XML descriptors -classpath <path> Specify where to find user class files and annotation processors -cp <path> Specify where to find user class files and annotation processors -I <path> Specify where to find include files -p <port> Specify port to which the btrace agent listens for clients -statsd <host[:port]> Specify the statsd server, if any
BTrace使用
- jps命令查出需要監控的jvm pid
- 編寫BTrace跟蹤程序
- 執行:btrace <pid> BTrace跟蹤程序
注意事項
Btrace腳本就是一個普通的用@Btrace注解的Java類,其中包含一個或多個public static void修飾的方法,注意攔截方法必須是用public static void 進行修飾的,如果不是靜態方法則會拋出 instance methods are not allowed 這樣的異常信息 如果不是public 則會提示btrace methods should be public ;如果有返回則提示信息為:btrace probe methods must return void
為了保證對目標程序不造成影響,Btrace腳本對其可以執行的動作做了很多限制,如下:
- 不能創建對象
- 不能拋出或者捕獲異常
- 不能用synchronized關鍵字
- 不能對目標程序中的instace或者static變量
- 不能調用目標程序的instance或者static方法
- 腳本的field、method都必須是static的
- 腳本不能包括outer,inner,nested class
- 腳本中不能有循環,不能繼承任何類,任何接口與assert語句
BTrace注解
對於BTrace的學習離不開對BTrace注解的理解,BTrace注解可以分為類注解 @BTrace 方法注解如@OnMethod 參數注解如:@ProbeClassName 為了后面更好理解demo我們首先從參數注解開始介紹
@ProbeClassName
用於標記處理方法的參數,僅用戶@OnMethod, 該參數的值就是被跟蹤的類名稱
@ProbeMethodName
用於表姐處理方法的參數,僅用戶 @OnMethod,該參數值是被跟蹤方法名稱
@Self
當前截取方法的封閉實例參數
@Return
當前截取方法的的返回值, 只對 location=@Location(Kind.RETURN) 生效
@Duration
當前截取方法的執行時間
@TargetInstance
當前截取方法內部調用的實例
@TargetMethodOrField
當前截取方法內部被調用的方法名
方法注解重點介紹OnMethod這個也是我們在排查問題時重點使用的.
@OnMethod
作用
用於指定跟蹤方法到目標類,目標方法和目標位置
格式
@OnMethod(clazz=<cname_spec>[, method=<mname_spec>]? [, type=<signature>]? [, location=<location_spec>]?)
參數說明
clazz 用於限定目標類
cname_spec = <class_name> | +<class_name> | /regex/ class_name 是完全限定類名 +class_name 完全限定類名稱前加上“+”表示這個類的所有子類或實現,/regex/就是用戶識別類名稱的標准正則表達式
method 用戶限定目標方法, mname_spec表示簡單的方法名稱,不包含簽名和返回類型;
type:用戶限定目標方法的簽名和返回類型 <return_type> ((arg_type(,arg_type)*)? return_type就是方法的返回類型,如void, java.lang.String; arg_type就是參數類型
location 用於限定目標方法的位置, 通過@Location注解進行指定
@Location 屬性有:
- value 默認值為Kind.ENTRY 即參數的入口位置
- where 限定探測位置 默認值為 Where.BEFORE 也可以設置為Where.AFTER
- clazz
- method
- field
- type
- line
其中 @Kind注解的值有
- Kind.ENTRY-被trace方法參數
- Kind.RETURN-被trace方法返回值
- Kind.THROW -拋異常
- Kind.ARRAY_SET, Kind.ARRAY_GET -數組索引
- Kind.CATCH -捕獲異常
- Kind.FIELD_SET -屬性值
- Kind.LINE -行號
- Kind.NEW -類名
- Kind.ERROR -拋異常
獲取截取方法 類,方法,實例,返回值以及耗時信息,限定的類為Map接口的實現類,方法為put 位置為return 之前
@BTrace
public class TracingScript {
@OnMethod(clazz="+java.util.Map",method="put",location=@Location(Kind.RETURN))
public static void testB(@ProbeClassName String pcm,@ProbeMethodName String pmn,@Self Object self,@Duration long duration,@Return Object result){
println(strcat(strcat(strcat(strcat(pcm,"#"),pmn)," called in"),str(self)));
println(strcat(strcat("result is ",str(result)),strcat(" duration is ",str(duration))));
}
}
輸出的結果是:
java.util.HashMap#put called in{name=javax.management.ObjectName$Property@338bf6b2, type=javax.management.ObjectName$Property@6f24f3ca}
result is null duration is 2488
java.util.HashMap#put called in{javax.management.ObjectName=java.lang.Object@74685cec}
result is null duration is 5692
java.util.HashMap#put called in{type=javax.management.ObjectName$Property@582e822b}
result is null duration is 2957
java.util.HashMap#put called in{name=javax.management.ObjectName$Property@10dc9047, type=javax.management.ObjectName$Property@582e822b}
result is null duration is 2683
獲取截取方法內部調用實例以及方法
@OnMethod(clazz="+java.util.Map",method="get",location=@Location(value=Kind.CALL, clazz="/.*/", method="/.*/"))
public static void testA(@ProbeClassName String pcm, @TargetInstance Object target, @TargetMethodOrField String field){
println(strcat("target is",strcat(strcat(str(target),"#"),field)));
}
輸出的結果是:
target isjava.io.ObjectStreamClass$WeakClassKey@3952cb51#hashCode target isjava.io.ObjectStreamClass$WeakClassKey@3952cb51#equals
@OnTimer
用於指定跟蹤操作定時執行。value用於指定時間間隔
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
@BTrace
public class TracingScript {
private static Map<String, AtomicInteger> histo = Collections.newHashMap();
@OnMethod(clazz = "+java.util.Map", method = "put", location = @Location(value = Kind.RETURN, clazz = "/.*/", method = "/.*/"))
public static void testB(@ProbeClassName String pcm, @ProbeMethodName String pmn, @Self Object self, @Duration long duration, @Return Object result) {
String cn = Reflective.name(classOf(self));
AtomicInteger ai = Collections.get(histo, cn);
if (ai == null) {
ai = Atomic.newAtomicInteger(1);
Collections.put(histo, cn, ai);
} else {
Atomic.incrementAndGet(ai);
}
}
@OnTimer(1000)
public static void print() {
if (Collections.size(histo) != 0) {
printNumberMap("Component Histogram", histo);
}
}
}
輸出的結果是:
* Component Histogram * java.util.HashMap = 130
@OnError
當trace代碼拋異常或者錯誤時,該注解的方法會被執行.如果同一個trace腳本中其他方法拋異常,該注解方法也會被執行。
@OnEvent
通過btrace client事件進行觸發,在jvisualvm 通過發送事件 就可以觸發如下代碼
@OnEvent
public static void printEvent(){
if(Collections.size(histo)!=0){
printNumberMap("Component Histogram", histo);
}
}
* Component Histogram * java.util.HashMap = 131
在jvisualvm中使用BTrace
首先選擇 工具->插件 安裝BTrace插件

選擇需要跟蹤的工程,

BTraceUtils方法介紹
- 獲取當前線程 BTraceUtils.currentThread()
- 獲取當前線程名稱 BTraceUtis.Threads.name(BTraceUtils.currentThread())
- 打印當前線程jstack BTraceUtils.Threads.jstack()
- 獲取當前時間 timestamp()
- 獲取當前時間 毫秒值 millis()
- 創建StringBuilder newStringBuilder()
- 追加字符串append()
Appendable builder=Strings.newStringBuilder();
Strings.append(builder,"當前時間:");
Strings.append(builder,timestamp());
println(str(builder));
- 字符串拼接 strcat("str1","str2") concat("str1","str2")
- 字符串比較strcmp("str1","str2")
- 截取字符串 substr(“str",0,1)
- 獲取字符串長度 strlen("str")
- 字符串是否匹配指定的正則表達式 matches(regex, input)
- 將對象轉換為字符串 str()
