1. 程式人生 > >Android下列印呼叫棧

Android下列印呼叫棧


1. Java層如何列印呼叫棧?

在優化Android啟動過程時,同事給出一種打印出呼叫棧的函式。分享一下

    java.util.Map<Thread, StackTraceElement[]> ts = Thread.getAllStackTraces();   
    StackTraceElement[] ste = ts.get(Thread.currentThread());   
    for (StackTraceElement s : ste) {   
        android.util.Slog.e("SS     ", s.toString()); //這個是android自帶的,如果沒有,用其他的列印函式一樣   
    }  

為了打印出在android啟動時,Zygote啟動的所有java應用。在

    //frameworks/base/services/java/com/android/server/am/ActivityManagerService.java   
    private final void startProcessLocked(ProcessRecord app, String hostingType, String hostingNameStr) {   
        //debug add   
        java.util.Map<Thread, StackTraceElement[]> ts = Thread.getAllStackTraces();   
        StackTraceElement[] ste = ts.get(Thread.currentThread());   
        for (StackTraceElement s : ste) {   
            android.util.Slog.e("SS     ", s.toString());   
        }        
    }

就可以在終端中使用命令logcat來檢視打印出的呼叫棧了。

2. C/C++層如何列印呼叫棧?

Java可以方便的列印函式的呼叫棧。C/C++也可以。
為了顯示在vm/Misc.cpp中誰呼叫了函式dvmAllocRegion,在這個函式中加入下面紅色程式碼:
#include <utils/CallStack.h>
...

void *dvmAllocRegion(size_t byteCount, int prot, const char *name) 
{
    ...
#ifdef _ARM_
    LOGW("name=%s", name);
    android::CallStack stack;
    stack.update(1, 100);
    stack.dump("");
#endif
...
}

在vm/Dvm.mk中,加入:
  LOCAL_CFLAGS += -D_ARM_
  LOCAL_SHARED_LIBRARIES += libutils

"mmm dalvik" 得到libdvm.so。然後
adb root
adb remount
adb push libdvm.so /system/lib
adb reboot
系統重啟後,新的libdvm.so就投入使用了。



列印呼叫棧是android平臺問題定位的基本方法,如果需要知道誰在呼叫某個函式,

可以在此函式中新增列印呼叫棧函式,弄清楚函式之間的呼叫關係。

1. Java層列印呼叫棧方法
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Log.w(TAG, "Called: " + this, here);

2. C++層列印呼叫棧方法

CallStack stack;
stack.update();
stack.dump();

備註:下面操作是可選操作,但加上去之後會有一些額外的功能

#define HAVE_DLADDR 1 :可以從lib中自己轉成c++程式碼行,不需要手動反編譯

#define HAVE_CXXABI 1:將c++ 已被name mangling的函式名轉化為原始檔中定義的函式名。

並在檔案frameworks/base/libs/utils/Android.mk中大約105行(LOCAL_SHARED_LIBRARIES)後新增

ifeq ($(TARGET_OS),linux)

LOCAL_SHARED_LIBRARIES += libdl

endif重新編譯push生成的libutils.so到/system/lib/目錄下,重啟裝置。

此外,由於CallStack.dump中使用的LOGD進行的列印,因此需要將後臺的Log Level設定為D一下才能出來。

3. C函式列印呼叫棧

可以參考CallStack.cpp的實現,通過呼叫_Unwind_Backtrace完成。

4. Kernel層列印呼叫棧方法
dump_stack();函式