Linux问题调试(3)--调试文件(core、map、Debug)的生成与调试工具(Addr2line、GDB)的使用

一、问题描述

        在项目开发过程中,时常会出现内存泄漏、设备异常重启等一系列问题,Linux也提供了丰富的调试工具和手段。本文主要基于Linux操作系统、Clang/Ninjia构建工具、交叉编译工具链和CMake构建系统,重点介绍基础了调试文件Debug、map和Core的生成与调试工具Addr2Line和GDB的基础使用。

二、项目调试文件(Core、map、Debug)

1、Debug文件

介绍

        Linux平台生成的可执行文件,带有Debug或Release属性。

        Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员对程序的调试。在Debug环境下,我们可以使用调试技巧,如观察监视、内存、反汇编等等。

        Release称为发布版本,它往往进行了各种优化,使程序在代码大小和运行速度上都是最优解,便于用户使用。

生成

        GCC提供了多种选项和方法来生成debug:

        1)通过-g选项生成带有调试信息的可执行文件。‌这些调试信息包括函数名、‌变量名等;

        2)使用-DDEBUG选项可以在编译时定义DEBUG宏。‌

[root&user:]gcc -g myprogram.c -o myprogram
[root&user:]gcc -DDEBUG myprogram.c -o myprogram_debug

         CMake中可通过设置CMAKE_BUILD_TYPE宏来设置debug属性,也在构建时用-D定义宏设置。除此之外,也可通过set_target_properties函数设置生成文件的输出地址,如下所示,3-5提供了静态库、动态库和可执行文件的生成方法。

1、CMake文件中设置
set(CMAKE_BUILD_TYPE "Debug")    //生成可执行文件的类型为Debug文件
set(CMAKE_BUILD_TYPE "Release")    //生成可执行文件的类型为Release文件
2、构建时设置
cmake .. -D CMAKE_BUILD_TYPE=Debug
cmake .. -D CMAKE_BUILD_TYPE=Release

3、静态库的输出
add_library(lib STATIC ${SRC})     //添加静态库源文件
set_target_properties(lib PROPERTIES    //设置静态库输出
ARCHIVE_OUTPUT_DIRECTORY_DEBUG /debug    //debug库的输出
ARCHIVE_OUTPUT_DIRECTORY_RELEASE /release)    //release库的输出

4、动态库的输出
add_library(lib SHARED ${SRC}) 
set_target_properties(lib PROPERTIES
LIBRARY_OUTPUT_DIRECTORY_DEBUG ${OUT_LIB_PATH}/debug
LIBRARY_OUTPUT_DIRECTORY_RELEASE ${OUT_LIB_PATH}/release)

5、可执行文件的输出
add_executable(out ${SRC})
set_target_properties(res PROPERTIES
RUNTIME_OUTPUT_DIRECTORY_DEBUG ${OUT_EXE_PATH}/debug
RUNTIME_OUTPUT_DIRECTORY_RELEASE ${OUT_EXE_PATH}/release
DEBUG_POSTFIX "_debug")    //debug版本加尾缀

2、map文件

介绍

        map文件通常由链接器(Linker)生成,在构建过程中,链接器负责将各个编译单元(如.o或.obj文件)组合成一个可执行文件或库。同时链接器会生成map文件,以文本形式记录链接过程的详细信息。

        map文件的基础结构主要包括以下几个部分:

段(section) :描述映像文件的代码和数据块。
RO:Read-Only的缩写,包括RO-data(只读数据)和RO-code(代码)。
RW:Read-Write的缩写,主要是RW-data,RW-data由程序初始化初始值。
ZI:Zero-initialized的缩写,主要是ZI-data,由编译器初始化为0。
.text:与RO-code同义。
.constdata:与RO-data同义。
.bss: 与ZI-data同义。
.data:与RW-data同义

        map文件可精细的计算可执行文件的代码段、静态区(Data+Bss)在内存中的大小,这部分会在后续的内存统计中阐述。

生成

        GCC可通过-Wl,-Map=的方式生成map文件。

[root&user:]gcc -g -Wl,-Map=output.map -o main main.c

