MF99 coding 💻

keep learning; keep coding;

如何在 Log 中印出 Call stack

這邊提供一個最近學到的小技巧,有時候常常需要在 Trace code 的時候打印 Log 來追蹤變數的變化、或是 function 的 call flow。 但是有時候常常除了知道某個 function 被 call 的時候送進來的參數以外,還想要知道到底是 「誰」 Call 的。 像是這樣:

D/Debug   ( 8776): com.android.calculator2.doTest.<init>(doTest.java:13)
D/Debug   ( 8776): com.android.calculator2.Calculator.onResume(Calculator.java:230)
D/Debug   ( 8776): android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1192)
D/Debug   ( 8776): android.app.Activity.performResume(Activity.java:5211)
D/Debug   ( 8776): more...

這時候就需要以下的技巧了!

請參考下面的 Sample code

StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
for(StackTraceElement e: stacktrace)  
  Log.d("Debug", e.toString());

基本上就是利用 Thread 中的 getStackTrace() 這個 method 來取得目前的 Call Stack。 然後透過一個迴圈把它依序印出來。 參照最上面的 Sample code 打印出來的效果會像是以下的樣子

D/Debug   ( 8776): dalvik.system.VMStack.getThreadStackTrace(Native Method)
D/Debug   ( 8776): java.lang.Thread.getStackTrace(Thread.java:579)
D/Debug   ( 8776): com.android.calculator2.doTest.<init>(doTest.java:13)
D/Debug   ( 8776): com.android.calculator2.Calculator.onResume(Calculator.java:230)
D/Debug   ( 8776): android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1192)
D/Debug   ( 8776): android.app.Activity.performResume(Activity.java:5211)
D/Debug   ( 8776): android.app.ActivityThread.performResumeActivity(ActivityThread.java:2780)
D/Debug   ( 8776): android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2819)
D/Debug   ( 8776): android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2266)
D/Debug   ( 8776): android.app.ActivityThread.access$600(ActivityThread.java:141)
D/Debug   ( 8776): android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
D/Debug   ( 8776): android.os.Handler.dispatchMessage(Handler.java:99)
D/Debug   ( 8776): android.os.Looper.loop(Looper.java:137)
D/Debug   ( 8776): android.app.ActivityThread.main(ActivityThread.java:5103)
D/Debug   ( 8776): java.lang.reflect.Method.invokeNative(Native Method)
D/Debug   ( 8776): java.lang.reflect.Method.invoke(Method.java:525)
D/Debug   ( 8776): com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
D/Debug   ( 8776): com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
D/Debug   ( 8776): dalvik.system.NativeStart.main(Native Method)

裡面包含了 Class / Method name 以及 File Name 還有 Line number (Native method 除外) 其中[0], [1] 以及最後幾個基本上都是固定的,所以如果精簡一點的話可以從[2]開始打印會更容易懂,由於他會一直 print 到 dalvik.system.NativeStart.main,這部份也沒什麼必要,所以基本上大概從 [2] 開始然後印個8~10行左右其實對於 debug 來說就很夠用了。

另外如果還嫌他預設的 toString() 方式資訊太多,你只需要局部的話,也可以參考 StackTraceElement | Android Developer

當中可以只要印出需要的資訊 (Class / Method name, line number 等) 對於 Debug 時有需要 track call stack 的時候還蠻實用的!