Linux 程序崩溃时如何产生 Core Dump 文件?

10 篇文章 1 订阅

原理:

1 、当程序出现异常时通常伴随着会收到一个由内核发过来的异常信号 (查看 kill -l),

  • 如当对内存出现非法访问时将收到段错误信号SIGSEGV,然后才退出。
  • 利用这一点,当我们在收到异常信号后将程序的调用栈进行输出

2、捕获系统异常信号输出程序的调用栈: 头文件<execinfo.h>提供了三个相关的函数

  • int backtrace(void **buffer, int size); // 用于获取堆栈的地址信息

  • char **backtrace_symbols(void *const *buffer, int size); //把堆栈地址翻译成我们易识别的字符串,其被存放在buffer中

  • void backtrace_symbols_fd(void *const *buffer, int size, int fd); //把字符串堆栈信息输出到文件中

  • 注意: GCC编译链接的话,加上“-rdynamic”参数,这个参数的意思是告诉ELF连接器添加“-export-dynamic”标记,这样所有的符号信息symbols就会添加到动态符号表中,以便查看完整的堆栈信息。

  • static函数不会导出符号信息symbols,在backtrace中无效。

  • 某些编译器的优化选项对获取正确的函数调用堆栈有干扰,内联函数没有堆栈框架,删除框架指针也会导致无法正确解析堆栈内容。

示例:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <execinfo.h>  //backtrace(), backtrace_symbols()

// 几个写文件函数
FILE *g_dump_file = NULL;
void dump_open()
{
    char chPath[] = "dumpfile.log";    
    if (g_dump_file)
    {
        fclose(g_dump_file);
        g_dump_file = NULL;
    }

    g_dump_file = fopen(chPath,"wb");   
}

void dump_write(char *p)
{
    if ((!g_dump_file) || (!p))    
        return;    
    
    fwrite(p,1,strlen(p),g_dump_file);
}

void dump_close()
{
    if (!g_dump_file)    
        return;    
    
    fclose(g_dump_file);
    g_dump_file = NULL;
}

// 信号处理二
void core_dump(int sig_no)
{  
	#define BACKTRACE_SIZE 20
    void *array[BACKTRACE_SIZE] = {0};	
	
	//获取程序的调用栈
    size_t size = backtrace (array, BACKTRACE_SIZE);  // [a]
    char **__backtrace= backtrace_symbols (array, size); // [b] 此处有 molloc

    //下面 %zd 指 size_t 参考 http://www.cplusplus.com/reference/cstdio/printf/
    char ch[128];
    sprintf_s(ch,sizeof(ch),"stack size [%zd]\r\n", size);
    dump_open();    // [1]
    dump_write(ch); // [2]
    printf(ch); // stdout
    
    for (size_t i = 0; i < size; i++) 
    {
        dump_write(__backtrace[i]); // [2]
        dump_write("\r\n");     // [2]		
        printf(__backtrace[i]); // stdout
        printf("\r\n"); 
    }
    
    dump_close(); // [3]
    free(__backtrace);// [b]
    exit(0); 
}

// 信号处理函数一
void sigroutine(int sig_no) 
{
    switch (sig_no) { 
    case 1: 
        printf("Get a signal -- SIGHUP ");    // 挂起
		break; 
    case 2: 
        printf("Get a signal -- SIGINT ");    // 对应为ctrl + C键
		break; 
    case 3: 
        printf("Get a signal -- SIGQUIT ");
		break; 
    } 
}

// 注册信号处理函数
void InstallSign()
{
    signal(SIGHUP,  sigroutine);//控制进程终止
    signal(SIGINT,  sigroutine);//键盘中断
    signal(SIGQUIT, sigroutine);//退出
    
    signal(SIGILL,  core_dump); //非法指令
    signal(SIGSEGV, core_dump); //段错误
}

int main()
{	
	InstallSign(); //先注册信号处理函数
	
	// ...

	return 0;
}


/* 信号列表 kill -l
   1) SIGHUP	     2) SIGINT	     3) SIGQUIT	     4) SIGILL	     5) SIGTRAP
   6) SIGABRT	     7) SIGBUS	     8) SIGFPE	     9) SIGKILL	    10) SIGUSR1
  11) SIGSEGV	    12) SIGUSR2	    13) SIGPIPE	    14) SIGALRM	    15) SIGTERM
  16) SIGSTKFLT	    17) SIGCHLD	    18) SIGCONT	    19) SIGSTOP	    20) SIGTSTP
  21) SIGTTIN	    22) SIGTTOU	    23) SIGURG	    24) SIGXCPU	    25) SIGXFSZ
  26) SIGVTALRM	    27) SIGPROF	    28) SIGWINCH	29) SIGIO	    30) SIGPWR
  31) SIGSYS	    34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
  38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
  43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
  48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
  53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
  58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
  63) SIGRTMAX-1	64) SIGRTMAX

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值