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

事情的起因是这样的,之前同事的代码有一个内存池出现了没有回收的情况。也就是是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                        ??:? 

大致满足自己的需求效果,函数名称还需要修饰一下!

时间: 2024-11-04 12:08:41

Linux下追踪函数调用,打印栈帧的相关文章

从汇编角度来理解linux下多层函数调用堆栈执行状态

注:在linux下开发经常使用的辅助小工具: readelf .hexdump.od.objdump.nm.telnet.nc 等,详细能够man一下. 我们用以下的C代码来研究函数调用的过程. C++ Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int bar(int c, int d) { int e = c + d; return e; } int foo(int a, int b) { return bar(a, b); } int main(

C语言函数调用及栈帧结构

source:http://blog.csdn.net/qq_29403077/article/details/53205010 一.地址空间与物理内存 (1)地址空间与物理内存是两个完全不同的概念,真正的代码及数据都存在物理内存中. 物理储存器是指实际存在的具体储存器芯片,CPU在操纵物理储存器的时候都把他们当做内存来对待,把他们看成由若干个储存单元组成的逻辑储存器,这个逻辑储存器就是我们所说的地址空间. 地址空间大小与逻辑储存器大小不一定相等. (2)进程的地址空间分布 进程的地址空间包括:

从汇编角度来理解linux下多层函数调用堆栈运行状态

我们用下面的C代码来研究函数调用的过程. C++ Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16   int bar(int c, int d) { int e = c + d; return e; } int foo(int a, int b) { return bar(a, b); } int main(void) { foo(2, 3); return 0; } 如果在编译时加上-g选项,那么用objdump反汇编时可以把C代码和汇编代码穿插起来显示

函数调用过程栈帧变化详解

http://www.cnblogs.com/fxplove/articles/2574451.html 数调用另一个词语表示叫作 过程.一个过程调用包括将数据和控制从代码的一部分传递到另一部分.另外,它还必须在进入时为过程的局部变量分配空间,并在推出时释放这些空间.而数据传递,局部变量的分配和释放通过操纵程序栈来实现.在了解本文章之前,您需要先对程序的进程空间有所了解,即对进程如何使用内存?如果你知道这些,下面的内容将是很easy的事情了.为了您的回顾还是将简单的分布图贴出来,便于您的回顾.

mac os x打印栈帧

// stacktrace.c #include <stdio.h> #include <dlfcn.h> void printframeinfo(unsigned int level, void *fp, void *ra) { int ret; Dl_info info; // Find the image containing the given address ret = dladdr(ra, &info); printf("#%u %s%s in %s,

Linux下调试程序方法

您可以用各种方法来监控运行着的用户空间程序:可以为其运行调试器并单步调试该程序,添加打印语句,或者添加工具来分析程序.本文描述了几种可以用来调试在 Linux 上运行的程序的方法.我们将回顾四种调试问题的情况,这些问题包括段错误,内存溢出和泄漏,还有挂起.本文讨论了四种调试 Linux 程序的情况.在第 1 种情况中,我们使用了两个有内存分配问题的样本程序,使用 MEMWATCH 和 Yet Another Malloc Debugger(YAMD)工具来调试它们.在第 2 种情况中,我们使用了

linux下gcc编译多个源文件、gdb的使用方法(转)

原文 http://www.cnblogs.com/jiu0821/p/4483804.html 一. gcc常用编译命令选项 假设源程序文件名为test.c. 1. 无选项编译链接 用法:#gcc test.c 作用:将test.c预处理.汇编.编译并链接形成可执行文件.这里未指定输出文件,默认输出为a.out. 2. 选项 -o 用法:#gcc test.c -o test 作用:将test.c预处理.汇编.编译并链接形成可执行文件test.-o选项用来指定输出文件的文件名. 3. 选项 -

linux下gcc编译多个源文件、gdb的使用方法

一. gcc常用编译命令选项 假设源程序文件名为test.c. 1. 无选项编译链接 用法:#gcc test.c 作用:将test.c预处理.汇编.编译并链接形成可执行文件.这里未指定输出文件,默认输出为a.out. 2. 选项 -o 用法:#gcc test.c -o test 作用:将test.c预处理.汇编.编译并链接形成可执行文件test.-o选项用来指定输出文件的文件名. 3. 选项 -E 用法:#gcc -E test.c -o test.i 作用:将test.c预处理输出test

.NET作品集:linux下的.net mvc cms

cms程序架构 本程序是主要是用于企业网站开发的,也可以做博客程序,程序是从之前上一篇的.net 博客程序改进过来的,主要技术由webform转成.net mvc了,由于是很早之前的项目,12年还是mvc3版本,当然还是跑在linux下的. 技术栈 .net framework 4.0 sqlite 数据库 mono linux 运行环境以及mono下的sqlite库 razor 模板引擎 dapper 轻量级orm框架 vs2017 社区版本 这次的开发工具比较新了吧,上次用vs2010发的,