背景:1.运行目标为MIPS机器,FLASH及RAM资源都非常紧张,无法运行带调试信息的程序2.程序有一定概率崩溃,从表现上难以分析
目标:直接定位到崩溃目标代码
说明:1、2在运行环境中操作,3、4在编译环境中操作
1.设置内核转储
内核转储可以保存程序崩溃时的内存状态。是系统级别的实现。查看运行机器当前内核转储的设置:
#ulimit -a
-f: file size (blocks) unlimited
-t: cpu time (seconds) unlimited
-d: data seg size (kb) unlimited
-s: stack size (kb) 8192
-c: core file size (blocks) 0
-m: resident set size (kb) unlimited
-l: locked memory (kb) 64
-p: processes 986
-n: file descriptors 1024
-v: address space (kb) unlimited
-w: locks unlimited
-e: scheduling priority 0
-r: real-time priority 0
当前“core file size”为0,即未开启。设置为不限制,修改运行机器的
/etc/profile:
#添加
ulimit -c unlimited
应用配置,在运行机器中执行:
#source /etc/profile
2.指定崩溃文件生成路径
默认崩溃文件会生成在程序运行路径下。程序占用的内存越大,生成的崩溃文件会越大。如果只需要部分内存段信息,可以通过coredump_filter文件指定。文件位于/proc/
/coredump_filter,默认值为0x23,数值含义如下:(bit 0) anonymous private memory(匿名私有内存段)
(bit 1) anonymous shared memory(匿名共享内存段)
(bit 2) file-backed private memory(file-backed 私有内存段)
(bit 3) file-backed shared memory(file-bakced 共享内存段)
(bit 4) ELF header pages in file-backed private memory areas (it is effective only if the bit 2 is cleared)(ELF 文件映射,只有在bit 2 复位的时候才起作用)
(bit 5) hugetlb private memory(大页面私有内存)
(bit 6) hugetlb shared memory(大页面共享内存)
这里我们不做修改,我们去压缩崩溃文件来减小占用体积。修改运行机器的/etc/sysctl.conf:#添加
kernel.core_pattern = | /usr/sbin/core_gzip %e %p
kernel.core_uses_pid = 0
创建/usr/sbin/core_gzip脚本:#!/bin/sh
exec gzip -> /tmp/$1_$2.core.gz #注意路径必须存在
修改文件属性:#chmod +x /usr/sbin/core_gzip
这样程序崩溃的时候会在运行机器的/tmp下生成例如prog_1480.core.gz文件,文件名格式是由前面的%e %p指定的,具体参数含义如下:%c 转储文件的大小上限
%e 所dump的文件名
%g 所dump的进程的实际组ID
%h 主机名
%p 所dump的进程PID
%s 导致本次coredump的信号
%t 转储时刻(由1970年1月1日起计的秒数)
%u 所dump进程的实际用户ID
应用配置,在运行机器中执行:#sysctl –p /etc/sysctl.conf
3.程序编译注意事项
在本机使用交叉工具链编译的时候需要生成2个文件,一个是带调试信息的,另外一个是我们实际运行的。方法如下:- 编译选项加上-g生成了体积巨大的程序prog,我们备份为prog_debug,prog_debug是用来分析崩溃文件的。
- 在交叉工具链中找到mips_xxx_strip工具,执行#mips_xxx_strip prog得到去掉了调试信息的正常大小的prog
另外需要注意的是,为了让程序能够产生崩溃文件,我们不能处理SIGQUIT,SIGILL,SIGABRT, SIGFPE和SIGSEGV这5个信号。4.定位崩溃
前面我们已经准备好了运行机器环境和带调试信息的程序。等待程序崩溃后,我们从运行机器中拿到prog_1480.core.gz,#gzip -d prog_1480.core.gz解压后得到prog_1480.core原始崩溃文件。使用交叉工具链的mips_gdb工具执行:#mips_xxx_gdb -ex bt prog_debug prog_1480.core
不出意外的话我们会看到崩溃代码和堆栈信息(-ex bt表示在mips_gdb中执行bt指令)Core was generated by `/tmp/prog'.
Program terminated with signal 11, Segmentation fault.
#0 manmade_crash(this=this@entry=0x8fdf50) at /mnt/d/workspace/crash/main.cpp:250
250 *i = 12;
#0 xxx1.cpp:123
#1 xxx2.cpp:86
#2 xxx.h:66
Backtrace stopped: frame did not save the PC
问题找到了。可能我们会在mips_gdb的时候看到不能加载共享库文件的提示warning: Could not load shared library symbols for 9 libraries, e.g. /lib/libpthread.so.0.
在gdb中输入(gdb) info sharedlibrary查看下共享库的加载情况
From To Syms Read Shared Object Library
No /lib/libpthread.so.0
No /usr/lib/libstdc++.so.6
No /lib/libm.so.0
No /lib/libgcc_s.so.1
No /lib/libc.so.0
No /lib/ld-uClibc.so.0
在本机用户目录下我们新建一个.gdbinit文件把我们程序用到的交叉编译链的库路径包涵进去:set solib-search-path ~/sdk/staging_dir/toolchain-mipsel/lib
这样使用mips_gdb分析的时候就可以加载程序使用的共享库了。