Android各種獲取代碼調用棧的方法[補]


 

打印調用棧不用說,基本上每位開發者都會用到,討論幾個方法,以前也說過,http://blog.csdn.net/freshui/article/details/9456889 再次簡單整理一下吧,啰嗦就啰嗦了 :)

 
基本分兩大類,一類是靜態的,要把打印語句插入到代碼中,一類是動態的,需要看的時候,查看一下,實時觀測各線程調用棧情況。
 

靜態方法

 

1. Java中打印調用棧

 

比較簡單,利用Throwable,直接log中打印出來:

 

[java]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. Log.d(TAG, Log.getStackTraceString(new Throwable()));   

 

 

2. C++中打印調用棧

也是比較簡單,直接利用CallStack類
[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. #include <utils/CallStack.h>    
  2. ...    
  3. CallStack stack("FUCK");    
  4. ...  
CallStack從4.3開始,重寫了下,現在使用比較簡單了,實例化的地方就直接打印了。
 

3. C中打印調用棧

   以前寫的方法,又復雜又不好用(版本更新,很多庫和接口都變了)。其實,在C中打印調用棧,也是超級簡單。只要隨便找一個C++的源文件,實現一個函數就可以了:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. #include <utils/CallStack.h>    
  2. extern "C" void printCallStack();  
  3. void printCallStack() {  
  4.  CallStack stk("Fuck");  
  5. }  

 

這可以添加在Android現有code中,也可以自己新增一個cpp的文件編進來。 想要打印的地方,直接調用即可:

[cpp]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. ...  
  2. extern void printCallStack();  
  3. void test_c_code() {  
  4.  ...  
  5.  printCallStack();  
  6.  ...  
  7. }  
  8. ...  

注意:

  • C++代碼的printCallStack聲明一定要有extern "C",否則c++的mangle后的符號,C中是不認的
  • 添加后注意庫的鏈接,還有命名空間別搞錯了。

另外,以上方法實際上也就適用在匯編中打印CallStack,都一樣的~~
以上CallStack方法, NDK中不好用,而且舊版本可能有兼容性問題,NDK想用的話,需要處理一下,不過不保證每家做出來的都一樣(理論上google沒統一的,可能都可能不保險..),應用包進去的話,估計要小心點。

4. 內核態代碼中打印調用棧

內核看調用棧,也是比較簡單的,直接在要看的地方,加入:

[html]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. WARN_ON(1);  
 

靜態打印的問題:

  1. 靜態打印調用棧,需要修改代碼,重新編譯運行
  2. 對於底層函數,或者調用次數很多的函數,會出現海量流氓log,沒法真正分析了。

解決方法,改代碼這個沒法解決,針對流氓log,可以想一些辦法:

 

  1. callStack的地方,條件觸發,根據上下文,設置條件觸發
  2. 不好以上下文設條件的,可以根據進程名/uid等設置觸發條件
  3. 可以用大絕招:自己定義property設條件,可以在shell串口通過改變property值來toggle log開關。

 

動態方法

 

 

動態的話,其實ADB連上DDMS后,查看更方便,Java堆棧查看起來特別順手,這里只看shell命令的幾種方式:

1. Dump Java調用棧

java調用棧實際也包含了native的棧和內核棧,比較全一些,看的也更清晰。使用方式也很簡單:

[html]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. kill -3 <pid>  


    由於是虛擬機幫忙的,目前只適用於虛擬機進程(即java進程)
2. Dump native棧
    用Debuggerd很容易獲取native棧信息,這個可以當采樣或者分析卡住或死鎖問題。

 

[html]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. debuggerd -b <pid>  



    注意這個對java進程,是得不到java調用棧的,不過得到的是虛擬機執行java指令時的虛擬機調用棧,debugger 虛擬機必用。
3. 查看內核棧
    比較難,大多系統沒有開放,看不到,不過調試版本可以。
    查看方法是:

[html]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. cat /proc/<pid>/task/<tid>/stack  

 

不過最新Naugot上的調用棧,看起來最新的不做demangle了:

[html]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. "main" prio=tid=1 Native  
  2.   | group="main" sCount=dsCount=obj=0x748ef6b8 self=0xa638b400  
  3.   | sysTid=1774 nice=cgrp=default sched=0/0 handle=0xaa74b534  
  4.   | state=schedstat=( 0 0 0 ) utm=18 stm=193 core=HZ=100  
  5.   | stack=0xbf403000-0xbf405000 stackSize=8MB  
  6.   | held mutexes=  
  7.   kernel: SyS_epoll_wait+0x23c/0x2d1  
  8.   kernel: SyS_epoll_pwait+0x70/0xe1  
  9.   kernel: sysenter_do_call+0x12/0x22  
  10.   native: #00 pc ffffe424  [vdso] (__kernel_vsyscall+16)  
  11.   native: #01 pc 000779ab  /system/lib/libc.so (__epoll_pwait+43)  
  12.   native: #02 pc 00020cb0  /system/lib/libc.so (epoll_pwait+112)  
  13.   native: #03 pc 00020d0e  /system/lib/libc.so (epoll_wait+62)  
  14.   native: #04 pc 00018f1b  /system/lib/libutils.so (_ZN7android6Looper9pollInnerEi+203)  
  15.   native: #05 pc 00018d84  /system/lib/libutils.so (_ZN7android6Looper8pollOnceEiPiS1_PPv+68)  
  16.   native: #06 pc 000d38c3  /system/lib/libandroid_runtime.so (_ZN7android18NativeMessageQueue8pollOnceEP7_JNIEnvP8_jobjecti+77)  
  17.   native: #07 pc 000d3934  /system/lib/libandroid_runtime.so (???)  
  18.   native: #08 pc 007b1c0c  /system/framework/x86/boot-framework.oat (Java_android_os_MessageQueue_nativePollOnce__JI+136)  
  19.   at android.os.MessageQueue.nativePollOnce(Native method)  
  20.   at android.os.MessageQueue.next(MessageQueue.java:323)  
  21.   at android.os.Looper.loop(Looper.java:136)  
  22.   at android.app.ActivityThread.main(ActivityThread.java:6119)  
  23.   at java.lang.reflect.Method.invoke!(Native method)  
  24.   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)  
  25.   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)  
這有點蛋疼,讀起來有點難受,看提交記錄,是為了繞某個bug:
[html]  view plain  copy
 
 在CODE上查看代碼片派生到我的代碼片
  1. commit 7c903fbacfaf449fb4f7a9fa2f1a1b6ba2db5330  
  2. Author: Josh Gao <jmgao@google.com>  
  3. Date:   Tue Mar 22 11:29:17 2016 -0700  
  4.     Don't demangle symbol names.  
  5.       
  6.     Bug: http://b/27299236  
  7.     Change-Id: I3a698c6d93e262fd78e743c1e6e946b054b9dcd1  

當然,這些動態看的調用棧,通過adb bugreport或dumpstate命令,都能得到。如果有bug的話,可以先保存一份bugreport,存下了放在后面慢慢看,然后再來解刨場景分析。
   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM