关闭

Linux下追踪函数调用,打印栈帧

发表于:2015-11-16 10:36

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:D.Ritchie    来源:51Testing软件测试网采编

  事情的起因是这样的,之前同事的代码有一个内存池出现了没有回收的情况。也就是是Pop出来的对象没有Push回去,情况很难复现,所以在Pop里的打印日志,跟踪是谁调用了它,我想在GDB调试里可以追踪调用的栈帧,那也一定有方法实现。首先上网搜索了一下,并没有结果!还好代码量不是很多,只能用最笨的方法,在每个调用Pop的地方,传参,把调用的文件,行号作为字符串传进去,在日志里打印!忙活完了,总感觉一定是有方法可以实现查看调用栈帧的,于是在QQ群里的问了下,果然有这方面经验的同学给出了答案!
  主要是通过backtrace返回调用的栈帧,然后通过backtrace_symbols把地址转换为字符串。最后,在Linux下有个工具addr2line可以将地址转换为文件名和行号!通过管道调用addr2line,最后打印调用栈帧。
1 #include <execinfo.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 /*
6  * 打印栈帧
7  *
8  * 通过backtrace,backtrace_symbols获取栈帧信息,然后建立管道,通过addr2line解析
9  *
10  */
11
12 int32_t myexec(const char *cmd)
13 {
14     FILE *pp = popen(cmd, "r"); //建立管道
15     if (!pp)
16     {
17         return -1;
18     }
19     char tmp[1024];
while (fgets(tmp, sizeof(tmp), pp) != NULL)
22     {
23         if (tmp[strlen(tmp) - 1] == '\n')
24         {
25             tmp[strlen(tmp) - 1] = '\0'; //去除换行符
26         }
27
28         printf("%-30s",tmp);
29     }
30     printf("\n");
31     pclose(pp); //关闭管道
32     return 0;
33 }
34
35 void parseName(char * str,char *exeName,char *addr)
36 {
37     char *strTemp = str;
38     char * addrTemp;
39     while (*strTemp != NULL)
40     {
41         if (*strTemp == '(')
42             memcpy(exeName, str, strTemp - str);
43
44         if (*strTemp == '[')
45             addrTemp = strTemp;
46
47         if (*strTemp == ']')
48             memcpy(addr, str + (addrTemp - str) + 1, strTemp - addrTemp - 1);
49         strTemp++;
50     }
51 }
52
53 void print_trace(void)
54 {
55     void *array[10];
56     size_t size;
57     char **strings;
58
59     size = backtrace(array,10);
60     strings = backtrace_symbols(array,size);
61
62     printf("Obtained %zd stack frames.\n",size);
63     char cmd[500] = {0};
64     char exeName[100] = {0};
65     char addr[100] = {0};
66     for(size_t i = 0;i < size;i++)
67     {
68       memset(cmd,0,sizeof(cmd));
69       memset(exeName,0,sizeof(exeName));
70       memset(addr,0,sizeof(addr));
71
72       parseName(strings[i],exeName,addr);
73       printf("%-15s",addr);
74       sprintf(cmd,"addr2line -f -e %s %s",exeName,addr);
75       myexec(cmd);
76     }
78 }
79
80 void dummp_function(void)
81 {
82     print_trace();
83 }
84
85 int main(int argc,char *argv[])
86 {
87     dummp_function();
88     return 0;
89 }
  编译:
  gcc -Wall -g backtrace.cpp -o bt
  执行:
  ./bt
  效果:
  1 Obtained 5 stack frames.
  2 0x4009e6       _Z11print_tracev              /home/dyf/Project/Tool/backtrace.cpp:59
  3 0x400b71       _Z14dummp_functionv           /home/dyf/Project/Tool/backtrace.cpp:83
  4 0x400b87       main                          /home/dyf/Project/Tool/backtrace.cpp:88
  5 0x7fa51f761af5 ??                            ??:0
  6 0x400769       _start                        ??:?
  大致满足自己的需求效果,函数名称还需要修饰一下!
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号