gdb调试

问题:

arm-mol-linux-uclibcgnueabihf-gdb: loadlocale.c:130: _nl_intern_locale_data: Assertion `cnt < (sizeof (_nl_value_type_LC_TIME) / sizeof (_nl_value_type_LC_TIME[0]))' failed.
Aborted (core dumped)
在运行GDB前临时修改locale环境变量,可以有效避免此问题:
export LC_ALL=C
export LANG=C

在启动 gdb 时传递参数

使用 --args 选项启动 gdb

gdb --args ./main 10001 /home/meian/work/MyProject/study/reator/reatorHttp/readme/

在 gdb 内部设置参数

启动 gdb 后,使用 set args 命令设置参数,然后运行

(gdb) set args 10001 /home/meian/work/MyProject/study/reator/reatorHttp/readme/
(gdb) run

gdbserver 使用方法

PC 运行 gdb

mips-linux-gnu-gdb [PC 端应用程序路径]

设置动态库搜索路径

​ 应用程序往往动态链接 libc、ld 等库,当运行于动态库中 break 时(比如 memcpy)往往无法跟踪到当前 pc 所在位置。动态库地址可由 GDB 结合目标装载信息重定位;无法解析 PC 的常见原因是缺少与目标二进制匹配的库或调试符号。因此应先提供与目标 rootfs/ABI 对应的 sysroot,再按需设置主机侧库搜索路径。方法是

# 将路径替换为与目标系统匹配的 rootfs,选择 glibc 或 uClibc 对应的一套
set sysroot /path/to/target-rootfs
set solib-search-path /path/to/target-rootfs/lib:/path/to/target-rootfs/usr/lib

--------------------------------------------
sdk的rootfs里的库
set sysroot /home/meian/work/SDK/JZ_SDK/ISVP-A1-1.6.1-20230911/software/sdk/Ingenic-SDK-A1-1.6.1-20230911/resource/rootfs/root-uclibc-toolchain720_229

set solib-search-path /home/meian/work/SDK/JZ_SDK/ISVP-A1-1.6.1-20230911/software/sdk/Ingenic-SDK-A1-1.6.1-20230911/resource/rootfs/root-uclibc-toolchain720_229/lib
---------------------------

set sysroot /home/meian/work/SDK/JZ_SDK/ISVP-A1-1.7.0-20250407/software/zh/board/Ingenic-SDK-A1-1.7.0-20250407-zh/resource/rootfs/root-uclibc-toolchain720_229

set solib-search-path \
/home/meian/work/SDK/JZ_SDK/ISVP-A1-1.7.0-20250407/software/zh/board/Ingenic-SDK-A1-1.7.0-20250407-zh/resource/rootfs/root-uclibc-toolchain720_229/lib

​ Unix 主机上多个搜索路径用英文冒号 : 分隔。下面的 glibc/uClibc 目录仅展示不同工具链路径,实际调试必须选择与目标一致的一套,不能把不同 libc 的库混放在同一个搜索路径中。

  • glibc 的动态库在 toolchain 文件夹下的相对目录为:mips-linux-gnu/libc/lib 与mips-linux-gnu/lib

  • uclibc 的动态库在 toolchain 文件夹下的相对目录为:mips-linux-uclibc/libc/lib 与 mips-linux-uclibc/lib

开发板启动 gdbserver

gdbserver [PC 机 IP:端口,与步骤 2 中端口需一致] [应用程序路径]
./gdbserver_a1n 192.168.10.27:1234 nvr_test

链接开发板

target remote [开发板 IP:端口]

GDB 找不到带调试信息的库

  • 你要确保运行时使用的 /tmp/nfs/lib//libmafs.so 与带有 debug 信息的版本是同一个文件,或者二者在不同路径导致 GDB 只看到运行时库,却找不到符号对应的调试信息库。

    可以在 GDB 中设置正确的搜索路径:

    set solib-search-path ~/work/MyProject/nvr_nfs/test/
    set auto-solib-add on
    

查看当前栈帧的变量

使用 info locals 查看当前函数的局部变量

(gdb) info locals 
pts = 0
tmp = "\000\000\000\000\000\000\000"
u32SetLen = 4
frmLen = 0
frame = 0xe43cf8
__FUNCTION__ = "PutOneNetAFrameToBuffer"
systime = {year = 24 '\030', month = 11 '\v', mday = 28 '\034', 
  hour = 16 '\020', minute = 5 '\005', second = 57 '9', 
  timestamp = 1732809957, week = 4 '\004', reserve = 245 '\365'}

使用info frame查看当前栈帧的所有信息

(gdb) info frame
Stack level 2, frame at 0xa8685c38:
 pc = 0x7783c in FrameData::InitData (./encode/include/BufferManage.h:148); 
    saved pc = 0x76b80
 called by frame at 0xa8685cc0, caller of frame at 0xa8685c08
 source language c++.
 Arglist at 0xa8685c34, args: this=0xee9da0, datalen=7492
 Locals at 0xa8685c34, Previous frame's sp is 0xa8685c38
 Saved registers:
  r11 at 0xa8685c30, lr at 0xa8685c34

p 打印变量

  • 以16进制打印内容
p/x <variable>

x (examine memory)

​ 功能:原始字节以十六进制打出来

  • 起始地址(spsData 指针)
  • 要看的字节数(这里是 21)
(gdb) x/21xb spsData
0x7ffff79d7234:	0x67	0x64	0x00	0x28	0xac	0xd2	0x01	0xe0
0x7ffff79d723c:	0x08	0x9f	0x96	0x10	0x00	0x00	0x3e	0x80
0x7ffff79d7244:	0x00	0x0c	0x35	0x00	0x40

x    examine memory

/21  取 21 个单元

x    以十六进制显示

b    每个单元大小是 1 byte

n (next)命令:

​ 功能:执行下一行代码。如果当前行是一个函数调用,它会把整个函数当作一条语句执行完,不会进入函数内部逐行调试,而是直接执行完函数调用后停在下一行代码处。

s (step)命令:

​ 功能:同样也是执行下一行代码,但如果当前行是一个函数调用,它会进入函数内部,开始逐行调试函数里的代码,方便查看函数内部具体的执行细节。

跳出循环until, /wach、

gdb 应用程序与源码不在一起:

在 gdb 中添加源码搜索路径:

(gdb) directory /tmp/source

验证源码路径是否正确: show directories 查看源码搜索目录,info sources 查看调试信息记录的源文件;再用 list 文件名:行号 验证是否实际找到了源码。构建时绝对路径与当前路径不同时,可用 set substitute-path 映射。

(gdb) show directories
(gdb) info sources

查看socket对应的ip地址

 2263 root      1:28 {IdleProcess} ./FocusAppMc6830v1
 2278 root      0:00 [jpegenc]
 2392 root      0:00 [kworker/0:2]
 2393 root      0:00 -sh
 2397 root      0:00 ps
~ # ls -l /proc/2263/fd
total 0
lrwx------    1 root     root            64 Jan 14 15:25 0 -> /dev/console
lrwx------    1 root     root            64 Jan 14 15:25 1 -> /dev/console
l-wx------    1 root     root            64 Jan 14 15:25 10 -> pipe:[3630]
lrwx------    1 root     root            64 Jan 14 15:25 11 -> /dev/sda1
lr-x------    1 root     root            64 Jan 14 15:25 12 -> /var/hi_system_config.xml
lr-x------    1 root     root            64 Jan 14 15:25 13 -> pipe:[923]
l-wx------    1 root     root            64 Jan 14 15:25 14 -> pipe:[923]
lr-x------    1 root     root            64 Jan 14 15:25 15 -> pipe:[924]
l-wx------    1 root     root            64 Jan 14 15:25 16 -> pipe:[924]
~ # cat /proc/net/tcp 
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
   0: 00000000:1F40 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 3637 1 c92c2280 100 0 0 10 0                              
   1: 00000000:5BA0 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 2945 1 c92c0000 100 0 0 10 0                              
   2: 00000000:1F48 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 929 1 c8a06780 100 0 0 10 0                               
   3: 00000000:0050 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 2946 1 c92c05c0 100 0 0 10 0                              
   4: 00000000:0017 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 393 1 c8a00000 100 0 0 10 0                               
   5: DE01A8C0:9CCC C801A8C0:1F40 01 00000000:00000000 00:00000000 00000000     0        0 3633 2 c92c6d40 21 4 0 10 -1                              
   6: 0100007F:5BA0 0100007F:A2D2 08 00000000:00000000 00:00000000 00000000     0        0 798 1 c92c0b80 21 4 28 10 -1                              
   7: 0100007F:5BA0 0100007F:A31E 08 00000000:00000000 00:00000000 00000000     0        0 886 1 c92c5640 21 4 28 10 -1                              
   8: 0100007F:5BA0 0100007F:A2BC 08 00000000:00000000 00:00000000 00000000     0        0 779 1 c8a04500 21 4 28 10 -1                              
   9: 0100007F:5BA0 0100007F:A340 01 00000000:00000000 00:00000000 00000000     0        0 3632 1 c8a005c0 21 4 31 10 -1                             

gdb不处理信号

(gdb) handle SIGINT pass nostop noprint
handle SIGABRT stop print nopass
handle SIGSEGV stop print nopass
handle SIGILL  stop print nopass
handle SIGBUS  stop print nopass
  • pass —— 将 SIGINT 转发给被调试程序
  • nostop —— 不在 GDB 层面停下来
  • noprint—— 不在 GDB 层面打印任何信息

推荐启动模板

在~/.gdbinit 里配置通用的gdbinit

# ========== 基本体验 ==========
set pagination off
set confirm off
set print thread-events off
set disassemble-next-line on

# 关键:共享库符号未加载时也允许设置断点(变为 pending)
set breakpoint pending on

# ========== 信号策略 ==========
# Ctrl-C:你要让程序自己处理就用 pass nostop;想随时停住就改成 stop print nopass
handle SIGINT  pass nostop noprint

# 致命信号:停住并打印,不交给程序(避免直接死掉)
handle SIGSEGV stop print nopass
handle SIGABRT stop print nopass
handle SIGBUS  stop print nopass
handle SIGILL  stop print nopass
handle SIGFPE  stop print nopass

# ========== C++ 异常/终止路径 ==========
# 所有 throw(可能很吵,必要时再开)
# break __cxa_throw
# break __cxa_rethrow

# terminate/abort(强烈建议)
break __cxa_terminate
break std::terminate
break abort

# 这条在你环境里很可能有效(你曾命中过)
break std::terminate()@plt

# 可选:如果你的 libstdc++ 有 verbose handler
break __gnu_cxx::__verbose_terminate_handler

add-auto-load-safe-path /home/meian/work/MyProject/test_nvr/nvr_36chn
  1. set pagination off (关闭分页显示)
    • 作用:当输出内容超过一屏时,GDB 默认会暂停并提示 --Type <RET> for more, q to quit, c to continue without paging--。
  2. set print thread-events off (关闭线程事件打印)
    • 作用:默认情况下,每当程序创建一个新线程或销毁一个线程时,GDB 都会在控制台打印一条消息(例如 [New Thread 0x... (LWP ...)] 或 [Thread ... exited])。
  3. set breakpoint pending on (开启待定断点)
    • 作用:允许你对尚未加载的共享库(.so)中的函数设置断点。
    • 为什么在嵌入式开发中是必须的:
      • 动态加载库:你的项目引用了 ZLToolkit 和 P2P SDK。这些库通常是以动态库形式存在的。
      • 先设置后运行:在程序刚启动但还没运行到加载这些 .so 文件的代码时,如果你尝试设置断点,GDB 可能会报错说“找不到函数”。开启此选项后,GDB 会先记录下这个断点,等程序运行并加载了对应的库后,再自动激活断点。
  4. set disassemble-next-line on
    • 让 GDB 在每次程序暂停(例如停在断点或单步执行后)时,自动显示即将执行的下一行源代码对应的汇编指令

在项目的.gdbinit

echo [loaded nvr_36 project .gdbinit]\n
# ========== sysroot ==========
set sysroot /home/meian/work/SDK/JZ_SDK/ISVP-A1-1.7.0-20250407/software/zh/board/Ingenic-SDK-A1-1.7.0-20250407-zh/resource/rootfs/root-uclibc-toolchain720_229

# ========== 搜索共享库 ==========
set solib-search-path \
   /home/meian/work/SDK/JZ_SDK/ISVP-A1-1.7.0-20250407/software/zh/board/Ingenic-SDK-A1-1.7.0-20250407-zh/resource/rootfs/root-uclibc-toolchain720_229/lib:\
  /home/meian/work/SDK/JZ_SDK/ISVP-A1-1.7.0-20250407/software/zh/board/Ingenic-SDK-A1-1.7.0-20250407-zh/resource/rootfs/root-uclibc-toolchain720_229/usr/lib

开启coredump

#确保内核中有开启CONFIG_COREDUMP,开启了这个目录就存在
ls /proc/sys/kernel/core_pattern

# 1. 打开 core
ulimit -c unlimited

# 2. 设置路径
echo "/tmp/core.%e.%p.%t" > /proc/sys/kernel/core_pattern

# 3. 运行程序
./nvr_app