1. 程式人生 > >跟蹤Android callback 呼叫堆疊

跟蹤Android callback 呼叫堆疊

0 在除錯 android  系統時可以通過列印呼叫堆疊 callback stack 來分析和解決android問題。

1 java 層列印callback stack 可以通過 catch exception 然後 使用 Log.w(LOGTAG, Log.getStackTraceString(throwable)) 列印呼叫堆疊

Throwable throwable = new Throwable();   
        Log.w(LOGTAG, Log.getStackTraceString(throwable));
或者
            try {
                wait();
            } catch (InterruptedException e) {
                Log.e(LOGTAG, "Caught exception while waiting for overrideUrl");
                Log.e(LOGTAG, Log.getStackTraceString(e));
            }

2 c/c++, 通常情況下,可以通過segment fault 等錯誤即訊號 SIGSEGV(11) 做出相應處理,即設定SIGSEGV的handler呼叫libc的backtrace,即可列印對於的callback stack;定位問題所在;但在android 中, bionic 不提供類似功能,而且只能通過logcat才能看到log資訊,但是我們也可以根據android出錯資訊獲得呼叫堆疊資訊,如以下出錯資訊:
    D/CallStack( 2029): #00  pc 00008156  /system/lib/hw/audio.primary.tf4.so
    D/CallStack( 2029): #01  pc 000089e8  /system/lib/hw/audio.primary.tf4.so (android_audio_legacy::AudioHardware::AudioStreamOutALSA::setParameters(android::String const&)+139)
    D/CallStack( 2029): #02  pc 0000b2ca  /system/lib/hw/audio.primary.tf4.so
    D/CallStack( 2029): #03  pc 0003ac6a  /system/lib/libaudioflinger.so (android::AudioFlinger::MixerThread::checkForNewParameters_l()+377)
    D/CallStack( 2029): #04  pc 0003960a  /system/lib/libaudioflinger.so (android::AudioFlinger::PlaybackThread::threadLoop()+145)
    D/CallStack( 2029): #05  pc 00011264  /system/lib/libutils.so (android::Thread::_threadLoop(void*)+111)
    D/CallStack( 2029): #06  pc 00010dca  /system/lib/libutils.so
    D/CallStack( 2029): #07  pc 0000e3f8  /system/lib/libc.so (__thread_entry+72)
    D/CallStack( 2029): #08  pc 0000dae4  /system/lib/libc.so (pthread_create+160)
