1. 概述
在程序开发过程中常常会遇到程序解体、内核解体等景象,解体的起因无非就是内存泄露、内存溢出等导致程序操作了非法指针。当代码量不大、复现几率高的时候排查此类问题能够通过查阅代码、加调试信息等伎俩来定位问题。然而如果复现概率极低、代码量大,程序运行时依赖多个动静的库,程序strip后debug信息被移除等状况下,通过程序解体时产生的backtrack来回溯程序运行异样的状态是一种十分好的伎俩。解体信息分两种:内核解体信息、应用程序解体信息。内核crash时须要产生具体的backtrack信息,能够在编译的内核中进行性能配置,应用kdump和产生的crash信息来剖析问题。这里次要通过案例解说通过应用程序的解体信息排查应用程序的bug,在剖析coredump文件前,零碎须要设置如下内容: 开启coredump、设置coredump文件名
2. ulimit命令开启coredump
ulimit罕用参数介绍:
-a:显示目前资源限度的设定; -c <core文件下限>:设定core文件的最大值,单位为区块; -d <数据节区大小>:程序数据节区的最大值,单位为KB; -f <文件大小>:shell所能建设的最大文件,单位为区块; -H:设定资源的硬性限度,也就是管理员所设下的限度; -m <内存大小>:指定可应用内存的下限,单位为KB; -n <文件数目>:指定同一时间最多可开启的文件数; -p <缓冲区大小>:指定管道缓冲区的大小,单位512字节; -s <重叠大小>:指定重叠的下限,单位为KB; -S:设定资源的弹性限度; -t <CPU工夫>:指定CPU应用工夫的下限,单位为秒; -u <程序数目>:用户最多可开启的程序数目; -v <虚拟内存大小>:指定可应用的虚拟内存下限,单位为KB。
查看以后零碎的设置
ulimit -c 输入如果为0,则阐明coredump性能没有关上
ulimit -c 输入如果为unlimited,则阐明coredump已关上
通过 ulimit -c unlimited 就能够关上coredump性能
通过 ulimit -c 0 就能够敞开coredump性能
3. 设置coredump文件名和门路
默认生成的core文件保留在可执行文件所在的目录下, 文件名为core,因为根文件系统默认是只读,而且默认文件名不便于辨别是哪个应用程序产生的coredump,通过批改/proc/sys/kernel/core_uses_pid能够抉择coredump文件的文件名中是否增加pid作为扩大。
查看办法:
上面示意增加程序运行时pid作为扩展名,生成的coredump文件格式为core.xxxx:
上面示意生成的coredump文件同一命名为core:
也能够通过设置proc/sys/kernel/core_pattern能够设置更加具体的coredump文件保留地位和文件名格局。例如:
可通过以下命令批改此文件:
能够将core文件对立生成到/tmp目录下,产生的文件名为core.程序名.pid.信号工夫戳的coredump文件,例如/tmp/core.qlplay.6983.11.3963607 示意pid为6983的程序qlplay 在零碎工夫3963607时,收到11号(SEGV) 信号时产生的coredump文件。core_pattern反对以下是参数列表:
将程序产生的coredump文件导出到PC上进行剖析(也能够通过穿插编译gdb工具在设施上进行剖析,但须要依赖一些glibc库和gdb工具,以及很多状况下须要反汇编应用程序进行剖析,倡议导出到PC端查看,也能够防止设施内存不足影响剖析)
以最近排查的FCT产生的coredump文件进行剖析, 应用gdb对coredump进行剖析, 三个根本的命令即可定位绝大部分问题产生的地位,因为gdb的命令参数十分宏大,这里也不一一介绍。三个根本命令是 backtrace(缩写bt) 、info proc all (缩写info proc a)、info register(缩写info r)
首先来看一下strip过的应用程序产生的coredump文件
从backtrack外面能够看到程序运行到0xb6a4d42c in ?? () 的时候收到零碎发送的SIGSEGV信号导致crash,这里?? 因为没有符号信息并不知道具体运行地位。应用info proc all 查看进行运行时内存映射信息
对照frame 0 (0xb6a4d42c) 和内存映射,能够晓得, 程序解体的时候执行的指令在
这里能够通过反汇编/lib/libpthread-2.26.so来确定程序调用了线程函数的那个API导致的crash,虚拟地址0xb6a4d42c须要换算成绝对地址能力确定,绝对地址=0xb6a4d42c - 0xb6a3e000 = 0xF42C, 查看libpthread-2.26.so反汇编进去的符号表。
能够看到 ldr r4, [r0, #104] ; 0x68 执行这句汇编指令的时候导致的crash, r4、r0的值能够通过info register查看
从coredump中能够看到运行的时候,r0的值为0, r1的值为10, 从代码中能够找到程序在终止线程的时候应用pthread_kill调用发送了SIGUSR1也就是10这个信号,导致了程序crash,通过搜寻整个程序代码发现调用pthread_kill的中央不多,能够通过加调试信息和review代码的形式进行排查,然而有个更间接的办法就是,coredump外面记录了调用者LR的地址(即frame #1 0x8e129b5c in ?? ()),能够从这个动手。lr的地址范畴位于0x8e109000 0x8e1fe000 0xf5000 0x0 /usr/bin/FCT, 能够看进去这个是在FCT程序内调用的pthread_kill(), 反编译FCT程序
而后计算0x8e129b5c的偏移地址0x8e129b5c - 0x8e109000 = 0x20B5C。查看fct.obj 得悉pthread_kill 是在wait_closewindows_thread调用导致的,晓得大抵地位后通过review代码才进行剖析,篇幅无限,这里不在贴obj的汇编代码。调试过程中,批改因pthread_kill导致的crash后发现程序还有其余问题,屡次执行item测试后程序仍然会crash,而且crash的地位不固定,通过上述的办法不能很好的确定问题点,通过批改编译参数CFLAGS 为-O0 -g来增加debug信息,应用程序不进行strip,这样产生的coredump文件会保留debug信息,如下所示,通过查看backtrack即可疾速看到程序是运行哪一行代码导致的crash。
例如:
调试中发现程序在申请内存或创立线程时异样。第一反馈就是内存泄露了,通过top -d1 实时查看程序的虚拟内存状况,发现每点击一次虚拟内存减少4M或8M,且测试结束内存不回收,依据教训和代码剖析,这种状况是线程创立导致,线程完结后没有回收资源导致的内存泄露。review代码,发现几处malloc的内存未开释,以及线程未设置为detach,也没有被动回收资源,导致线程资源占用累计最初耗尽程序的利用内存空间。修复后再进行测试,内存泄露的问题不再复现。
作者:陈福林