打印調用棧不用說,基本上每位開發者都會用到,討論幾個方法,以前也說過,http://blog.csdn.net/freshui/article/details/9456889 再次簡單整理一下吧,啰嗦就啰嗦了 :)
靜態方法
1. Java中打印調用棧
比較簡單,利用Throwable,直接log中打印出來:
- Log.d(TAG, Log.getStackTraceString(new Throwable()));
2. C++中打印調用棧
- #include <utils/CallStack.h>
- ...
- CallStack stack("FUCK");
- ...
3. C中打印調用棧
以前寫的方法,又復雜又不好用(版本更新,很多庫和接口都變了)。其實,在C中打印調用棧,也是超級簡單。只要隨便找一個C++的源文件,實現一個函數就可以了:
- #include <utils/CallStack.h>
- extern "C" void printCallStack();
- void printCallStack() {
- CallStack stk("Fuck");
- }
這可以添加在Android現有code中,也可以自己新增一個cpp的文件編進來。 想要打印的地方,直接調用即可:
- ...
- extern void printCallStack();
- void test_c_code() {
- ...
- printCallStack();
- ...
- }
- ...
注意:
- C++代碼的printCallStack聲明一定要有extern "C",否則c++的mangle后的符號,C中是不認的
- 添加后注意庫的鏈接,還有命名空間別搞錯了。
另外,以上方法實際上也就適用在匯編中打印CallStack,都一樣的~~
以上CallStack方法, NDK中不好用,而且舊版本可能有兼容性問題,NDK想用的話,需要處理一下,不過不保證每家做出來的都一樣(理論上google沒統一的,可能都可能不保險..),應用包進去的話,估計要小心點。
4. 內核態代碼中打印調用棧
內核看調用棧,也是比較簡單的,直接在要看的地方,加入:
- WARN_ON(1);
靜態打印的問題:
- 靜態打印調用棧,需要修改代碼,重新編譯運行
- 對於底層函數,或者調用次數很多的函數,會出現海量流氓log,沒法真正分析了。
解決方法,改代碼這個沒法解決,針對流氓log,可以想一些辦法:
- callStack的地方,條件觸發,根據上下文,設置條件觸發
- 不好以上下文設條件的,可以根據進程名/uid等設置觸發條件
- 可以用大絕招:自己定義property設條件,可以在shell串口通過改變property值來toggle log開關。
動態方法
動態的話,其實ADB連上DDMS后,查看更方便,Java堆棧查看起來特別順手,這里只看shell命令的幾種方式:
1. Dump Java調用棧
java調用棧實際也包含了native的棧和內核棧,比較全一些,看的也更清晰。使用方式也很簡單:
- kill -3 <pid>
由於是虛擬機幫忙的,目前只適用於虛擬機進程(即java進程)
2. Dump native棧
用Debuggerd很容易獲取native棧信息,這個可以當采樣或者分析卡住或死鎖問題。
- debuggerd -b <pid>
注意這個對java進程,是得不到java調用棧的,不過得到的是虛擬機執行java指令時的虛擬機調用棧,debugger 虛擬機必用。
3. 查看內核棧
比較難,大多系統沒有開放,看不到,不過調試版本可以。
查看方法是:
- cat /proc/<pid>/task/<tid>/stack
不過最新Naugot上的調用棧,看起來最新的不做demangle了:
- "main" prio=5 tid=1 Native
- | group="main" sCount=1 dsCount=0 obj=0x748ef6b8 self=0xa638b400
- | sysTid=1774 nice=0 cgrp=default sched=0/0 handle=0xaa74b534
- | state=S schedstat=( 0 0 0 ) utm=18 stm=193 core=0 HZ=100
- | stack=0xbf403000-0xbf405000 stackSize=8MB
- | held mutexes=
- kernel: SyS_epoll_wait+0x23c/0x2d1
- kernel: SyS_epoll_pwait+0x70/0xe1
- kernel: sysenter_do_call+0x12/0x22
- native: #00 pc ffffe424 [vdso] (__kernel_vsyscall+16)
- native: #01 pc 000779ab /system/lib/libc.so (__epoll_pwait+43)
- native: #02 pc 00020cb0 /system/lib/libc.so (epoll_pwait+112)
- native: #03 pc 00020d0e /system/lib/libc.so (epoll_wait+62)
- native: #04 pc 00018f1b /system/lib/libutils.so (_ZN7android6Looper9pollInnerEi+203)
- native: #05 pc 00018d84 /system/lib/libutils.so (_ZN7android6Looper8pollOnceEiPiS1_PPv+68)
- native: #06 pc 000d38c3 /system/lib/libandroid_runtime.so (_ZN7android18NativeMessageQueue8pollOnceEP7_JNIEnvP8_jobjecti+77)
- native: #07 pc 000d3934 /system/lib/libandroid_runtime.so (???)
- native: #08 pc 007b1c0c /system/framework/x86/boot-framework.oat (Java_android_os_MessageQueue_nativePollOnce__JI+136)
- at android.os.MessageQueue.nativePollOnce(Native method)
- at android.os.MessageQueue.next(MessageQueue.java:323)
- at android.os.Looper.loop(Looper.java:136)
- at android.app.ActivityThread.main(ActivityThread.java:6119)
- at java.lang.reflect.Method.invoke!(Native method)
- at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
- at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
- commit 7c903fbacfaf449fb4f7a9fa2f1a1b6ba2db5330
- Author: Josh Gao <jmgao@google.com>
- Date: Tue Mar 22 11:29:17 2016 -0700
- Don't demangle symbol names.
- Bug: http://b/27299236
- Change-Id: I3a698c6d93e262fd78e743c1e6e946b054b9dcd1
當然,這些動態看的調用棧,通過adb bugreport或dumpstate命令,都能得到。如果有bug的話,可以先保存一份bugreport,存下了放在后面慢慢看,然后再來解刨場景分析。