BTrace 是一套可以讓你動態追踨(trace)運作中程式的工具,它是由當時Sun公司的兩位高級工程師(Staff Engineer)Sundararajan Athijegannathan和Kannan Balasubramanian,在2007年開始進行的一項計劃,並於2008年在JavaOne的會議上正式對外公開:BTrace: Java™ Platform Observability by Bytecode Instrumentation (pdf),不過隨著Oracle在2010年併購Sun之後,目前只有A. Sundararajan仍然在Oracle公司服務,而B. Kannan則是在VMWare服務。
那究竟BTrace有何過人之處呢?舉個實例來說,以往要評估一個method會耗多少執行時間,可能的寫法如下:
long start = System.nanoTime(); test(); long end = System.nanoTime(); System.out.println(end - start);
但這樣的作法在小程式還可行,倘若要用來追踨一些大型Project原始碼的話就太麻煩了...Orz
雖然還有另一種方法,就是用JDK 5所提供的java.lang.instrument API來達成,透過AOP的方式在class載入之前就注入(inject)一些bytecode,如此便能無須更動原始碼即可完成所需,但這若是面對一個正在「運作中的程式(running program)」仍有些麻煩,除非透過JVM Tool Interface (JVM TI)的技術去達成,不過採用JVM TI還要寫一些native code,這實在還是不夠方便... 而就在2006年底時JDK 6(Mustang)正式推出之後,它為Java帶來了一些新的API,如:Java Attach API和Java Compiler API等。
簡單來說,透過Java Attach API不像JVM TI技術需要寫些native code,它就能藉由VirtualMachine class用純Java的方式去attach to a JVM,如:
VirtualMachine vm = VirtualMachine.attach(processid); String agent = ... vm.loadAgent(agent);
也正因為如此,所以BTrace是一個結合Java Attach API、Java Compiler API、Java Instrumentation API和ASM (A Java bytecode engineering library)等技術來實現的,而這些關鍵API對應BTrace的原始碼如下:
Java Attach API: net.java.btrace.client.Client.java
Java Compiler API: net.java.btrace.compiler.Compiler.java
Java Instrumentation API: net.java.btrace.agen.Main.java
ASM: net.java.btrace.instr.Instrumentor.java
P.S. The ASM name does not mean anything, it's just a reference to the __asm__ keyword in C. (ASM 4.0 A Java bytecode engineering library)
這裡我們來看一個實際的例子,如下述程式:
Hello.java
import java.util.*; public class Hello { public Hello(){} public void sayHello()throws Exception { Random random = new Random(); int r = random.nextInt(1000); Thread.sleep(r); System.out.println("Hello: "+r); } public static void main(String args[]) { Hello h = new Hello(); try { while(true) { Thread.sleep(1000l); h.sayHello(); } }catch(Exception e){ e.printStackTrace(); } } }
接下來~ 假設我們想知道上述程式sayHello method耗多少執行時間或stack trace都可藉由BTrace來完成,如下:
HelloTest.java
import static com.sun.btrace.BTraceUtils.*; import com.sun.btrace.annotations.*; @BTrace public class HelloTest { @OnMethod(clazz="Hello",method="sayHello") public static void trace() { jstack(); } @OnMethod(clazz="Hello",method="sayHello",location=@Location(value=Kind.RETURN)) public static void func(@Duration long duration) { println(duration); } }
上述annotations的詳細用法及BTrace的使用限制可參考BTrace User's Guide。
執行BTrace
$BTRACE_HOME/bin/btrace [pid] HelloTest.java
P.S. [pid]可透過「jps」指令來取得。
結果
Hello.sayHello(Hello.java) Hello.main(Hello.java:21) 803517667 Hello.sayHello(Hello.java) Hello.main(Hello.java:21) 681229152
相關資源
.BTrace – a Simple Way to Instrument Running Java Applications