-g:生成调试信息
-Wl,-Map=output.map:告诉链接器生成一个名为 output.map 的映射文件
-o:生成可执行文件main

        在CMake工程中可通过设置CMAKE_EXE_LINKER_FLAGS链接宏的方式生成map。

set (CMAKE_EXE_LINKER_FLAGS "-Xlinker -Map=bdc.map")

3、Core文件

介绍

       内核转储(core dump)是 Linux程序调试的一种有效手段,它能保存问题发生时的状态。只要有问题发生时程序的可执行文件和core dump,就可以知道进程当时的状态。它是该进程实际使用的物理内存的快照。

        Linux信号是一种异步事件处理机制,每种信号都对应有默认的异常处理操作,默认操作包括忽略该信号(Ignore)、暂停进程(Stop)、终止进程(Terminate)、终止并产生core dump(Core)等。Core dump是Linux基于信号实现的,可生成Core文件的异常信号如下:

Signal	  Value	    Action	Comment
SIGQUIT	    3	    Core	Quit from keyboard                    //按键退出
SIGILL	    4	    Core	Illegal Instruction                   //非法指令
SIGTRAP	    5	    Core	Trace/breakpoint trap                 //断点或陷阱指令
SIGABRT	    6	    Core	Abort signal from abort(3)            //abort发出的信号
SIGFPE	    8	    Core	Floating point exception              //浮点异常
SIGBUS	10,7,10	    Core	Bus error (bad memory access)         //内存访问越界
SIGSEGV	    11	    Core	Invalid memory reference              //无效内存访问
SIGSYS	12,31,12	Core	Bad argument to routine (SVr4)        //
SIGXCPU	24,24,30	Core	CPU time limit exceeded (4.2BSD)      //CPU超时
SIGXFSZ	25,25,31	Core	File size limit exceeded (4.2BSD)     //文件大小超出限制
SIGUNUSED	31	    Core	Synonymous with SIGSYS                //系统调用异常

生成

        ulimit –c 查看core dump机制是否使能,若为0则默认不产生;可通过ulimit –c unlimited(或者指定core文件大小)来使能。

ulimit -c    //查看core文件大小限制
ulimit -c unlimited    //不限制core文件大小
ulimit -c 1024*1024    //设置core文件的大小为1024*1024

        在大型工程项目中,会希望Core文件存放在固定的位置。默认情况下会在当前目录中生成。Core保存位置的完整路径可以通过sysctl变量kernel.core_pattern设置。如下设置,当执行可执行文件时,则在/var/core生成core文件。

[root&user:]cat /etc/sysctl.conf
kernel.core_pattern = /var/core/%t-%e-%p-%c.core
kernel.core_uses_pid = 0
[root&user:]sysctl -p    #从指定的文件加载系统参数,如不指定即从/etc/sysctl.conf中加载

[root&user:]ls var/core/
1223267175-a.out-2820-182867378623467164789123.core

%% 单个%字符
%p 所dump进程的进程ID
%u 所dump进程的实际用户ID
%g 所dump进程的实际组ID
%s 导致本次core dump的信号
%t core dump的时间 (由1970年1月1日计起的秒数)
%h 主机名
%e 程序文件名

        kernel_core_pattern经常会加入管道符,启动用户模式辅助程序,常用的路径是/proc/sys/kernel/core_pattern。

[root&user:]cat /proc/sys/kernel/core_pattern    #查看Core文件默认保存路径
[root&user:]echo “/data/xxx/<core_file>” > /proc/sys/kernel/core_pattern    #指定Core文件保存路径和文件名

        也可通过core_helper来自动压缩Core文件数据,这样Core就会在/date/core下生成压缩文件了。

[root&user:]cat /date/core_helper    #查看core_helper脚本
#!/bin/sh

exec gzip - > /date/core/$1-$2-$3-$4.core.gz

#core文件的转存
[root&user:]echo “|/data/core_helper” > /proc/sys/kernel/core_pattern
[root&user:]cat /etc/sysctl.conf
kernel.core_pattern = /date/core_helper %t %e %p %c
kernel.core_uses_pid = 0
[root&user:]sysctl -p

#Core压缩文件的生成
[root&user:]ls /data/core
1223267175-a.out-2820-182867378623467164789123.core.gz

