问题背景:
在linux上使用了nginx+fcgi的后端框架,cgi会偶发性地产生段错误Segfault Fault,想用gdb来定位出错原因。
1、尝试使用gdb coredump
但是cgi出现问题时,一直无法产生coredump,做了如下几个尝试:
- ulimit -c unlimited # 使能coredump生成
- sysctl -w fs.suid_dumpable=1 # 使能非root进程的coredump生成?因为此处我用的是www-data权限,非root。
- 保证进程对存放 core 文件的目录拥有写入权限。 nginx: master process是root权限,nginx: worker process是www-data权限,fcgiwrap也是root权限。
- 在配置文件 conf/nginx.conf 中增加两行配置。(但这个应该是针对nginx崩溃的情况,和cgi貌似没什么关系)
worker_rlimit_core 500m; //core文件大小限制
working_directory core_files; //core文件目录
以上尝试经验证全都无效,当www-data用户调用cgi程序产生错误时,就是不生成coredump。
2、gdb 直接运行程序
无效。cgi程序的参数无法直接从命令行输入,而是在代码中调用了cgi相关的库,去读取http request的相关信息。直接用gdb 直接运行程序,是无法复现出错现场的。
3、gdb attach
因为nginx+fcgi的后端框架会产生三个进程,分别为nginx的master process和worker process,以及cgi的fcgiwrap。
这里对框架不做过多介绍,只说明下调用流程是worker process → fcgiwrap → cgi程序。
因此gdb attach进程时,要attach的是fcgiwrap进程。
但这里又有一个新的问题,cgi程序执行时间通常很短,不加sleep的话,根本无法获取到进程的pid。而加了sleep,往往错误也不会产生了(因为错误可能和执行时序有关),且这个错误不是必现的,attach后有99%的概率是正常的。
这个方案一开始走不通被废弃了,但后来用下面第四种方案解决后,发现其实是可以sleep的,但不能在cgi中sleep,而是要在kernel中sleep。这样只有发生了段错误才会sleep,避免了以上问题。
这样sleep之后,即可gdb attach,查看各个线程的现场,从而定位出错原因。
4、反汇编debug
这种方法是向大神请教得到的。其实并没有用到gdb,但其中核心思想和gdb应该是一致的。
这里简要描述下,具体步骤可以参考下一篇文章。
一、修改系统内核,在出错处截获信号进入循环。
二、复现问题,获取指针地址A和进程号B。
三、根据B进程的smaps内存映射表,由A所在地址区间得到出错的库,记录库的起始地址C。
四、A-C=D,再对库进行反汇编,得到偏移量D所属的函数。
五、后面就是到代码中去定位这个函数了。