簡易的btrace需求
偶現的方法執行慢,我們是可以用jstack捕捉到的,但是慢到什麼地步卻是不一定知道的,現在就需要在不重啓應用的情況下,獲取方法執行的時間。
需求特點
- 應用不重啓
- 獲取方法執行時間
技術選型
想要打印出時間,起碼想到的是aop的方式。常規的方法是必須重啓應用才能加的,典型的就是spring的aop,如果允許修改代碼的話可以使用動態代理,或者自己寫死到代碼裏。
操作方案
動態代理或者寫死到代碼
這種情況對代碼的入侵太高了,如果要去掉這個功能,修改代碼也是很麻煩的。
spring aop
這個需要依賴spring框架來做。可以不修改代碼,但是不能做到動態生效。
javaagent
javaagent在1.6之後可以使用遠程attach的方式,進行類的重新轉化。這個是滿足需求的。這個不瞭解的話可以去網上查查看。
字節碼增強技術
方式選擇好以後,那麼關鍵點就是如何選擇修改字節碼的框架。
javassist
javassist是相對好用的,不過他想要在一個方法前後加代碼,是通過方法包裝來的,就是命名一個新的方法名和現在執行的方法名一樣,然後把舊的方法重新命名。流程如下: 轉化前:
public void hello(){
int a=0;
}
轉化後
public void hello(){
xxx
helloxx();
xxx
}
public void helloxx(){
int a=0;
}
這種情況是transform的做法,但是retransform有不能新增方法的限制。jdk的文檔描述如下
The retransformation must not add, remove or rename fields or methods, change the signatures of methods, or change inheritance.
asm
asmapi操作比較麻煩。但是可以直接修改方法體的內容。 轉化前:
public void hello(){
int a=0;
}
轉化後:
public void hello(){
xxx
int a=0;
xxx
}
asm的做法也有兩種方式,一種是增加方法調用,然後把傳遞的值保存在threadlocal裏,另外一種就是把所有的內容寫到方法裏中。這兩種都可以滿足需求,第一種實現方式還簡單一些,不用新增本地變量。
實現方式
修改字節碼的代碼比較枯燥,所有直接附上代碼地址,此次使用的是asm增加方法局部變量的方式做的,這樣的demo網上博客沒有,asm的手冊中也沒有這樣的例子,如果有興趣的可以去看看 https://github.com/xpbob/lightTrace