如何记录程序调用栈

先看效果

[root@node223 test]# ls
a.out  backtrace.c  core.2021-9-26_11_13_26
[root@node223 test]# cat core.2021-9-26_11_13_26
Dump Time: 2021-9-26 11:13:26
Curr thread: 2905941824, Catch signal:11
backtrace rank = 8
./a.out(server_backtrace+0x29c) [0x400f39]
./a.out(funcd+0xe) [0x400c6b]
./a.out(funcc+0xe) [0x400c7b]
./a.out(funcb+0xe) [0x400c8b]
./a.out(funca+0xe) [0x400c9b]
./a.out(main+0xe) [0x40107c]
/lib64/libc.so.6(__libc_start_main+0xf5) [0x7fe8acb77625]
./a.out() [0x400b99]
[root@node223 test]# addr2line -e a.out 0x400c6b
/liuye/test/backtrace.c:15
[root@node223 test]# addr2line -e a.out 0x400c7b
/liuye/test/backtrace.c:24
[root@node223 test]# addr2line -e a.out 0x400c8b
/liuye/test/backtrace.c:33
[root@node223 test]# addr2line -e a.out 0x400c9b
/liuye/test/backtrace.c:42
[root@node223 test]# addr2line -e a.out 0x40107c
/liuye/test/backtrace.c:113
[root@node223 test]#
可用脚本翻译
cat core.1085720-11-2021-9-26_14_24_18 | cut -d"[" -f2 | tr -d "]" | addr2line -e a.out
cut主要参数
-b :以字节为单位进行分割。这些字节位置将忽略多字节字符边界,除非也指定了 -n 标志。
-c :以字符为单位进行分割。
-d :自定义分隔符,默认为制表符。
-f  :与-d一起使用,指定显示哪个区域。
-n :取消分割多字节字符。仅和 -b 标志一起使用。如果字符的最后一个字节落在由 -b 标志的 List 参数指示的<br />范围之内,该字符将被写出;否则,该字符将被排除。

Linux tr 命令用于转换或删除文件中的格式。
  • -c, 补:反设定编码。
  • -d, --delete:删除字符字符
  • -s, --squeeze-repeats:连续重复的字符成指定的字符字符
  • -t, --truncate-set1:缩小SET1指定范围,使之与SET2设定长度相等
  • --help:显示程序使用信息
  • --version:显示程序制作的版本信息

[root@node223 test]# cat core.1085720-11-2021-9-26_14_24_18 | cut -d"[" -f2 | tr -d "]" | addr2line -e a.out
??:0
??:0
??:0
??:0
/xxx/test/backtrace.c:75
/xxx/test/backtrace.c:12
/xxx/test/backtrace.c:16
/xxx/test/backtrace.c:20
/xxx/test/backtrace.c:24
/xxx/test/backtrace.c:98
??:0
??:?



贴源码:

1 #include <stdio.h>
2 #include <execinfo.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <pthread.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 void server_backtrace(int sig);
9 void funcd()
10 {
11 server_backtrace(11);
12 }
13 void funcc()
14 {
15 funcd();
16 }
17 void funcb()
18 {
19 funcc();
20 }
21 void funca()
22 {
23 funcb();
24 }
25 void server_backtrace(int sig)
26 {
27 //打开文件
28 time_t tSetTime;
29 time(&tSetTime);
30 struct tm* ptm = localtime(&tSetTime);
31 char fname[256] = {0};
32 sprintf(fname, "core.%d-%d-%d-%d-%d_%d_%d_%d",
33 getpid(),sig,
34 ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday,
35 ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
36 FILE* f = fopen(fname, "a");
37 if (f == NULL){
38 return;
39 }
40 int fd = fileno(f);
41
42 //锁定文件
43 struct flock fl;
44 fl.l_type = F_WRLCK;
45 fl.l_start = 0;
46 fl.l_whence = SEEK_SET;

47 fl.l_len = 0;
48 fl.l_pid = getpid();
49 fcntl(fd, F_SETLKW, &fl);
50
51 //输出程序的绝对路径
52 char buffer[4096];
53 memset(buffer, 0, sizeof(buffer));
54 int count = readlink("/proc/self/exe", buffer, sizeof(buffer));
55 if(count > 0){
56 buffer[count] = ' ';
57 buffer[count + 1] = 0;
58 fwrite(buffer, 1, count+1, f);
59 }
60
61 //输出信息的时间
62 memset(buffer, 0, sizeof(buffer));
63 sprintf(buffer, "Dump Time: %d-%d-%d %d:%d:%d ",
64 ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday,
65 ptm->tm_hour, ptm->tm_min, ptm->tm_sec);
66 fwrite(buffer, 1, strlen(buffer), f);
67
68 //线程和信号
69 sprintf(buffer, "Curr thread: %u, Catch signal:%d ",
70 (int)pthread_self(), sig);
71 fwrite(buffer, 1, strlen(buffer), f);
72
73 //堆栈
74 void* DumpArray[256];
75 int nSize = backtrace(DumpArray, 256);
76 sprintf(buffer, "backtrace rank = %d ", nSize);
77 fwrite(buffer, 1, strlen(buffer), f);
78 int i;
79 if (nSize > 0){
80 char** symbols = backtrace_symbols(DumpArray, nSize);
81 if (symbols != NULL){
82 for (i=0; i<nSize; i++){
83 fwrite(symbols[i], 1, strlen(symbols[i]), f);
84 fwrite(" ", 1, 1, f);
85 }
86 free(symbols);
87 }
88 }
89
90 //文件解锁后关闭
91 fl.l_type = F_UNLCK;

92 fcntl(fd, F_SETLK, &fl);
93 fclose(f);
94 }
95 int main()
96 {
97 funca();
98 return 0;
99 }
100

编译:gcc -g -rdynamic -Wall  backtrace.c -pthread -Werror

  -rdynamic:选项 -rdynamic 用来通知链接器将所有符号添加到动态符号表中

有时候,不小心知道了一些事,才发现自己所在乎的事是那么可笑。
原文地址:https://www.cnblogs.com/axjlxy/p/15338075.html