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

717 阅读5分钟
原文链接: mp.weixin.qq.com

作者:夏志培

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

背景:

 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 ,系统配置层未发现问题。

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

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

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

  5. 为了所辖排查范围,用 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)
  6.   经过测试,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))

今天的故障分析到此啦。

End

活动推荐

点击下方图片即可阅读


沪江 x 饿了么联手举办的运维专场技术沙龙即将来袭!赶快报名上车!