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,也没有被动回收资源,导致线程资源占用累计最初耗尽程序的利用内存空间。修复后再进行测试,内存泄露的问题不再复现。
作者:陈福林