1.使用背景
生產(chǎn)環(huán)境系統(tǒng)發(fā)生問題時脑又,定位問題需要獲取系統(tǒng)運行時的相關(guān)數(shù)據(jù),如方法參數(shù)、返回值挂谍、全局變量、堆棧信息等瞎饲。為了獲取這些數(shù)據(jù)口叙,需要修改代碼,將數(shù)據(jù)輸出到日志文件嗅战,再發(fā)布到生產(chǎn)環(huán)境妄田。這種方式,一方面將增大定位問題的成本和周期驮捍,對于緊急問題無法做到及時定位及解決疟呐;另一方面重新部署后環(huán)境很大程度上已被破壞,很難重現(xiàn)問題东且。BTrace在這種背景環(huán)境下應(yīng)運而生了启具。
2.BTrace簡述
Btrace (Byte Trace)是sun推出的一款Java 動態(tài)、安全追蹤(監(jiān)控)工具珊泳,可以在不停機的情況下監(jiān)控系統(tǒng)運行情況鲁冯,并且做到最少的侵入,占用最少的系統(tǒng)資源色查。官方網(wǎng)址:https://kenai.com/projects/btrace薯演。BTrace在使用上做了很多限制,如不能創(chuàng)建對象秧了、不能使用數(shù)組跨扮、不能拋出或捕獲異常、不能使用循環(huán)验毡、不能使用synchronized關(guān)鍵字衡创、腳本的屬性和方法都必須使用static修飾等,具體限制條件可參考用戶手冊晶通。根據(jù)官方聲明钧汹,不當(dāng)?shù)厥褂肂Trace可能導(dǎo)致JVM崩潰,如BTrace使用錯誤的.class文件录择,所以拔莱,可以先在本地驗證BTrace腳本的正確性再使用。
3.BTrace優(yōu)點
安全性:安全性不會導(dǎo)致對目標(biāo)Java進程的任何破壞性影響隘竭;
無侵入性:無需對原有代碼做任何修改塘秦,降低上線風(fēng)險和測試成本,并且無需重啟系統(tǒng)动看。
4.安裝BTrace
1)下載地址:https://github.com/btraceio/btrace/releases/tag/v1.3.8.3-1
2)解壓縮
3)設(shè)置環(huán)境變量
BTRACE_HOME=/Users/wlxs/btrace-bin-1.3.8.3
export BTRACE_HOME
export PATH=$PATH:$BTRACE_HOME/bin
5.使用btrace
作用:運行Btrace腳本
命令格式:
參數(shù)說明:
6.使用btracec
?作用:預(yù)編譯BTrace腳本尊剔,在編譯期驗證腳本正確性。
?命令格式:
參數(shù)說明:directory指定編譯結(jié)果輸出路徑菱皆,其它參數(shù)同btrace须误。
7.使用btracer
?作用:btracer命令同時啟動應(yīng)用程序和BTrace腳本
?命令格式:
參數(shù)說明:
8.注解說明
1)類注解
?@com.sun.btrace.annotations.BTrace指定該java類為一個btrace腳本文件挨稿。
2)屬性注解
?@TLS標(biāo)注的屬性可以在追蹤腳本的方法中通訊
3)方法注解
?@OnMethod:指定該方法在什么情況下被執(zhí)行,clazz屬性指定要跟蹤的類的全限定類名京痢,也可以用正則表達式奶甘,“/類名的Pattern/”匹配,如/javax\\.swing\\..*/祭椰;用”+類名”追蹤所有子類臭家,如+java.lang.Runnable;用”@xxx”追蹤用該注解注解過的類方淤,如@javax.jws.WebService钉赁。method屬性指定要追蹤的方法名稱,也可以用正則表達式携茂。location屬性用@Location來指定該方法在目標(biāo)方法執(zhí)行前(后你踩、異常、某行讳苦、某個方法調(diào)用)被執(zhí)行?姓蜂。
@OnTimer:定時執(zhí)行該方法?。
@OnExit:當(dāng)腳本運行Sys.exit(code)時執(zhí)行該方法?医吊。
@OnError:當(dāng)腳本運行拋出異常時執(zhí)行該方法?钱慢。
@OnEvent:腳本運行時Ctrl+C可以發(fā)送事件?。
@OnLowMemory:指定一個內(nèi)存閥值卿堂,低于閥值值執(zhí)行該方法?束莫。
@OnProbe:指定一個xml文件來描述在什么時候執(zhí)行該方法。
4)方法參數(shù)注解
?@Self:指目標(biāo)對象本身?草描。
@Retrun:指目標(biāo)程序方法返回值(需要配合Kind.RETURN)?览绿。
@ProbeClassName:指目標(biāo)類名。
?@ProbeMethodName:指目標(biāo)方法名?穗慕。
@targetInstance:指@Location指定的clazz和method的目標(biāo)(需要配合Kind.CALL)?饿敲。
@targetMethodOrField:指@Location指定的clazz和method的目標(biāo)的方法或字段(需要配合Kind.CALL)?。
@Duration:指目標(biāo)方法執(zhí)行時間逛绵,單位是納秒(需要需要配合Kind.RETURN或Kind.ERROR一起使用)怀各。
?AnyType:獲取對應(yīng)請求的參數(shù),泛指任意類型术浪。
9.追蹤時機參數(shù)
?Kind.Entry:開始進入目標(biāo)方法時瓢对,默認(rèn)值?。
Kind.Return:目標(biāo)方法返回時胰苏。
?Kind.Error:異常沒被捕獲被拋出目標(biāo)方法之外時?硕蛹。
Kind.Throw:異常拋出時?。
Kind.Catch:異常被捕獲時。
?Kind.Call:被調(diào)用時法焰。
?Kind.Line:執(zhí)行到某行時秧荆。
10.其它
?1)追蹤構(gòu)造函數(shù)?:@OnMethod(clazz="java.net.ServerSocket",method="<init>”)。
?2)追蹤靜態(tài)內(nèi)部類:?在類與內(nèi)部類之間加上"$"埃仪,?示例如@OnMethod(clazz="com.vip.MyServer$MyInnerClass", method="hello”)?乙濒。
3)追蹤同名函數(shù):?如果有多個同名的函數(shù),可以在攔截函數(shù)上定義不同的參數(shù)列表?贵试。
4)追蹤結(jié)果輸出?可以使用>將結(jié)果輸出到指定文件。
11.示例代碼
Calculator類的add方法每隔5秒對a凯正、b兩個數(shù)進行相加毙玻,代碼如下。
public class Calculator {
private int c = 1;
public int add(int a, int b) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return a + b;
}
}
BTraceDemo調(diào)用Calculator的add方法對兩個隨機數(shù)進行相加廊散,代碼如下桑滩。
public class BTraceDemo {
public static void main(String[] args) {
Calculator calculator = new Calculator();
Random random = new Random();
while (true) {
System.out.println(calculator.add(random.nextInt(10), random.nextInt(10)));
}
}
}
1)BTraceTest則是相應(yīng)的追蹤腳本,代碼如下允睹。
@BTrace
public class BTraceTest {
private static long count;
static{
println("---------------------------JVM properties:---------------------------");
printVmArguments();
println("---------------------------System properties:------------------------");
printProperties();
println("---------------------------OS properties:----------------------------");
printEnv();
exit();
}
@OnMethod(
clazz = "Calculator",
method = "add",
location = @Location(Kind.RETURN)
)
public static void trace1(int a, int b, @Return int sum) {
println("trace1:a=" + a + ",b=" + b + ",sum=" + sum);
}
}
運行如下命令:
btrace 11308 /Users/wlxs/java/BTraceTest.java
11308是BTraceDemo的進程ID运准,靜態(tài)塊中的輸出結(jié)果就不展示了。trace1方法實現(xiàn)對Calculator類的add方法的入?yún)⒑头祷刂颠M行追蹤缭受,結(jié)果如下胁澳。
trace1:a=2,b=6,sum=8
2)為了節(jié)省篇幅,下面都將只列出各個追蹤的方法米者,trace2追蹤Calculator類的add方法執(zhí)行時間韭畸,默認(rèn)時間單位是納秒。
@OnMethod(
clazz = "Calculator",
method = "add",
location = @Location(Kind.RETURN)
)
public static void trace2(@Duration long duration) {
println(strcat("duration(nanos): ", str(duration)));
println(strcat("duration(s): ", str(duration / 1000000000)));
}
結(jié)果如下蔓搞。
duration(nanos): 5004187000
duration(s): 5
3)trace3追蹤Calculator類的add方法胰丁,并且追蹤add方法中的任何類的sleep方法,代碼如下喂分。
@OnMethod(
clazz = "Calculator",
method = "add",
location = @Location(value = Kind.CALL, clazz = "/.*/", method = "sleep")
)
public static void trace3(@ProbeClassName String pcm, @ProbeMethodName String pmn,
@TargetInstance Object instance, @TargetMethodOrField String method) {
println(strcat("ProbeClassName: ", pcm));
println(strcat("ProbeMethodName: ", pmn));
println(strcat("TargetInstance: ", str(instance)));
println(strcat("TargetMethodOrField : ", str(method)));
println(strcat("count: ", str(++count)));
}
結(jié)果如下锦庸。
ProbeClassName: Calculator
ProbeMethodName: add
TargetInstance: null
TargetMethodOrField : sleep
count: 1
4)trace4每隔6秒打印一次count的值,代碼如下蒲祈。
@OnTimer(6000)
public static void trace4() {
println(strcat("trace4:count: ", str(count)));
}
結(jié)果如下甘萧。
trace4:count: 1
5)trace5用于獲取Calculator類的c屬性的值,代碼如下梆掸。
@OnMethod(
clazz = "Calculator",
method = "add",
location = @Location(Kind.RETURN)
)
public static void trace5(@Self Object calculator) {
println(get(field("Calculator", "c"), calculator));
}
6)traceMemory每隔4秒打印一次印堆和非堆內(nèi)存信息幔嗦,代碼如下。
@OnTimer(4000)
public static void traceMemory() {
println("heap:");
println(heapUsage());
println("no-heap:");
println(nonHeapUsage());
}
結(jié)果如下沥潭。
heap:
init = 10485760(10240K) used = 4430576(4326K) committed = 9961472(9728K) max = 9961472(9728K)
no-heap:
init = 24576000(24000K) used = 7813992(7630K) committed = 24576000(24000K) max = 136314880(133120K)
7)trace6每隔4秒檢測是否有死鎖產(chǎn)生邀泉,并打印產(chǎn)生死鎖的相關(guān)類信息、對應(yīng)的代碼行、線程信息汇恤,代碼如下庞钢。
@OnTimer(4000)
public static void trace6() {
deadlocks();
}