事情的起因是這樣的,之前同事的代碼有一個內存池出現了沒有回收的情況。也就是是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 ??:?
大致滿足自己的需求效果,函數名稱還需要修飾一下!