嵌入式设备使用GDB及coredump文件查找崩溃问题

背景:

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个文件,一个是带调试信息的,另外一个是我们实际运行的。方法如下:

  1. 编译选项加上-g生成了体积巨大的程序prog,我们备份为prog_debug,prog_debug是用来分析崩溃文件的。
  2. 在交叉工具链中找到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分析的时候就可以加载程序使用的共享库了。




你可能感兴趣的:(嵌入式)