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