函数调用栈分析

本文介绍了解析Android系统中C/C++代码产生的函数调用栈的方法,包括使用addr2line工具、ndk-stack工具及自定义脚本,旨在帮助开发者更清晰地理解程序执行流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

函数调用栈分析

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]) 








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值