调试

        1)生成代码调试信息的debug文件(详情见Debug文件那节);

        2)启动调试器(GDB):如下图可知产生Linux设备异常的信号是8(SIGFPE),同时打印出出问题的代码行;

        3)显示栈帧bt命令(backtrace命令)

        4)通过disassemble命令可以打印出错时的汇编代码片段,其中箭头指向的是出错的指令,即PC寄存器指向的地址,PC寄存器存放的是下一条执行指令。

三、调试工具(Addr2line、GDB)的使用

        在介绍调试工具(Addr2line、GDB)调试工具之前,还需要介绍两个Linux内核打印,分别是/proc/self/maps(/smaps)和/proc/kmsg。

1、maps与smaps

       通过/proc可以比较详细和精确地知道整机内存/某个进程内存的使用情况,单个进程的内存查看/proc/[pid]下面有几个文件:maps , smaps, status(本文只介绍前两种)。

        maps 文件可以查看某个进程的代码段、栈区、堆区、动态库、内核区对应的虚拟地址。

[root&user:]cat /proc/self/maps    #/proc/self代码指向当前进程
561a305e8000-561a305f0000 r-xp 00000000 fd:00 1835050                    /bin/cat
561a307ef000-561a307f0000 r--p 00007000 fd:00 1835050                    /bin/cat
561a307f0000-561a307f1000 rw-p 00008000 fd:00 1835050                    /bin/cat
561a32021000-561a32042000 rw-p 00000000 00:00 0                          [heap]
7fee490cb000-7fee4955f000 r--p 00000000 fd:00 656887                     /usr/lib/locale/locale-archive
7fee4955f000-7fee49746000 r-xp 00000000 fd:00 2626554                    /lib/x86_64-linux-gnu/libc-2.27.so
7fee49746000-7fee49946000 ---p 001e7000 fd:00 2626554                    /lib/x86_64-linux-gnu/libc-2.27.so
7fee49946000-7fee4994a000 r--p 001e7000 fd:00 2626554                    /lib/x86_64-linux-gnu/libc-2.27.so
7fee4994a000-7fee4994c000 rw-p 001eb000 fd:00 2626554                    /lib/x86_64-linux-gnu/libc-2.27.so
7fee4994c000-7fee49950000 rw-p 00000000 00:00 0 
7fee49950000-7fee49977000 r-xp 00000000 fd:00 2622693                    /lib/x86_64-linux-gnu/ld-2.27.so
7fee49b33000-7fee49b57000 rw-p 00000000 00:00 0 
7fee49b77000-7fee49b78000 r--p 00027000 fd:00 2622693                    /lib/x86_64-linux-gnu/ld-2.27.so
7fee49b78000-7fee49b79000 rw-p 00028000 fd:00 2622693                    /lib/x86_64-linux-gnu/ld-2.27.so
7fee49b79000-7fee49b7a000 rw-p 00000000 00:00 0 
7ffd32698000-7ffd326ba000 rw-p 00000000 00:00 0                          [stack]
7ffd326f4000-7ffd326f7000 r--p 00000000 00:00 0                          [vvar]
7ffd326f7000-7ffd326f9000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

         相对于maps文件只能显示简单的分区,smap文件可以显示每个分区的更详细的内存占用数据。

[root&user:] cat /proc/self/smaps    #每个分区显示内存类似,只展示一个分区情况
5592857f8000-559285800000 r-xp 00000000 fd:00 1835050                    /bin/cat
Size:                 32 kB        #虚拟内存大小
KernelPageSize:        4 kB        #实际使用物理内存大小
MMUPageSize:           4 kB
Rss:                  32 kB
Pss:                  32 kB
Shared_Clean:          0 kB        
Shared_Dirty:          0 kB
Private_Clean:        32 kB        #页面被改,则是dirty,否则是clean
Private_Dirty:         0 kB        #页面引用计数>1,是shared,否则是private
Referenced:           32 kB
Anonymous:             0 kB
LazyFree:              0 kB
AnonHugePages:         0 kB
ShmemPmdMapped:        0 kB
Shared_Hugetlb:        0 kB
Private_Hugetlb:       0 kB
Swap:                  0 kB        #处于交换区的页面大小
SwapPss:               0 kB        #操作系统一个页面大小
Locked:                0 kB        #体系结构MMU一个页面大小
VmFlags: rd ex mr mw me dw sd 

