如何优雅的写C++代码 Obotcha介绍(StackTrace类)

    编写Java代码的时候可以通过打印Throwable来看函数的调用关系。但是在C++里面没有相关的接口来实现。所以我们只用通过C接口backtrace/backtrace_symbols来实现相关功能。和网上的大多数介绍一下,如果需要backtrace_symbols解析出相关trace,需要添加编译选项:-rdynamic。在Obotcha中,有一个单独的Exception类的printStack就有这个功能,代码如下:

void _Exception::printStack() {
    printf("-----------------[maps dump start]--------------- \n");
    char *path = "/proc/self/maps";
    int fd = open(path,O_RDONLY);
    if(fd >=0) {
        char *buff[1024*256];
        read(fd,buff,1024*256);
        close(fd);
        printf("%s \n",buff);
    }
    printf("-----------------[maps dump end]---------------- \n");
    printf("\n");
    printf("\n");

    printf("-----------------[stack dump start]-------------- \n");
    int size = 16;
    void * array[16];
    int stack_num = backtrace(array, size);
    char ** stacktrace = backtrace_symbols(array, stack_num);
    for (int i = 0; i < stack_num; ++i) {
        printf("%s\n", stacktrace[i]);
    }
    printf("-----------------[stack dump end]--------------- \n");
    free(stacktrace);
}

我们来看一下调用后出现的打印:

-----------------[maps dump start]--------------- 
555a4f0cc000-555a4f0cf000 r-xp 00000000 08:05 10621902                   /home/sunliwang/mysource/Obotcha/test/testFileNotFoundException/mytest
555a4f2ce000-555a4f2cf000 r--p 00002000 08:05 10621902                   /home/sunliwang/mysource/Obotcha/test/testFileNotFoundException/mytest
555a4f2cf000-555a4f2d0000 rw-p 00003000 08:05 10621902                   /home/sunliwang/mysource/Obotcha/test/testFileNotFoundException/mytest
555a509ad000-555a509ce000 rw-p 00000000 00:00 0                          [heap]
7f5f76cc1000-7f5f76e5e000 r-xp 00000000 08:05 5772002                    /lib/x86_64-linux-gnu/libm-2.27.so
7f5f76e5e000-7f5f7705d000 ---p 0019d000 08:05 5772002                    /lib/x86_64-linux-gnu/libm-2.27.so
7f5f7705d000-7f5f7705e000 r--p 0019c000 08:05 5772002                    /lib/x86_64-linux-gnu/libm-2.27.so
7f5f7705e000-7f5f7705f000 rw-p 0019d000 08:05 5772002                    /lib/x86_64-linux-gnu/libm-2.27.so
7f5f7705f000-7f5f77062000 r-xp 00000000 08:05 5771962                    /lib/x86_64-linux-gnu/libdl-2.27.so
7f5f77062000-7f5f77261000 ---p 00003000 08:05 5771962                    /lib/x86_64-linux-gnu/libdl-2.27.so
7f5f77261000-7f5f77262000 r--p 00002000 08:05 5771962                    /lib/x86_64-linux-gnu/libdl-2.27.so
7f5f77262000-7f5f77263000 rw-p 00003000 08:05 5771962                    /lib/x86_64-linux-gnu/libdl-2.27.so
7f5f77263000-7f5f7727d000 r-xp 00000000 08:05 5772072                    /lib/x86_64-linux-gnu/libpthread-2.27.so
7f5f7727d000-7f5f7747c000 ---p 0001a000 08:05 5772072                    /lib/x86_64-linux-gnu/libpthread-2.27.so
7f5f7747c000-7f5f7747d000 r--p 00019000 08:05 5772072                    /lib/x86_64-linux-gnu/libpthread-2.27.so
7f5f7747d000-7f5f7747e000 rw-p 0001a000 08:05 5772072                    /lib/x86_64-linux-gnu/libpthread-2.27.so
7f5f7747e000-7f5f77482000 rw-p 00000000 00:00 0 
7f5f77482000-7f5f77489000 r-xp 00000000 08:05 5772080                    /lib/x86_64-linux-gnu/librt-2.27.so
7f5f77489000-7f5f77688000 ---p 00007000 08:05 5772080                    /lib/x86_64-linux-gnu/librt-2.27.so
7f5f77688000-7f5f77689000 r--p 00006000 08:05 5772080                    /lib/x86_64-linux-gnu/librt-2.27.so
7f5f77689000-7f5f7768a000 rw-p 00007000 08:05 5772080                    /lib/x86_64-linux-gnu/librt-2.27.so
7f5f7768a000-7f5f77871000 r-xp 00000000 08:05 5771939                    /lib/x86_64-linux-gnu/libc-2.27.so
7f5f77871000-7f5f77a71000 ---p 001e7000 08:05 5771939                    /lib/x86_64-linux-gnu/libc-2.27.so
7f5f77a71000-7f5f77a75000 r--p 001e7000 08:05 5771939                    /lib/x86_64-linux-gnu/libc-2.27.so
7f5f77a75000-7f5f77a77000 rw-p 001eb000 08:05 5771939                    /lib/x86_64-linux-gnu/libc-2.27.so
7f5f77a77000-7f5f77a7b000 rw-p 00000000 00:00 0 
7f5f77a7b000-7f5f77a92000 r-xp 00000000 08:05 5767842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f77a92000-7f5f77c91000 ---p 00017000 08:05 5767842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f77c91000-7f5f77c92000 r--p 00016000 08:05 5767842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f77c92000-7f5f77c93000 rw-p 00017000 08:05 5767842                    /lib/x86_64-linux-gnu/libgcc_s.so.1
7f5f77c93000-7f5f77e0c000 r-xp 00000000 08:05 13369538                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f5f77e0c000-7f5f7800c000 ---p 00179000 08:05 13369538                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f5f7800c000-7f5f78016000 r--p 00179000 08:05 13369538                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f5f78016000-7f5f78018000 rw-p 00183000 08:05 13369538                   /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.25
7f5f78018000-7f5f7801c000 rw-p 00000000 00:00 0 
7f5f7801c000-7f5f787ca000 r-xp 00000000 08:05 9721046                    /home/sunliwang/mysource/Obotcha/out/lib/libobotcha.so
7f5f787ca000-7f5f789ca000 ---p 007ae000 08:05 9721046                    /home/sunliwang/mysource/Obotcha/out/lib/libobotcha.so
 
