软件调试技术(2)-- coredump如何实现

6350阅读 0评论2015-12-29 alloysystem
分类:C/C++

最近在工作中了解到应用软件在崩溃的时候可以设置生成转存文件,这个功能就是coredump。不论是应用程序崩溃还是kernel崩溃转存功能都是很实用技术。本文是想从实现原理角度来理解这两项技术实现方法。首先分析一下应用程序coredump的实现方法。

什么条件触发转存
这个转存发生在应用程序收到致命的signal,在处理这个signal的时候会执行应用程序的转存,并在之后退出执行。有一点必须注意,如果用户给这些致命signal安装了自定义的handler,转存是不会发生的。OK,那么这里总结出两点:
1,致命signal会出发转存发生,这些signal包括如下内容。

点击(此处)折叠或打开

  1. 398 #define SIG_KERNEL_COREDUMP_MASK(\
  2. 399 rt_sigmask(SIGQUIT)|rt_sigmask(SIGILL)|\
  3. 400 rt_sigmask(SIGTRAP)|rt_sigmask(SIGABRT)|\
  4. 401 rt_sigmask(SIGFPE)|rt_sigmask(SIGSEGV)|\
  5. 402 rt_sigmask(SIGBUS)|rt_sigmask(SIGSYS)|\
  6. 403 rt_sigmask(SIGXCPU)|rt_sigmask(SIGXFSZ)|\
  7. 404 SIGEMT_MASK
2,如果上面的致命signal被用户安装了自定义的处理函数,coredump也不会发生。

点击(此处)折叠或打开

  1. 2264if(ka->sa.sa_handler!=SIG_DFL){
  2. 2265/*Run the handler.*/
  3. 2266 ksig->ka=*ka;
  4. 2267
  5. 2268if(ka->sa.sa_flags&SA_ONESHOT)
  6. 2269 ka->sa.sa_handler=SIG_DFL;
  7. 2270
  8. 2271 break;/*will return non-zero"signr"value*/
  9. 2272}
在kernel查找需要处理的signal的时候,首先会判断这个signal是不是默认处理方式,如果是默认处理才会继续执行到coredump分支,否则跳出查找环节,执行用户自定义处理方式。

触发coredump执行的分支

点击(此处)折叠或打开

  1. 2335if(sig_kernel_coredump(signr)){
  2. 2336if(print_fatal_signals)
  3. 2337 print_fatal_signal(ksig->info.si_signo);
  4. 2338 proc_coredump_connector(current);
  5. 2339/*
  6. 2340*Ifit was abletodump core,this kills all
  7. 2341*other threadsinthe groupandsynchronizes with
  8. 2342*their demise.Ifwe lost the race with another
  9. 2343*thread getting here,itsetgroup_exit_code
  10. 2344*firstandour do_group_exitcallbelow will use
  11. 2345*that valueandignore the one we pass it.
  12. 2346*/
  13. 2347 do_coredump(&ksig->info);
  14. 2348}


将致命signal打印到控制台的Tips
如果你开启如下proc目录下的option,将开启打印致命signal到控制台的功能。
/proc/sys/kernel/print-fatal-signals

coredump主要步骤
其实将这个应用程序都写入到磁盘的过程肯定是非常负责的。这里我不会扣没一个细节,有一些原理性的细节可能推到后面的部分再详细学习和分析。但是,这里我们需要了解如下几个关于coredump的关键处理流程。
1, coredump之间,首先要检查线程组中所有线程是不是已经都进入到了休眠状态。我们要coredump必须保证我们在拷贝这个进程的内存空间的时候进程中的某些线程还在运行并改写内存空间。
2, 对于直接将进程内存转存到文件的情况,没有什么特别之处,就是打开一个文件,并开始写入。
3,对于制定了用户空间脚本,进行一些压缩操作的情况,这里会启动一个用户态进程,并在kernel和用户态进程之间建立一个管道,
kernel=========读出===========用户态进程(压缩)-----------写入-------->文件

对于第一步处理,kernel想进程组中所有的线程发送kill signal信号,然后等待所有进程都不处在运行状态。
对于第二步,处理相对简单,只是之后如果有兴趣还可深入研究一下coredump文件的格式到底是什么样子的。
第三步,相对比较复杂,比较巧妙的地方就是在kernel中建立了一条管道,kernel处理signal部分将coredump写入管道,而新建的进程中管道的另外一端被设置到了标准输入上面。

对于压缩进程转存的方法
我们可以兴建一个shell执行文件
#!/bin/sh
exec gzip - > /root/$1.core.$2.gz
然后在/proc/sys/kernel/core_pattern 中写入
|/usr/sbin/core_helper %e %p  

上一篇:软件调试技术(1)
下一篇:计算机系统中各种延迟的比较