Android裏調用callstack(轉)

1.        爲什麼要打印函數調用堆棧?

打印調用堆棧可以直接把問題發生時的函數調用關係打出來,非常有利於理解函數調用關係。比如函數A可能被B/C/D調用,如果只看代碼,B/C/D誰調用A都有可能,如果打印出調用堆棧,直接就把誰調的打出來了。

不僅如此,打印函數調用堆棧還有另一個好處。在Android代碼裏,函數命名很多雷同的,虛函數調用,幾個類裏的函數名相同等,即使用source insight工具看也未必容易看清函數調用關係。如果用了堆棧打印,很容易看到函數調用邏輯。

那麼一個問題來了,Android/kernel本身在發生問題(kernel panic, tombstone, …)時,都可以打出詳細的堆棧信息,這裏幹嘛還要費勁研究打堆棧?答案是發生問題時的堆棧的確很詳細,但這裏研究的是不影響(準確說是基本不影響)系統運行的境況下,打印出某個情形下的堆棧信息,這個對源代碼邏輯研究很有幫助。或者是說,在未發生kernel panic, tombstone的時候,我們需要打印出堆棧信息。

 

2.        Linux Kernel

Kernel裏最簡單,直接有幾現成的函數可以使用:

dump_stack() 這個函數打出當前堆棧和函數調用backtrace後接着運行

WARN_ON(x) 這個函數跟dump_stack很像,它有個條件,如果條件滿足了就把stack打出來。

打印出來的結果都在kernel log裏,一般dmesg命令就可以看到了

 

3.        Native C++

Android在新版(至少5.0, 6.0)里加入了CallStack類,這個類可以打出當前的backtrace。用法很簡單:

前面確保包含頭文件#include <utils/CallStack.h> 

Android.mk的庫依賴列表(LOCAL_SHARED_LIBRARIES)裏包含libutilscallstack,一般都已經包含了。

然後在要打印堆棧處加入android::CallStack cs(“My CallStack Debug”);

“My CallStack Debug”是在logcat輸出的TAG,這裏可以自己定義。如果上下文已經在android namespace裏,”android::”前綴就不必加了。

Native C++的輸出log可以在logcat裏看到。

 

注意,在網上的一些文檔裏說要這麼用:

CallStack stack; 

stack.update(); 

stack.dump();

這樣做已經不行了,在新版Android裏編譯不過。

 

4.        Native C

Android對C的堆棧打印支持不太好。過去網上的文章一般是推薦libcorkscrew.so,並加入大段代碼來unwind_backtrace。新版Android上libcorkscrew已經被拿掉了,網上的加載libcorkscrew庫的方法自然就不能用了。

 

一個簡單方法是用C語言調C++的函數,對,就是extern “C”。

先在項目里加入一個c++文件,比如callstack.cpp,裏面是:

#include <utils/CallStack.h>
extern "C" void dumping_callstack(void);
void dumping_callstack(void)
{
    android::CallStack cs("My CallStack Debug");
}

在項目裏再加入一個c++的頭文件,比如callstack.h,裏面是:

void dumping_callstack(void);

 

在Android.mk裏源文件列表LOCAL_SRC_FILES里加入callstack.cpp,並確保libutilscallstack在依賴列表裏。

在native C裏include callstack.h後直接調用dumping_callstack()就可以了。

這個log也可以在logcat裏看到。

 

For Celadon:

callstack.cpp:

複製代碼
#include "callstack.h"
#include <utils/CallStack.h>
extern "C" {
    void dump_stack02(void)
   {
        //android::CallStack stack;
        //stack.update();
        //stack.dump("INTEL-MESA");
       android::CallStack cs("INTEL-MESA");
       cs.update();
       cs.log("INTEL-MESA", ANDROID_LOG_ERROR, "");
   }
}
複製代碼

callstack.h:

複製代碼
#ifndef _CALLBACK_H
#define _CALLBACK_H
#ifdef __cplusplus
extern "C" {
#endif
    void dump_stack02(void);
#ifdef __cplusplus
}
#endif
#endif
複製代碼

in Android.mk, add libutilscallstack in LOCAL_SHARED_LIBRARIES

 LOCAL_SHARED_LIBRARIES := \
+   libutilscallstack \

in Makefile.sources, add the callstack.cpp and callstack.h like below

複製代碼
@@ -2,4 +2,6 @@ C_SOURCES := \
    virgl_drm_public.h \
    virgl_drm_winsys.c \
    virgl_drm_winsys.h \
-   virtgpu_drm.h
+   virtgpu_drm.h \
+   callstack.cpp \
+   callstack.h
複製代碼

 

in the file you want to add callstack:

調用 dump_stack02();

 

還需要

$ adb root

$ adb remount

$ adb push out/target/product/caas/vendor/lib64/egl/libGLES_mesa.so /vendor/lib64/egl/
$ adb push out/target/product/caas/vendor/lib/egl/libGLES_mesa.so /vendor/lib/egl/

$ adb push out/target/product/caas/vendor/lib64/dri/i965_dri.so /vendor/lib64/dri/
$ adb push out/target/product/caas/vendor/lib/dri/i965_dri.so /vendor/lib/dri/

 

然後

$ adb shell sync

最後把QEMU停掉,再啓動。

 

5.        Java

Java最簡單,它的backtrace最詳細,連文件名和行號都打出來了:

Exception e = new Exception("haha");

e.printStackTrace();

log在logcat裏看以看到。

 

大部分內容轉自: http://blog.sina.com.cn/s/blog_7213e0310102wtge.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章