BTrace 是什么
BTrace 是檢查和解決線上的問題的殺器,BTrace 可以通過編寫腳本的方式,獲取程序執行過程中的一切信息,並且,注意了,不用重啟服務,是的,不用重啟服務。寫好腳本,直接用命令執行即可,不用動原程序的代碼。
原理
安裝和配置
export JAVA_HOME=/home/fengzheng/soft/jdk1.8.0_111
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
export BTRACE_HOME=/home/fengzheng/soft/btrace
export PATH=$PATH:$BTRACE_HOME/bin
簡單測試用例
package kite.lab.utils;
/**
* NumberUtil
*
* @author fengzheng
* @date 2017/2/15
*/
public class NumberUtil {
public int sum(){
int result = 0;
for(int i = 0; i< 100; i++){
result += i * i;
}
return result;
}
public static void main(String[] args){
while (true) {
Thread.currentThread().setName("計算");
NumberUtil util = new NumberUtil();
int result = util.sum();
System.out.println(result);
try {
Thread.sleep(5000);
}catch (InterruptedException e){
}
}
}
}
2. 執行上面的程序后,可用 jps 命令查看 pid(一般情況下用哪個賬號啟動的程序,就要用哪個賬號執行 jps ,root 賬號除外),執行 jps 命令看到如下結果:
root@ubuntu:/home/fengzheng/codes/btrace# jps 10906 Jps 10860 NumberUtil
3. 可以看到剛剛執行的 java 進程為 10860
4. 編寫 btrace 腳本,腳本內容簡單如下:
package kite;
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.Strings.strcat;
import static com.sun.btrace.BTraceUtils.jstack;
import static com.sun.btrace.BTraceUtils.println;
import static com.sun.btrace.BTraceUtils.str;
/**
* NumberUtilBTrace
*
* @author fengzheng
* @date 2017/6/20
*/
@BTrace
public class NumberUtilBTrace {
@OnMethod(
clazz="kite.lab.utils.NumberUtil",
method="sum",
location=@Location(Kind.RETURN)
)
public static void func(@Return int result) {
println("trace: =======================");
println(strcat("result:", str(result)));
jstack();
}
}
意思是在執行結束后(location=@Location(Kind.RETURN) 表示執行結束)輸出結果和堆棧信息
trace: ======================= result:328350 kite.lab.utils.NumberUtil.sum(NumberUtil.java:16) kite.lab.utils.NumberUtil.main(NumberUtil.java:27)
7.
使用場景
-
比如哪些方法執行太慢,例如監控執行時間超過1s的方法
-
查看哪些方法調用了 System.gc() ,調用棧是怎樣的
-
查看方法參數或對象屬性
-
哪些方法發生了異常
多說一點,為了更好解決問題,最好還要配合事前准備和進行中監控,事前准備就是埋點嘛,在一些可能出現問題的方法中進行日志輸出,進行中監控就是利用一些實時監控工具,例如 VisualVM 、jmc 這些帶界面的工具或者 jdk 提供的命令行工具等,再高級一點的就是利用 Graphite 這樣的Metrics 工具配合 web 界面展示出來。
使用限制
-
BTrace class不能新建類, 新建數組, 拋異常, 捕獲異常,
-
不能調用實例方法以及靜態方法(com.sun.btrace.BTraceUtils除外)
-
不能將目標程序和對象賦值給BTrace的實例和靜態field
-
不能定義外部, 內部, 匿名, 本地類
-
不能有同步塊和方法
-
不能有循環
-
不能實現接口, 不能擴展類
-
不能使用assert語句, 不能使用class字面值
攔截方法定義
如何定位
1. 精准定位
直接定位到一個類下的一個方法,上面測試用的例子就是
2. 正則表達式定位
正則表達式在兩個"/" 之間,例如下面的例子,監控 javax.swing 包下的所有方法,注意正式環境中,范圍盡可能小一點,太大了性能會有影響。
@OnMethod(clazz="/javax\\.swing\\..*/", method="/.*/")
public static void swingMethods( @ProbeClassName String probeClass, @ProbeMethodName String probeMethod) {
print("entered " + probeClass + "." + probeMethod);
}
通過在攔截函數的定義里注入@ProbeClassName String probeClass, @ProbeMethodName String probeMethod 參數,告訴腳本實際匹配到的類和方法名。
3. 按接口或繼承類定位
@OnMethod(clazz="+com.kite.base", method="doSome")
4. 按注解定位
在前面加上 @ 即可,例如@OnMethod(clazz="@javax.jws.WebService", method="@javax.jws.WebMethod")
攔截時機
攔截時機由 location 決定,當然也可為同一個定位加入多個攔截時機,即可以在進入方法時攔截、方法返回時攔截、拋出異常時攔截
1. Kind.Entry與Kind.Return
2. Kind.Error, Kind.Throw和 Kind.Catch
表示異常被 throw 、異常被捕獲還有異常發生但是沒有被捕獲的情況,在攔截函數的參數定義里注入一個Throwable的參數,代表異常
@OnMethod(clazz = "com.kite.demo", location = @Location(value = Kind.LINE, line = 20))
public static void onBind() {
println("執行到第20行");
}
@OnMethod(clazz = "java.net.ServerSocket", method = "bind", location =@Location(Kind.ERROR)) public static void onBind(Throwable exception, @Duration long duration){ }
3. Kind.Call 和 Kind.Line
Kind.Call 表示被監控的方法調用了哪些其他方法,例如:
@OnMethod(clazz = "com.kite",
method = "login",
location = @Location(value = Kind.CALL, clazz = "/.*/", method = "/.*/", where = Where.AFTER))
public static void onBind(@Self Object self, @TargetInstance Object instance, @TargetMethodOrField String method, @Duration long duration){
println(strcat("self: ", str(self)));
println(strcat("instance: ", str(instance)));
println(strcat("method: ", str(method)));
println(strcat("duration(ms): ", str(duration / 1000000)));
}
Kind.Line 監測類是否執行到了設置的行數,例如:
@OnMethod(clazz = "com.kite.demo", location = @Location(value = Kind.LINE, line = 20))
public static void onBind() {
println("執行到第20行");
}
幾個例子
@OnMethod(clazz = "java.lang.System", method = "gc")
public static void onSystemGC() {
println("entered System.gc()");
jstack();
}
@OnMethod(clazz = "/com\\.kite\\.controller\\..*/",method = "/.*/",location = @Location(Kind.RETURN))
public static void slowQuery(@ProbeClassName String pcn,@ProbeMethodName String probeMethod, @Duration long duration){
if(duration > 1000000 * 100){
println(strcat("類:", pcn));
println(strcat("方法:", probeMethod));
println(strcat("時長:", str(duration / 1000000)));
}
}
注意問題
古時的風箏 【微信公眾號】gushidefengzheng