2、dmesg与kmsg

        dmesg是一种程序,用于检测和控制内核环缓冲,程序用来帮助用户了解系统的启动和运行信息。kernel会将开机信息存储在ring buffer中,若是开机时来不及查看信息,可利用dmesg来查看。

        dmesg是从kernel的ring buffer(环缓冲区)中读取信息的,可以用dmesg 来显示ring buffer内容,还可以用dmesg >xxx.txt 把ring buffer的内容保存为文件,但是如果ring buffer有可能刷新了或者满了有些信息会漏掉,而且dmesg不能实时打印系统发生了什么,这个时候就需要kmsg。

dmesg -w            #可以实时查看日志信息
dmesg -w >log.txt   #可以把实时的打印放到log.txt中
dmesg -C            #可以清除ringbuf的内容

         /proc/kmsg是专门输出内核信息的地方,为了能够方便的在 user space 读取 Kernel log,Kernel driver 里面将ring buffer映射到了 /proc 目录下的文件节点 /proc/kmsg。所以读取 /proc/kmsg 文件其实就是在访问 Kernel Log 的循环缓冲区。虽然 Log buffe的大小是固定的,但是可以通过不断的访问 /proc/kmsg 将所有的log都备份下来。

        #实时显示内核打印信息,它会block住,然后随着调试不断打印。当有printk打印出信息的时候,自然会在窗口中打印出来的,他只打印最新的打印。

[root&user:]cat /proc/kmsg
#重定向将 Log 转存到文件中
[root&user:]cat /proc/kmsg >~/Desktop/a.txt

        当设备出现异常时,通过使用 dmesg或 /proc/kmsg可以Linux的栈回溯信息。在栈回溯过程中,关键的寄存器是程序计数器(Program Counter,PC),它保存着当前指令的地址。栈回溯需要从 PC 寄存器中获取每个函数调用的返回地址。除了PC指针以外,也需要关注连接寄存器(LR),在执行子函数前,LR会保存上一层执行的函数地址。