-----------------[maps dump end]---------------- 


-----------------[stack dump start]-------------- 
../../out/lib/libobotcha.so(_ZN7obotcha10_Exception10printStackEv+0xf4) [0x7f5f7842b1f8]
./mytest(main+0x146) [0x555a4f0cd4d0]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f5f776abb97]
./mytest(+0x12aa) [0x555a4f0cd2aa]
-----------------[stack dump end]---------------

呵呵和java的trace比起来,上面的这个可读性很差,但是能有trace和map就很不错了,我们就不要挑剔了。重点来了,接下来,我们通过2中方法来追一下函数调用的栈哈。

方法1(通过函数的偏移0xf4来看函数呢运行到哪个位置):

步骤1:

使用c++filt来解析出_ZN7obotcha10_Exception10printStackEv具体是哪个函数。

命令:

 c++filt _ZN7obotcha10_Exception10printStackEv

结果:

obotcha::_Exception::printStack()

 

步骤2:

解析libobotcha.so中的printStack的地址,

命令:

readelf -a ../../out/lib/libobotcha.so |grep -i _ZN7obotcha10_Exception10

感觉是elfread好像没有把函数名称全打印出来所以只能通过前面的字符串匹配,结果如下:

  5993: 000000000040f0f2    17 FUNC    GLOBAL DEFAULT   12 _ZN7obotcha10_Exception10
  6193: 000000000040f0c4    45 FUNC    GLOBAL DEFAULT   12 _ZN7obotcha10_Exception10
 14202: 000000000040f104   400 FUNC    GLOBAL DEFAULT   12 _ZN7obotcha10_Exception10
 14942: 000000000040f104   400 FUNC    GLOBAL DEFAULT   12 _ZN7obotcha10_Exception10
 20681: 000000000040f0f2    17 FUNC    GLOBAL DEFAULT   12 _ZN7obotcha10_Exception10
 23242: 000000000040f0c4    45 FUNC    GLOBAL DEFAULT   12 _ZN7obotcha10_Exception10

这样的话,会出现6个结果,实际上,这6个就是Exception的6个接口。这样,没办法,我们只好用addr2line来一个个看哪个是printStack函数。

命令:

addr2line -e ../../out/lib/libobotcha.so 40f0f2

结果:

/home/dji/mysource/Obotcha/lang/Exception.cpp:36

查看代码,36行不是printStack.继续找,我们会找到4f104这个地址是对应的printStack函数。

步骤3:

printStack的起始40f104找到了,然后再加上偏移0xf4,结果:40f1f8

再用addr2line看一下是哪一行:

/home/dji/mysource/Obotcha/lang/Exception.cpp:57

这个就是抓取backtrace的点。

 

方法2(通过读取smap中so库加载的起始地址算出偏移)

步骤1:

查找libobotcha.so 加载的起始地址:

7f5f7801c000-7f5f787ca000 r-xp 00000000 08:05 9721046                    /home/sunliwang/mysource/Obotcha/out/lib/libobotcha.so
7f5f787ca000-7f5f789ca000 ---p 007ae000 08:05 9721046                    /home/sunliwang/mysource/Obotcha/out/lib/libobotcha.so
所以起始地址是7f5f7801c000

步骤2:

../../out/lib/libobotcha.so(_ZN7obotcha10_Exception10printStackEv+0xf4) [0x7f5f7842b1f8]

中的地址减去步骤1中的起始地址,就是函数指针的偏移了。

0x7f5f7842b1f8 - 7f5f7801c000 = 40f1f8

这个地址和方法1计算出来是一样的。接下来用addr2line就可以计算了。

 

总结:

方法2比方法1简单了很多,但是方法1可以更加清晰的看到so库中函数/全局变量的地址,在有些稳定性问题(例如指向全局数据的指针越界)上比较有用。具体的案例下次有机会可以show给大家看一下。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值