gdb调试core dump文件

1 随时收录

  • gdb 调试时符号文件在哪里?

    若是指定为CMAKE_BUILD_TYPE=Debug,那么符号文件会嵌入到可执行文件中;
    如Debug编译产生的文件不能显示源代码,可以再执行以便cmake和make,因为有可能在某种情况下,编译会出错。

  • 如何定位动态库中的代码

    在gdb调试时,只有让程序运行起来之后,才会加载动态库,之后,b到某一个行之后,使用i sharedlibrary查看已经加载的动态库。之后,就可以b 动态库中的代码所在文件:行号,来定位具体的代码; 之后再执行c,就可以在运行过程中,当触发对应的条件,就可以定位到断点处;

  • 如何设置条件断点

    condition开头,后面跟上断点号,之后对期望的变量进行条件判断,如下所示:

      #gdb条件断点
      condition 1 value==1 # (C语言风格),只有再value为1时才命中
    
      #忽略前几次命中,在ignore之后紧跟断点号,之后是数据表示忽略前几次
      ignore 1 4 # 忽略断点1的前4次命中
    
  • d br: 删除所有的断点

  • p /t var-name: 打印变量var-name的二进制内容

  • i args: 显示当前和函数的参数

2 core dump文件处理和分析

将core文件,统一放到一个固定的目录下,如/corefile,方便统一管理;

sudo mkdir /corefile
sudo chmod /corefile

/proc/sys/kernel/core_pattern中的内容改为:/corefile/core-%e-%p%t;

一个小方法测试产生core文件, 直接输入命令:kill -s SIGSEGV $$

产生coredump一般都是在进程收到某个信号的时候,Linux下大概有60多个信号,可以使用kill -l命令全部列出来.

2.1 产生和配置core文件

  • ulimit -c: 检查和设置core文件生成的限制,输出为0,表示不生成core文件,使用ulimit -c unlimited,设置为无限制;

  • /proc/sys/kernel/core_pattern: 默认情况下,core文件会生成再程序运行所在的目录,可以修改此文件来指定core文件的存放位置和命名方式,例如:

    sudo echo "/tmp/core.%e-%p-%t-%s" > sudo /proc/sys/kernel/core_pattern

    该命令改变code dump位置

    说明:
    %e: 程序文件的完整路径(路径中的/会被!替换)
    %p: 进程ID
    %t: 进程崩溃的时间戳
    %s: 哪个信号让程序崩溃

2.2 永久生效可生成core dump的方法

2.2.1 方法一

使用2.1的方法,只能对当前的终端环境有效,要永久生效,可以修改文件/etc/security/limits.conf文件.

  • 关闭服务apport:默认自动开启,功能是自动生成崩溃报告,官方为来自动收集错误的软件,即automatic crash report generation.

    经验证同时开启coredump功能,对debug版本带-g编译产生的c++程序,不能自动生成崩溃文件.

    解决方案:关闭apport服务

    • service apport status: 查看该服务
    • sudo service apport stop: 关闭apport.service服务程序

2.2.2 方法一

vim ~/.bashrc
# 添加如下信息
ulimit -c unlimited
source ~/.bashrc

2.3 分析core文件

  1. 启动gdb

    将可执行文件和core文件一起传给gdb

     gdb /executable /path/to/core
    
  2. 在gdb中分析

    进入gdb后,可以使用一些命令来分析文件

  • bt: 查看崩溃位置
  • list: 查看代码上下文
  • print variable_name: 查看变量值
  • other command:
    • info locals: 显示当前函数的局部变量;
    • info args: 显示当前函数的输入参数;
    • info registers: 显示CPU寄存器的内容;

3 在Ubuntu上调试ARMv7的core文件

3.1 预备

  1. uname -a: 获得目前嵌入式系统

     Linux (none) 3.8.11-xilinx #40 SMP PREEMPT Thu Jan 12 17:02:11 CST 2023 armv7l GNU/Linux
    
  2. 安装armv7的交叉编译工具链:

    sudo apt-get udpate
    sudo apt-get install gcc-arm-linux-gnueabihf gdb-multiarch
    
    • gcc-arm-linux-gnueabihf: 是交叉编译器,用于在Ubuntu上编译ARMv7架构程序;
    • gdb-multiarch: 支持多种架构的GDB版本,可以用来调试ARMv7程序;

    安装gcc-arm-linux-gnueabihf之后,相关的文件在如下目录中:

    • /usr/local/ti-sdk-am335x-evm/linux-devkit/sysroots/i686-arago-linux/usr/bin/

    将ARMv7下的可执行文件和core dump文件拷贝到如上文件夹下

    若如上文件夹下没有arm-linux-gnueabihf-gdb文件,可以使用gdb-multiarch替代.

    为方便gdb调试时使用set solib-search-path设置库文件,可以将可执行文件所需要引用的库文件也一同放在如上目录下,不过建议还是同一放到一个固定的地方,方便调试,如:~/crash/下,可以存放,如下文件:

    • 可执行文件: 执行该文件产生了如下的core dump文件;
    • core: 即core dump文件
    • library: 该可执行文件依赖的库文件

    而将对应的ARMv7的系统库文件可以放在/opt/下,如:

    • /opt/arm_lib: 该文件夹下存放ARMv7系统/lib下的系统库文件和C运行库;
    • /opt/arm_usr_lib: 该文件夹下存放ARMv7系统/usr/lib下的用户级运行库;

    以上这5个文件,当使用gdb-multiarch调试时,需要使用set solib-search-path来设置,各个文件夹以:隔开;

  3. 获取ARMv7上的系统库文件

