linux 进程崩溃 堆栈,如何在程序崩溃时自动生成堆栈跟踪

linux

虽然在execinfo.h中使用backtrace()函数打印堆栈跟踪并在得到分段错误时优雅地退出已被建议,我没有看到任何必要的复杂之处,以确保所产生的回溯指向故障的实际位置(至少对于某些体系结构-x86&ARM)。

当您进入信号处理程序时,堆栈帧链中的前两个条目在信号处理程序中包含一个返回地址,在libc中包含一个在Sigaction()中的返回地址。在信号(故障位置)之前调用的最后一个函数的堆栈帧丢失。

电码#ifndef _GNU_SOURCE#define _GNU_SOURCE#endif#ifndef __USE_GNU#define __USE_GNU#endif#include #include

#include #include #include #include #include /*

This structure mirrors the one found in /usr/include/asm/ucontext.h */typedef struct _sig_ucontext {

unsigned long     uc_flags;

struct ucontext   *uc_link;

stack_t           uc_stack;

struct sigcontext uc_mcontext;

sigset_t          uc_sigmask;} sig_ucontext_t;void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext){

void *             array[50];

void *             caller_address;

char **            messages;

int                size, i;

sig_ucontext_t *   uc;

uc = (sig_ucontext_t *)ucontext;

/* Get the address at the time the signal was raised */#if defined(__i386__) // gcc specific

caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific#elif defined(__x86_64__) // gcc specific

caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific#else#error Unsupported architecture.

// TODO: Add support for other arch.#endif

fprintf(stderr, "signal %d (%s), address is %p from %p\n",

sig_num, strsignal(sig_num), info->si_addr,

(void *)caller_address);

size = backtrace(array, 50);

/* overwrite sigaction with caller's address */

array[1] = caller_address;

messages = backtrace_symbols(array, size);

/* skip first stack frame (points here) */

for (i = 1; i 

{

fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]);

}

free(messages);

exit(EXIT_FAILURE);}int crash(){

char * p = NULL;

*p = 0;

return 0;}int foo4(){

crash();

return 0;}int foo3(){

foo4();

return 0;}int foo2(){

foo3();

return 0;}int foo1(){

foo2();

return 0;}int main(int argc, char ** argv){

struct sigaction sigact;

sigact.sa_sigaction = crit_err_hdlr;

sigact.sa_flags = SA_RESTART | SA_SIGINFO;

if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0)

{

fprintf(stderr, "error setting signal handler for %d (%s)\n",

SIGSEGV, strsignal(SIGSEGV));

exit(EXIT_FAILURE);

}

foo1();

exit(EXIT_SUCCESS);}

输出量signal 11 (Segmentation fault), address is (nil) from 0x8c50[bt]: (1) ./test(crash+0x24) [0x8c50][bt]: (2) ./test(foo4+0x10) [0x8c70][bt]:

(3) ./test(foo3+0x10) [0x8c8c][bt]: (4) ./test(foo2+0x10) [0x8ca8][bt]: (5) ./test(foo1+0x10) [0x8cc4][bt]:

(6) ./test(main+0x74) [0x8d44][bt]: (7) /lib/libc.so.6(__libc_start_main+0xa8) [0x40032e44]

在信号处理程序中调用Backtrace()函数的所有危险仍然存在,不应忽视,但我发现这里描述的功能对于调试崩溃非常有帮助。

需要注意的是,我提供的示例是在Linuxforx86上开发/测试的。我也成功地在ARM上实现了这个uc_mcontext.arm_pc而不是uc_mcontext.eip.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值