[ 3893.210792] rtsp_msg_svc (27315): undefined instruction: pc=d9e3d8d2
[ 3893.238816] CPU: 0 PID: 27315 Comm: rtsp_msg_svc Tainted: P           O      4.19.91 #1-svn659773
[ 3893.259340] Hardware name: Novatek Video Platform
[ 3893.283800] PC is at 0x70872890
[ 3893.300188] LR is at 0x722d3620
[ 3893.309075] pc : [<70872890>]    lr : [<722d3620>]    psr: 20000010
[ 3893.327978] sp : 4f8a3c48  ip : 4f8a3c25  fp : 00b7b6f6
[ 3893.340079] r10: 00b7b9c7  r9 : 00002000  r8 : 0000007d
[ 3893.350536] r7 : 4f8a3cc0  r6 : 65696c63  r5 : 3baf96d8  r4 : 6ffebf80
[ 3893.378390] r3 : 00000000  r2 : 0000000a  r1 : 00002002  r0 : 00000000
[ 3893.407519] Flags: nzCv  IRQs on  FIQs on  Mode USER_32  ISA ARM  Segment user
[ 3893.422177] Control: 10c5387d  Table: 0f108059  DAC: 00000055
[ 3893.452478] Code: 00000000 000000f0 00000000 0000000d (ffffffff) 
[ 3893.758042] [0;34m[DDM] close wdt[0m

3、addr2line

介绍

        Addr2line 工具(它是标准的 GNU Binutils 中的一部分)是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具。它通常用于解析堆栈跟踪信息。

        Addr2line的工作原理是读取一个程序的映射文件(通常是.map文件)、一个核心转储文件(core dump)或内核的栈回溯信息(dmesg),然后使用这个映射信息将程序地址转换为源代码的具体位置。它的工作原理涉及到二进制文件的调试信息,它通常包含在可执行文件中。当编译程序时,需要打开调试信息(详情见Debug文件),这样Addr2line才能正确解析地址。

基本用法:addr2line [选项] [地址]

选项    描述
-a    --addresses 显示地址
-b    --target= 设置二进位文件格式
-e    --exe= 设置输入文件名称(默认为 a.out)
-i    --inlines 解开内联函数
-j    --section= 读取相对于段的偏移而非地址
-p    --pretty-print 让输出对人类更可读
-s    --basenames 去除目录名
-f    --functions 显示函数名
-C    --demangle[=style] 解码函数名
-h    --help 显示本帮助

调试

1、用户态普通程序

使用方法:addr2line -e 可执行文件 栈回溯地址 -f

        用户态程序有时可能因为各种原因导致崩溃,发生段错误,比如空指针等。如果没有靠谱的工具,我们就只能靠猜哪里的代码可能存在问题,这里通过Linux自带的addr2line工具调试程序,能够快速直接帮我们准确定位到文件、异常函数名以及行号。

        使用步骤是通过dmesg找到崩溃的栈指针回溯,主要看PC指针和LR指针,使用/proc/maps查看崩溃地址是否在代码段中,在用户代码段这可以执行下述步骤。

#打印崩溃指针
[root&user:]dmesg
...
[ 3893.283800] PC is at 0x6a2e5c
[ 3893.300188] LR is at 0x683620
...
#找到崩溃地址
[root&user:]cat /proc/self/maps
...
00010000-00e43000 r-xp 00000000 b3:06 32004      /out
00e52000-00e59000 r--p 00e32000 b3:06 32004      /out
00e59000-00ef2000 rw-p 00e39000 b3:06 32004      /out
...
#使用addr2line工具
[root&user:]arm-linux-gnueabihf-addr2line -e -f out_debug 0x6a2e5c
rtsps_client
/src/.../app/rtsps_client.cpp:1969

        需要注意的是,如果编译程序时不含调试信息,就只能显示出函数名,显示不出具体所在文件的位置,也可能什么都不显示,如下:

[root&user:]arm-linux-gnueabihf-addr2line -e out 0x6a2e5c
??
??:0

2、动态库程序

使用方法:addr2line -e 动态库名 栈回溯地址-基地址 -f

         在动态库中发生段错误,也可以使用addr2line进行定位,编码动态库时同样需要加上调试信息。使用步骤与用户程序相类似,首先是通过dmesg找到崩溃的栈指针回溯,主要看PC指针和LR指针,使用/proc/maps查看崩溃地址是否在某个动态库中,找到对应动态库的基地址,栈回溯地址-基地址,具体步骤如下。

#打印崩溃指针
[root&user:]dmesg
...
[ 3893.283800] PC is at 0x70872890
[ 3893.300188] LR is at 0x722d3620
...
#找到崩溃地址
[root&user:]cat /proc/self/maps
...
700fc000-715fd000 r-xp 00001000 00:12 8988       /app/bsp/libbsp.so
715fd000-7370d000 r--p 00000000 00:12 8987       /app/bsp/libbsp.so
7370d000-777de000 rw-p 00000000 00:12 8987       /app/bsp/libbsp.so
...
#崩溃指针-库基地址:70872890-700fc000=776890
[root&user:]arm-linux-gnueabihf-addr2line -e -f libbsp.so 0x776890
bsp_init
/src/.../bsp/bsp_init.c:170

4、gdb

介绍

        GDB 是由 GNU 软件系统社区提供的调试工具,同 GCC 配套组成了一套完整的开发环境,GDB 是 Linux 和许多类 Unix 系统中的标准开发环境。

        一般来说,GDB 主要完成下面四个方面的功能:

启动程序:可以按照自定义的要求随心所欲的运行程序。
设置断点:可让被调试的程序在所指定的调置的断点处停住,断点可以是条件表达式。
打印信息:当程序被停住时,可以检查此时程序中所发生的事。
修改变量:可以通过修改程序中的变量,将一个 BUG 产生的影响修正从而测试其他 BUG。

        启动GDB的方法有以下几种:

1、gdb program
program 也就是你的执行文件,一般在当前目录下。

2、gdb program core
用gdb同时调试一个运行程序和core文件,core是程序非法执行后core dump后产生的文件。

3、gdb program <进程ID>
如果你的程序是一个服务程序,那么你可以指定这个服务程序运行时的进程ID。gdb会自动attach上去,并调试他。program应该在PATH环境变量中搜索得到。

常用命令

命令含义
r重新执行
q退出gdb
n下一步
c继续执行

b

打断点

【普通断点】b file.c :行号

【条件断点】b file.c : 行号 if num == 2

【查看断点】info b

【删除断点】del 2

【禁用/启用】disable /enable 2

watch

观察断点

【监控num】watch num 

当被监控变量/表达式的值发生改变时,程序停止运行

【查看观察断点】info watchpoints

监控局部变量num:一旦num失效,则监控操作也随即失效。
监控指针变量*p:  watch *p表示监控p所指数据的变化情况;watch p表示监控p指针本身是否改变指向。
监控数组a[10]中的元素:watch a表示只要数组a中元素发生改变,程序就会停止执行。

catch

捕捉断点

                
bt查看函数调用堆栈信息,用于发生段错误时,追踪错误
display

【频繁查看变量】display num   程序每暂停一次(遇断点或单步调试),自动打印变量num值

【禁用/启用】disable / enable display num

list默认显示当前行的上5行和下5行的源代码

print

打印变量

【打印变量的值】print var

【打印变量的地址】print &var

【打印地址的数据值】print *address

finish执行完当前函数并跳出
frame 栈帧号打印栈帧
info frame显示当前栈帧的详细信息

调试

1、调试可执行文件

        在生成包含调试信息的 out_debug 可执行文件的基础上,启动 GDB 调试器的指令如下:

[root@user:]# gdb out_debug
GNU gdb (GDB) 8.0.1
Copyright (C) 2017 Free Software Foundation, Inc.
......
(gdb) 

2、调试Core文件

        可导出core文件,在编译服务器中,使用out_debug和core,启动GDB调试:

[root&user:]# arm-linux-gnueabihf-gdb out_deubg core
GNU gdb (GDB) 7.12.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=arm-ca9-linux-gnueabihf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from hicore.map...done.

warning: core file may not match specified executable file.
[New LWP 1064]
[New LWP 1144]
[New LWP 935]

warning: Corrupted shared library list: 0x0 != 0xe2e094

warning: Could not load shared library symbols for 97 libraries, e.g. /lib/libboundscheck.so.
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?
Core was generated by `/home/app/hicore'.
Program terminated with signal SIGABRT, Aborted.
#0  0x72aac688 in ?? ()
[Current thread is 1 (LWP 3513)]
(gdb) bt
#0  0x72aac688 in ?? ()
#1  0x40df2b28 in ?? ()

3、attach命令调试对应进程

        要使用gdb进行进程调试并附加到正在运行的进程,可以使用gdb的attach命令。attach命令的语法如下:

[root&user:]# attach <进程ID>

        其中,<进程ID>是要调试的目标进程的进程ID。使用attach命令进行进程调试的一般步骤:

#找打进程号
[root&user:]# top
...
PID USER      PR  NI    VIRI    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                    
26283 root      20   0       0      0      0 I   5.6  0.0   0:00.88 [kworker/1:2]
... 

#启动gdb
[root&user:]# gdb

#在gdb提示符下,使用attach命令附加到目标进程
[root&user:]# attach 26283

#也可以将上述两个指令一起执行
[root&user:]# gdb attach 26283

四、总结

        当遇到设备中程序异常结束问题时,如果打印中得不到想要的信息时,基本上可以通过下述方式进行问题的定位:

1、通过dmesg或/proc/ksmg得到内核的栈回溯信息;

[ 3893.259340] Hardware name: Novatek Video Platform
[ 3893.283800] PC is at 0x70872890
[ 3893.300188] LR is at 0x722d3620
[ 3893.309075] pc : [<70872890>]    lr : [<722d3620>]    psr: 20000010
[ 3893.327978] sp : 4f8a3c48  ip : 4f8a3c25  fp : 00b7b6f6
[ 3893.340079] r10: 00b7b9c7  r9 : 00002000  r8 : 0000007d
[ 3893.350536] r7 : 4f8a3cc0  r6 : 65696c63  r5 : 3baf96d8  r4 : 6ffebf80
[ 3893.378390] r3 : 00000000  r2 : 0000000a  r1 : 00002002  r0 : 00000000

2、使用maps与smap信息,查找PC与LR指针所在的进程的内存位置,如崩溃在栈区,则可以得到对应线程的TID,再通过ps -T找到崩溃的线程;

7007c000-7007d000 ---p 00000000 00:00 0 
7007d000-700fd000 rwxp 00000000 00:00 0          [stack:941]
700fd000-700fe000 ---p 00000000 00:00 0 
700fe000-708fe000 rwxp 00000000 00:00 0          [stack:939]
708fe000-708ff000 ---p 00000000 00:00 0 
708ff000-709ff000 rwxp 00000000 00:00 0          [stack:938]
709ff000-70a00000 ---p 00000000 00:00 0 
70a00000-71200000 rwxp 00000000 00:00 0          [stack:935]
71200000-712d5000 rw-p 00000000 00:00 0 
712d5000-71300000 ---p 00000000 00:00 0 
71303000-71364000 rw-p 00000000 00:00 0 
71364000-71365000 ---p 00000000 00:00 0 
71365000-7137e000 rwxp 00000000 00:00 0          [stack:957]
7137e000-7137f000 ---p 00000000 00:00 0 
7137f000-713ff000 rwxp 00000000 00:00 0          [stack:940]
713ff000-71400000 ---p 00000000 00:00 0 
71400000-71800000 rwxp 00000000 00:00 0          [stack:931]
71800000-718f0000 rw-p 00000000 00:00 0 
718f0000-71900000 ---p 00000000 00:00 0 
71907000-71908000 rw-p 00000000 00:00 0 
71908000-71909000 rw-s 00000000 00:05 32769      /SYSV00000100 (deleted)
71909000-7190b000 rw-s 17e02000 00:10 9246       /dev/nvtmpp
7190b000-7190c000 rw-s 00000000 00:05 0          /SYSV6869636f (deleted)
7190c000-7190d000 rw-s 00000000 00:05 98307      /SYSV474f4c48 (deleted)
7190d000-71916000 r-xp 00000000 00:01 5692       /lib/libnss_files-2.29.so
71916000-71925000 ---p 00009000 00:01 5692       /lib/libnss_files-2.29.so
71925000-71926000 r--p 00008000 00:01 5692       /lib/libnss_files-2.29.so
71926000-71927000 rw-p 00009000 00:01 5692       /lib/libnss_files-2.29.so
71927000-71955000 rw-p 00000000 00:00 0 
71955000-71956000 ---p 00000000 00:00 0 
71956000-72156000 rwxp 00000000 00:00 0          [stack:930]
72156000-72157000 ---p 00000000 00:00 0 
72157000-72957000 rwxp 00000000 00:00 0          [stack:929]
72957000-72958000 ---p 00000000 00:00 0 
72958000-72a58000 rwxp 00000000 00:00 0          [stack:928]
72a58000-72a5e000 rw-p 00000000 00:00 0 

3、根据PC与LR指针地址,使用Addr2line工具反查函数信息。

#PC与LR地址在进程的代码段
[root&user:]arm-linux-gnueabihf-addr2line -f -e out_debug 0x2821dc
#PC与LR地址指向动态库
[root&user:]arm-linux-gnueabihf-addr2line -f -e xx.so (崩溃地址-so首地址)

4、使用GDB工具分析Core文件

[root&user:]arm-linux-gnueabihf-gdb out_debug core

        最好的做法是在设备重启之前,将dmesg、/proc/kmsg、/proc/self/maps、/proc/self/smaps、ps -T信息保存到flash中。

参考资料:

CMake生成Debug和Release目标程序时的一些配置

MAP文件分析

Linux内核调试方法总结之coredump

linux 内存查看方法:meminfo\maps\smaps\status 文件解析

linux内核调试之kmsg和dmesg

【Linux】GDB用法详解(5小时快速教程)

GDB常用命令

高级调试技巧揭秘:深入了解gdb调试正在运行的进程

《Debug Hacks中文版—深入调试的技术和工具》

  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值