函数调用栈分析
Android中C/C++代码使用CallStack打印出的函数调用栈通常不是特别的详细,有些地方只是打印出了库之间的调用关系,具体的函数之间的调用关系无法清楚的获取;
基于这些问题,我们需要使用其他的一些工具对打印出的调用栈进行进一步的分析;
打印出的函数调用栈信息如下:
D/Audio ( 262): #00 pc 0000cff1 /system/lib/libutils.so (android::CallStack::update(int, int)+52)
D/Audio ( 262): #01 pc 0000d107 /system/lib/libutils.so (android::CallStack::CallStack(char const*, int)+38)
D/Audio ( 262): #02 pc 00004f20 /system/lib/hw/audio.primary.msm8974.so (dumping_callstack+28)
D/Audio ( 262): #03 pc 000073f8 /system/lib/hw/audio.primary.msm8974.so (enable_snd_device+372)
D/Audio ( 262): #04 pc 00008da4 /system/lib/hw/audio.primary.msm8974.so (select_devices+392)
D/Audio ( 262): #05 pc 0000ac88 /system/lib/hw/audio.primary.msm8974.so (start_output_stream+344)
D/Audio ( 262): #06 pc 0000b160 /system/lib/hw/audio.primary.msm8974.so
D/Audio ( 262): #07 pc 00004ca9 /system/lib/libnbaio.so (android::AudioStreamOutSink::write(void const*, unsigned int)+16)
D/Audio ( 262): #08 pc 000340a5 /system/lib/libaudioflinger.so
D/Audio ( 262): #09 pc 00034afb /system/lib/libaudioflinger.so
D/Audio ( 262): #10 pc 000104d5 /system/lib/libutils.so (android::Thread::_threadLoop(void*)+112)
D/Audio ( 262): #11 pc 00010045 /system/lib/libutils.so
D/Audio ( 262): #12 pc 00016afb /system/lib/libc.so (__pthread_start(void*)+30)
D/Audio ( 262): #13 pc 00014b2b /system/lib/libc.so (__start_thread+6)
方法一:
使用arm-linux交叉编译工具,利用其中的addr2line工具。
arm-linux-addr2line功能: 把程序地址转换为文件名和行号。在命令行中给它一个地址和一个可执行文件名,它就会使用这个可执行文件的调试信息指出在给出的地址上是哪个文件以及行号。
需要使用到Android编译出的symbols信息,位于~/[SOURCE-DIR]/out/target/product/[PROJECT]/symbols/system/lib/ 中;
使用如下命令可以得到symbols对于的函数名:
addr2line -C -f -e out/target/product/Leopard/symbols/system/lib/hw/audio.primary.msm8974.so 000073f8
对于的调用栈信息为:
D/Audio ( 262): #03 pc 000073f8 /system/lib/hw/audio.primary.msm8974.so (enable_snd_device+372)
输出的函数信息为:
enable_snd_device
~/[SOURCE-DIR]/hardware/qcom/audio/hal/audio_hw.c:432
方法二:
使用ndk-stack 工具;
保存出错log为 logcat.log,
使用如下命令打印调用栈对应的函数名:
cat logcat..log | ndk-stack -sym ~/[SOURCE-DIR]/out/target/product/[PROJECT]/symbols/system/lib/
方法三:
使用 panic.py(代码在下面) 脚本分析并打印调用堆栈;
$ ./panic.py logcat.log
注意logcat 必须转换成以下格式
D/Audio ( 262): #00 pc 0000cff1 /system/lib/libutils.so
D/Audio ( 262): #01 pc 0000d107 /system/lib/libutils.so
D/Audio ( 262): #02 pc 00004f20 /system/lib/hw/audio.primary.msm8974.so
D/Audio ( 262): #03 pc 000073f8 /system/lib/hw/audio.primary.msm8974.so
D/Audio ( 262): #04 pc 00008da4 /system/lib/hw/audio.primary.msm8974.so
D/Audio ( 262): #05 pc 0000ac88 /system/lib/hw/audio.primary.msm8974.so
D/Audio ( 262): #06 pc 0000b160 /system/lib/hw/audio.primary.msm8974.so
D/Audio ( 262): #07 pc 00004ca9 /system/lib/libnbaio.so
D/Audio ( 262): #08 pc 000340a5 /system/lib/libaudioflinger.so
D/Audio ( 262): #09 pc 00034afb /system/lib/libaudioflinger.so
D/Audio ( 262): #10 pc 000104d5 /system/lib/libutils.so
D/Audio ( 262): #11 pc 00010045 /system/lib/libutils.so
D/Audio ( 262): #12 pc 00016afb /system/lib/libc.so
D/Audio ( 262): #13 pc 00014b2b /system/lib/libc.so
脚本源码如下:
#!/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])