分别有/lib/usr/lib

  • /lib: 包含基本系统程序使用的共享库文件,例如: C库, 内核模块等

  • /usr/lib: 包含用户级应用程序使用的库文件

    并将该库存放到当前ubuntu下的/opt/下,如/opt/arm_lib/opt/arm_usr_lib

  1. 将Arm下运行的可执行文件所依赖的库,存放到指定位置,如/opt/arcs/librarys/

    gdb-multiarch调试时,需要使用set solib-search-path来设置所引用库的全部文件;

3.2 直接在非ARM的主机上调试ARM下生成的core文件

  1. sudo gdb-mulitarch ./ARCS ./core
  2. set solib-search-path /opt/arm_lib/:/opt/arm_usr_libs/:Libraries/:Libraries/lib1/:Libraries/lib2/: 设置so的库文件搜索路径, 注意这里的Libraries/:Libraries/lib1/:Libraries/lib2/这三个文件夹是当前可执行文件所需要使用的库文件,且都存放在/usr/local/ti-sdk-am335x-evm/linux-devkit/sysroots/i686-arago-linux/usr/bin/下,推荐放在和可执行文件、core文件的同一级目录,方便调试。
  3. (gdb) set sysroot /opt/arm_lib: 设置root目录,并回车之后,就会开始加载有符号文件的动态库;之后,就可以运行bt查看崩溃堆栈信息;
  4. bt: 查看堆栈信息
  5. i sharedlibrary: 查看哪些动态库有符号文件;
  6. f num: 切换堆栈层;
  7. l: 查看相关代码;

3.3 注意事项

  1. 产生core文件的可执行文件,最好使用debug版本,这样可以更好的进行排查问题;
  2. 最好将可执行文件,core文件,当前可执行文件所依赖的本地库文件放到同一个文件夹下,方便gdb-mutliarch进行set solib-search-path设置;
  3. 若执行以上的2.操作之后,没有分析堆栈信息,可以使用set sysroot /opt/arm_lib来强制分析;

4 在Ubuntu上远程调试ARMv7上的可执行文件

4.1 在ARM目标设备上安装gdbserver

可以多种方法安装gdbserver

  1. 通过sudo apt直接在目标设备上安装gdbserver,如下所示

    • sudo apt update
    • sudo apt install gdbserver
  2. 通过sudo apt先在Ubuntu上安装gdbserver,如下所示

    • sudo apt update
    • sudo apt install gdbserver: 安装gdbserver
    • which gdbserver: 查找到gdbserver的安装位置,找到该文件之后,将它拷贝到ARM系统中,假如在/usr/bin/gdbserver下;
    • scp /usr/bin/gdbserver root@target_device_id:/home/user: 将Ubuntu下的gdbserver拷贝到ARM设备上;
      • 可能遇到的问题:gdbserver: line 1: syntax error: unexpected "("

        • 原因分析: 拷贝到ARMv7上的gdbserver可执行文件并不是ARM架构的二进制文件,而是x86/x86_64架构的二进制文件,导致在ARMv7设备上无法运行;

        要解决该问题,需要获取适用于ARMv7架构的gdbserver,有以下几种方法:

        1. 从ARMv7的软件源安装
        • sudo apt update
        • sudo apt install gdbserver

        若该方法不可用,使用以下方法

        1. 交叉编译gdbserver

          1. 安装必要工具

            • sudo apt install gcc-arm-linux-gnueabi
            • sudo apt install g++-arm-linux-gnueabi (也可直接安装gcc-arm-linux-gnueabihf)
          2. 获取gdbserver源码

             git clone git://sourceware-org/git/binutils-gdb.git
             cd binutils-gdb
            
          3. 配置和编译

             cd binutils-gdb
             ./config --host=arm-linux-gnueabi
             make
            
          4. 将编译好的gdbserver拷贝到ARMv7设备

             scp gdbserver user@target_device_ip:/path/to/targe
            
          5. 在目标设备上设置权限

             ssh user@target_device_ip
             chmod +x /path/to/target/gdbserver
            
        2. 下载预编译的gdbserver

          一些嵌入式Linux发行版(例如:Linar或Yoctor Project)提供适用于ARM的预编译gdbserver

        3. 注意项

          无论使用哪种方法,都可以使用file命令检查gdbserver可执行文件的架构.

           file /path/to/targe/gdbserver
          

          输出应该包含ARM

4.2 在目标设备上设置权限

  • ssh user@target_device_ip: 通过ssh登录可以登录ARM系统;target_device_ip为ARM系统的IP地址,user为ARM系统的用户;
  • chmod +x /home/user/gdbserver: 设置gdbserver为可执行文件;

4.3 在目标设备上启动gdbserver

在目标设别上,通过gdbserver启动我的可执行文件,并指定调试的端口,假设我的可执行文件位于/home/user

  • /home/user/gdbserver :1234 /home/user/my_program

4.5 连接

在Ubuntu主机上启动gdb-multiarch并连接gdbserver

gdb-multiarch my_program

在GDB提示符下,连接到目标设备上的gdbserver

(gdb) target remote target_device_ip:1234

4.6 开始调试

现在已经连接到目标设备,可以使用GDB进行调试, 一下是一些常用命令:

(gdb) break main
(gdb) continue
(gdb) step
(gdb) print variable_name
(gdb) bracetrace
posted @ 2024-07-16 15:42  绍荣  阅读(10)  评论(0编辑  收藏  举报