优秀博客:
https://blog.csdn.net/skdkjzz/article/details/46046271
https://blog.csdn.net/littlefang/article/details/42295803
http://blog.chinaunix.net/uid-24774106-id-3457205.html
一.利用backtrace,栈信息找bug
一般察看函数运行时堆栈的方法是使用GDB(bt命令)调试工具。但是,有些时候为了分析程序的BUG(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的。
作用:用于程序异常退出时寻找错误原因。
功能:回溯堆栈,简单的说就是可以列出当前函数调用关系。
原理:
- 1. 通过对当前堆栈的分析,找到其上层函数在栈中的帧地址,再分析上层函数的堆栈,再找再上层的帧地址……一直找到最顶层为止,帧地址指的是一块:在栈上存放局部变量,上层返回地址,及寄存器值的空间。
- 2. 由于不同处理器堆栈方式不同,此功能的具体实现是编译器的内建函数
__buildin_frame_address
和
__buildin_return_address
中,它涉及工具glibc和gcc, 如果编译器不支持此函数,也可自己实现此函数,举例中有arm上的实现。
··
二. 相关函数接口简析
在glibc头文件”execinfo.h”中声明了三个函数用于获取当前线程的函数调用堆栈。
2.1 backtrace()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
说明:
1. 该函数用于获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针列表。
参数 size 用来指定buffer中可以保存多少个void* 元素。函数返回值是实际获取的指针个数,
最大不超过size大小。
2. 在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈框架有一个返回地址。
3. 某些编译器的优化选项会对获取正确的调用堆栈有干扰;
4. 内联函数没有堆栈框架;
5. 删除框架指针也会导致无法正确解析堆栈内容。
*/
int backtrace(void **buffer, int size)
2.2 backtrace_symbols ()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
说明:
1. backtrace_symbols将从backtrace函数获取的信息转化为一个字符串数组。
2. 参数buffer应该是从backtrace函数获取的指针数组,size是该数组中的元素个数(backtrace的返回值)。
3. 函数返回值是一个指向字符串数组的指针,它的大小同buffer相同。每个字符串包含了一个相对于
buffer中对应元素的可打印信息。它包括函数名、函数的偏移地址、实际的返回地址。
4. 目前,只有使用ELF二进制格式的程序才能获取函数名称和偏移地址。在其他系统,只有16进制的返回地址能被
获取。另外,你可能需要传递相应的符号给链接器,以能支持函数名功能。(比如,在使用GNU ld链接器的系统
中,器支持-rdynamic的话,建议将其加上!)
5. 该函数的返回值是通过malloc函数申请的空间,因此调用者必须使用free函数来释放指针。
6. 如果不能为字符串获取足够的空间函数的返回值将会为NULL。
*/
char ** backtrace_symbols (void *const *buffer, int size)
2.3 backtrace_symbols_fd()
1
2
3
4
5
6
7
/*
说明:
1. backtrace_symbols_fd与backtrace_symbols 函数具有相同的功能,不同的是它不会给调用者
返回字符串数组,而是将结果写入文件描述符为fd的文件中,每个函数对应一行.它不需要调用malloc函数,
因此适用于有可能调用该函数会失败的情况。
*/
void backtrace_symbols_fd(void *const *buffer, int size, int fd)
三. 实例
3.1 编译
- 如果是x86平台gcc编译:
gcc -g -rdynamic backtrace.c -o backtrace
- 如果是ARM平台arm-linux-gcc交叉编译,需要带如下编译参数:
-rdynamic
-funwind-tables
-ffunction-sections
否则可能backtrace返回值为0,得不到需要的信息。
3.2 实例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/* backtrace.c */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>
#include <time.h>
#include <stdbool.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <memory.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/mman.h>
#include <sys/shm.h>
#include <poll.h>
#include <sys/epoll.h>
#include <sys/select.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <malloc.h>
#include <pthread.h>
#include <sys/types.h>
#include <execinfo.h>
#define BACKTRACE_BUF_SIZE 50
void sig_children(int signal)
{
pid_t pid;
int stat;
while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) {
printf("children %d terminatedn", pid);
}
}
void SigSegv_handler(int signal)
{
int j, nptrs;
void *buffer[BACKTRACE_BUF_SIZE];
char **strings;
#if 1
nptrs = backtrace(buffer, BACKTRACE_BUF_SIZE);
#else
nptrs = backtrace_arm(buffer, BACKTRACE_BUF_SIZE);
#endif
printf("signal_test exit: backtrace() returned %d addresses, sig:%dn", nptrs, signal);
switch (signal) {
case SIGFPE: // 8:
printf("The signal type: Floating point exception!n");
break;
case SIGILL: // 4:
printf("The signal type: Illegal instruction!n");
break;
case SIGSEGV: // 11:
printf("The signal type: Segmentation fault!n");
break;
case SIGBUS: // 7:
printf("The signal type: Bus error!n");
break;
case SIGABRT: // 6:
printf("The signal type: Aborted!n");
break;
case SIGTRAP: // 5:
printf("The signal type: Trace/breakpoint trap!n");
break;
case SIGSYS: // 31:
printf("The signal type: Bad system call!n");
break;
case SIGTERM: // 15:
printf("The signal type: Terminated!n");
break;
case SIGINT: // 2:
printf("The signal type: Interrupt!n");
break;
case SIGQUIT: // 3:
printf("The signal type: Quit!n");
break;
case SIGKILL: // 9:
printf("The signal type: Killed!n");
break;
case SIGHUP: // 1:
printf("The signal type: Hangup!n");
break;
case SIGALRM: // 14:
printf("The signal type: Alarm clock!n");
break;
case SIGVTALRM: // 26:
printf("The signal type: Virtual timer expired!n");
break;
case SIGPROF: // 27:
printf("The signal type: Profiling timer expired!n");
break;
case SIGIO: // 29:
printf("The signal type: I/O possible!n");
break;
case SIGPIPE: // 13:
printf("The signal type: Broken pipe!n");
break;
case SIGXCPU: // 24:
printf("The signal type: CPU time limit exceeded!n");
break;
case SIGXFSZ: // 25:
printf("The signal type: File size limit exceeded!n");
break;
case SIGUSR1: // 10:
printf("The signal type: User defined signal 1!n");
break;
case SIGUSR2: // 12:
printf("The signal type: User defined signal 2!n");
break;
default:
printf("The signal type: Unkown signal:%d!n", signal);
break;
}
/* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)
would produce similar output to the following: */
strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL) {
printf("backtrace_symbols");
exit(EXIT_FAILURE);
}
for (j = 0; j < nptrs; j++) {
printf("%sn", strings[j]);
}
free(strings);
exit(-1);
}
void main(int argc, char *argv[])
{
signal(SIGCHLD, sig_children);
signal(SIGSEGV, SigSegv_handler);
signal(SIGILL, SigSegv_handler);
signal(SIGBUS, SigSegv_handler);
signal(SIGFPE, SigSegv_handler);
signal(SIGABRT, SigSegv_handler);
signal(SIGTRAP, SigSegv_handler);
signal(SIGSYS, SigSegv_handler);
signal(SIGTERM, SigSegv_handler);
signal(SIGINT, SigSegv_handler);
signal(SIGQUIT, SigSegv_handler);
signal(SIGKILL, SigSegv_handler);
signal(SIGHUP, SigSegv_handler);
signal(SIGALRM, SigSegv_handler);
signal(SIGVTALRM, SigSegv_handler);
signal(SIGPROF, SigSegv_handler);
signal(SIGIO, SigSegv_handler);
signal(SIGPIPE, SigSegv_handler);
signal(SIGXCPU, SigSegv_handler);
signal(SIGXFSZ, SigSegv_handler);
signal(SIGUSR1, SigSegv_handler);
signal(SIGUSR2, SigSegv_handler);
while(1)
sleep(10);
}
3.3 定位问题(c文件:backtrace.c ;编译结果:backtrace;报错行:0x4007e9)
1.通过objdump工具(注:只有加了编译选项的,汇编结果才能有对应的c语言。)
x86:
objdump
arm:
arm-poky-linux-gnueabi-objdump
例如:
objdump -d backtrace > backtrace.s
//没有对应的c语句
objdump -S backtrace | less
//可能有对应的c语句,取决于编译时添加的编译选项。
2.通过addr2line工具
addr2line 0x4007e9 -e backtrace -f
最后
以上就是可耐未来最近收集整理的关于应用程序调试-signal和backtrace一.利用backtrace,栈信息找bug二. 相关函数接口简析三. 实例的全部内容,更多相关应用程序调试-signal和backtrace一.利用backtrace,栈信息找bug二.内容请搜索靠谱客的其他文章。
发表评论 取消回复