应用程序未产生core原因定位

作者:夏志培
本文为原创文章,转载请注明作者及出处

背景:

c/c++应用程序进程由于各种原因会崩溃,人为上去的迅速干预处理也会造成一定的业务影响, 于是运维的同事将应用程序全部接入到supervisor,通过supervisor来防止应用进程宕掉短时间无人处理。经过一段时间后,supervisor带来一些收效,但同时引入一些问题。譬如应用程序崩溃后,在系统日志和内核的环形日志中留下一些报错信息(例如指令指针地址),虽说通过指令指针地址信息找到对应的出错行,但是由于该应用经过多次版本发布,即使通过指令指针地址找到了对应的代码行,也无法确定该出错的代码行就是当前代码仓库最新版本中的代码行。仍然不好定位bug, 形成较大的困扰。所以业务方仍然需要应用产生的core信息。于是介入参与,解决这个问题。(备注:环境信息:centos6.5 x64 ,supervisord-2.1-9)

过程和原因:

  1. 运维已经下线该Serve,保留了现场,于是登上目标服务器上查看,发现应用崩溃后在系统日志中仅存在少量的报错日志,(指令地址信息 ,文件的装载地址信息), 但是没有core文件。先检查core文件的名字生成规则有没有被修改。
\#sysctl -a |grep  core_pattern 

确认core的文件格式以core结尾。
然后检查系统参数core file size 是unlimited ,系统配置层未发现问题。

  1. 然后进行故障重现,在终端中手工启动该应用程序,崩溃后可以产生core文件。(向目标进程发SIGUSR2信号)

  2. 开始检查和对比 supervisor运行环境变量 和 终端中执行程序的环境变量 【可以借助gdb , 查看进程相关的内存】

  3. 对比环境后确实存在差异, supervisor接管后进程的 core file size 为0, 终端中执行的进程中的core file size 为ulimited, 其中0是不能产生core文件,ulimit是可以产生core文件。

  1. 为了所辖排查范围,用python写一个类似supervider的程序 和 产生core的C程序,并且放入到crontab中运行, 代码如下:

5.1 产生core的测试C代码: #cd /tmp/ && gcc test.c

 #include<stdio.h>
int main()
{
        char *str="this is a Segmentation fault";
        str[0]='f';
        return 0;
}

5.2 测试的python程序(类似supervisor作用)

 #!/usr/bin/env python
 #coding=utf8
 
from time import sleep
import os,signal
def child_handler(signum,stackframe):
    while 1:
        try:
             result = os.waitpid(-1,os.WNOHANG)
         except:
             break
             print "Reaped child process %d" % result[0]
        signal.signal(signal.SIGCHLD,child_handler)
 
signal.signal(signal.SIGCHLD,child_handler)
 
try:
    pid = os.fork()
    if pid == 0:
        os.execv("/tmp/a.out",[])
except OSError, e:
    pass
 
sleep(30)

  1. 经过测试,C程序可以产生core文件,此时原因就是supervidoer程序本身了。
    顺着/usr/bin/supervisord去查看源代码, 发现supervisord有一个set_rlimits函数,专门处理环境变量这一块的参数, 但supervisord的set_rlimits只会获取系统环境中两个变量:
    RLIMIT_NOFILE 最大打开文件句柄数
    RLIMIT_NOPROC 用户最大的进程数
    ,其他全部使用python.resource模块设置的默认值, 具体看https://pymotw.com/2/resource/,
    为了验证这一点, 查看了当前未被supervisord接管某个应用进程的 max locked memory 和 supervisor接管进程的max locked memory, 正如预期中的一样,发现明显的差异。supervisor中由于core file size 这个参数也是使用python.resource模块设置的默认值(0),所以造成应用进程无法产生core文件。

解决办法:

  1. 不重启应用进程的3种解决办法:
    1.1 借助gdb 直接修改ulimit的一些参数【不推荐】【内核>2.6】
    1.2. 直接修改/proc/xx/limits的参数,echo -n ‘Max open files=unlimited:unlimited’ > /proc/15533/limits 【内核>2.6】
    1.3. 借助prlimit工具 【内核>2.6.36】,prlimit –pid 25622 –nofile=10240:10240
  2. 根本解决办法, 修改supvisor模块中 options.py 文件第1293行下面加入以下代码
soft, hard = resource.getrlimit(resource.RLIMIT_CORE)
resource.setrlimit(resource.RLIMIT_CORE, (-1, hard))

示例:
#vim /usr/lib/python2.6/site-packages/supervisor/options.py

        if hasattr(resource, 'RLIMIT_NPROC'):
            limits.append(
                {
                'msg':('The minimum number of available processes required '
               'to run this program is %(min)s as per the "minprocs" '
               'command-line argument or config file setting. '
               'The current environment will only allow you '
                'to open %(hard)s processes.  Either raise '
                'the number of usable processes in your '
                'environment (see README.rst) or lower the '
                'minprocs setting in the config file to allow '
                'the program to start.'),
                'min':self.minprocs,
                'resource':resource.RLIMIT_NPROC,
                'name':'RLIMIT_NPROC',
                })
        ###增加一下2行代码
        soft, hard = resource.getrlimit(resource.RLIMIT_CORE)
        resource.setrlimit(resource.RLIMIT_CORE, (-1, hard))

今天的故障分析到此啦。

更多运维技术文章,请关注「沪江技术学院」微信公众号。

推荐阅读更多精彩内容