2.1 可以使用 arm-linux-addr2line 獲得呼叫堆疊,arm-eabi-addr2line -C -f -e symbols/system/lib/*.so addr, 這樣就可以打印出 呼叫堆疊資訊了。

2.2 使用ndk-stack 工具, 儲存出錯log為 logcat.log, cat logcat..log | ndk-stack -sym ~/[SOURCE-DIR]/out/target/product/[PROJECT]/symbols/system/lib/ 列印呼叫堆疊

2.3 使用 panic.py(程式碼在下面) 指令碼分析並列印呼叫堆疊,/panic.py logcat.log, 注意logcat 必須轉換成以下格式

    D/CallStack( 2029): #00  pc 00008156  /system/lib/hw/audio.primary.tf4.so
    D/CallStack( 2029): #01  pc 000089e8  /system/lib/hw/audio.primary.tf4.so
    D/CallStack( 2029): #02  pc 0000b2ca  /system/lib/hw/audio.primary.tf4.so
    D/CallStack( 2029): #03  pc 0003ac6a  /system/lib/libaudioflinger.so
    D/CallStack( 2029): #04  pc 0003960a  /system/lib/libaudioflinger.so
    D/CallStack( 2029): #05  pc 00011264  /system/lib/libutils.so
    D/CallStack( 2029): #06  pc 00010dca  /system/lib/libutils.so
    D/CallStack( 2029): #07  pc 0000e3f8  /system/lib/libc.so
    D/CallStack( 2029): #08  pc 0000dae4  /system/lib/libc.so
這樣執行指令碼 ./panic.py setincallpath_l.txt, 列印定位資訊:
    [email protected]:/meizu/JellyBean-4.2.1/trunk/out/target/product/tf4$ ./panic.py ./backtrack/setincall_path.txt
    read file ok
    AudioHardware.cpp:829         android_audio_legacy::AudioHardware::setIncallPath_l(unsigned int)
    AudioHardware.cpp:1537        android_audio_legacy::AudioHardware::AudioStreamOutALSA::setParameters(android::String8 const&)
    audio_hw_hal.cpp:197          out_set_parametersAudioFlinger.cpp:3535         android::AudioFlinger::MixerThread::checkForNewParameters_l()
    AudioFlinger.cpp:2586         android::AudioFlinger::PlaybackThread::threadLoop()
    Threads.cpp:793               android::Thread::_threadLoop(void*)
    Threads.cpp:132               thread_data_t::trampoline(thread_data_t const*)
    pthread.c:204                 __thread_entry
    pthread.c:348                 pthread_create
2.4 google提供了一個python指令碼,可以從 http://code.google.com/p/android-ndk-stacktrace-analyzer/ 下載這個python指令碼,然後使用
adb logcat -d > logfile 匯出 crash 的log,
使用 arm-eabi-objdump (位於build/prebuilt/linux-x86/arm-eabi-4.2.1/bin下面)把so或exe轉換成彙編程式碼,如:
arm-eabi-objdump -S mylib.so > mylib.asm,
然後使用指令碼
python parse_stack.py <asm-file> <logcat-file>

panic.py 需要設定環境,如下:

#!/usr/bin/python
# stack symbol parser

import os
import string
import sys

#define android product name
#ANDROID_PRODUCT_NAME = 'generic'
ANDROID_PRODUCT_NAME = 'ok'

ANDROID_WORKSPACE = os.getcwd()+"/"

# addr2line tool path and symbol path
addr2line_tool = 'arm-linux-addr2line'
symbol_dir = ANDROID_WORKSPACE + '/symbols'
symbol_bin = symbol_dir + '/system/bin/'
symbol_lib = symbol_dir + '/system/lib/'

class ReadLog:
    def __init__(self,filename):
        self.logname = filename
    def parse(self):
        f = file(self.logname,'r')
        lines = f.readlines()
        if lines != []:
            print 'read file ok'
        else:
            print 'read file failed'
        result =[]
        for line in lines:
            if line.find('stack') != -1:
                print 'stop search'
                break
            elif line.find('system') != -1:
                #print 'find one item' + line
                result.append(line)
        return result

class ParseContent:
    def __init__(self,addr,lib):
            self.address = addr # pc address
            self.exename = lib  # executable or shared library
    def addr2line(self):
        cmd = addr2line_tool + " -C -f -s -e " + symbol_dir + self.exename + " " + self.address
        #print cmd
        stream = os.popen(cmd)
        lines = stream.readlines();
        list = map(string.strip,lines)
        return list
    
inputarg = sys.argv
if len(inputarg) < 2:
    print 'Please input panic log'
    exit()

filename = inputarg[1]
readlog = ReadLog(filename)
inputlist = readlog.parse()

for item in inputlist:
    itemsplit = item.split()
    test = ParseContent(itemsplit[-2],itemsplit[-1])
    list = test.addr2line()
    print "%-30s%s" % (list[1],list[0])

3 出了上述系統主動輸出出錯資訊,我們還可以通過程式碼在系統不出錯的情況下,列印呼叫資訊,然後通過panic.py 列印呼叫堆疊

3.1 在cpp檔案新增如下資訊

    #include <utils/CallStack.h>
    ...
    status_t AudioHardware::setIncallPath_l(uint32_t device) {
    ...
    #ifdef _ARM_
        android::CallStack stack;
        stack.update(1, 100);
        stack.dump("");
    #endif
    ...
    }
3.2 在Android.mk中,加入:
    LOCAL_CFLAGS += -D_ARM_
    LOCAL_SHARED_LIBRARIES += libutils

這樣將會列印上面所述的呼叫資訊,便於分析程式碼,debug,定位問題。