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();函式