赞
踩
GDB调试的三种方式:
参考资料
实践工程中,程序基本都是多进程,由于gdb命令来切换线程、打断点等操作较为麻烦,一般很少用这种方式直接调试。
目标板gdbserver+主机gdb远程调试的方式,比较适合目标板性能受限,只能提供gdbserver功能。
具体使用:参考章节二
借助VSCode+gdbserver的方式可以直接在设备程序运行的情况下,通过VSCode工程代码来打断点调试。
核心思路:调试功能/接口,通过VSCode在对应的代码处打上断点即可。(省去加调试打印和重复编译)
准备内容:(编译工具链、代码、带g程序)、(VSCode、WSL/SSH、GDB插件)、gdbserver。
打开VSCode 配置Debug launch.json
{ // 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。 // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { "name": "arm-gdb_V2", //配置名称,显示在配置下拉菜单中 "type": "cppdbg", //配置类型 "request": "launch", //请求配置类型,可以是启动或者是附加 "program": "${workspaceFolder}/main/Bin/O1/Mystical_gdb", //程序可执行文件的完整路径,${workspaceFolder}表示VSCode 当前打开源码的根目录 "args": [], //传递给程序的命令行参数 "stopAtEntry": false,//可选参数,如果为true,调试程序应该在入口(main)处停止 "cwd": "${workspaceFolder}", //目标的工作目录 "miDebuggerPath": "arm-fslc-linux-gnueabi-gdb", // 交叉工具的gdb "miDebuggerServerAddress": "10.1.30.142:3456", // 调试设备的IP端口 "environment": [], //表示要预设的环境变量 "externalConsole": false,//如果为true,则为调试对象启动控制台 "MIMode": "gdb",//要连接到的控制台启动程序 "setupCommands": [ //为了安装基础调试程序而执行的一个或多个GDB/LLDB命令 { "description": "为 gdb 启用整齐打印", "text": "-enable-pretty-printing", "ignoreFailures": true }] }, { "name": "arm-gdb_V3", //配置名称,显示在配置下拉菜单中 "type": "cppdbg", //配置类型 "request": "launch", //请求配置类型,可以是启动或者是附加 "program": "${workspaceFolder}/Bin/O1/OBU/CloudService_gdb", //程序可执行文件的完整路径,${workspaceFolder}表示VSCode 当前打开源码的根目录 "args": [], //传递给程序的命令行参数 "stopAtEntry": false,//可选参数,如果为true,调试程序应该在入口(main)处停止 "cwd": "${workspaceFolder}", //目标的工作目录 "miDebuggerPath": "arm-fslc-linux-gnueabi-gdb",// 交叉工具的gdb "miDebuggerServerAddress": "10.1.30.142:3456", // 调试设备的IP端口 "environment": [], //表示要预设的环境变量 "externalConsole": false,//如果为true,则为调试对象启动控制台 "MIMode": "gdb",//要连接到的控制台启动程序 "setupCommands": [ //为了安装基础调试程序而执行的一个或多个GDB/LLDB命令 { "description": "为 gdb 启用整齐打印", "text": "-enable-pretty-printing", "ignoreFailures": true }] }, { "name": "arm-gdb_O2_V2", //配置名称,显示在配置下拉菜单中 "type": "cppdbg", //配置类型 "request": "launch", //请求配置类型,可以是启动或者是附加 "program": "${workspaceFolder}/main/Bin/O2/Mystical_gdb", //程序可执行文件的完整路径,${workspaceFolder}表示VSCode 当前打开源码的根目录 "args": [], //传递给程序的命令行参数 "stopAtEntry": false,//可选参数,如果为true,调试程序应该在入口(main)处停止 "cwd": "${workspaceFolder}", //目标的工作目录 "miDebuggerPath": "arm-linux-gnueabi-gdb",// 交叉工具的gdb "miDebuggerServerAddress": "10.1.36.46:3456", // 调试设备的IP端口 O2设备 miniOBU没有 网口 需要通过windows进行端口转发, 10.1.36.46为PC的IP "environment": [], //表示要预设的环境变量 "externalConsole": false,//如果为true,则为调试对象启动控制台 "MIMode": "gdb",//要连接到的控制台启动程序 "setupCommands": [ //为了安装基础调试程序而执行的一个或多个GDB/LLDB命令 { "description": "为 gdb 启用整齐打印", "text": "-enable-pretty-printing", "ignoreFailures": true }] }] }
配置完成后显示配置选项如下
gdbserver : 3456 ./app
这里gdbserver监听端口3456和VSCode配置文件要对应起来。若程序为运行 状态可以 使用选项 --attach [pid]
采用附着的方式。
在代码上打断点,点击对应的调试配置项启动调试,既可开始调试,示例:
直接参考示例:
ulimit -c unlimited #设置开启core文件生成
mount -t nfs -o nolock 192.168.3.65:/home/mayue/nfs /mnt; #设备支持挂载时可用,可省略
echo "/mnt/core-%e-%p-%t" > /proc/sys/kernel/core_pattern #设置core文件生成路径和命名规则
方式一:将上面内容加到启动脚本中,要放在启动程序之前
方式二:手动输入下面内容,然后重新启动程序
有问题的程序运行后,产生“段错误 (核心已转储)”时生成的具有堆栈信息和调试信息的文件。
编译时需要加 -g 选项使程序生成调试信息: gcc -g core_test.c -o core_test
注意:实践项目中,会同时编译出两个程序,一个带g,一个不带g。设备运行不带g程序,产生core文件后再用带g的程序来调试。
(1) core文件开关
①使用 ulimit -c 查看core开关,如果为0表示关闭,不会生成core文件;
②使用 ulimit -c [filesize] 设置core文件大小,当最小设置为4之后才会生成core文件;
③使用 ulimit -c unlimited 设置core文件大小为不限制,这是常用的做法;
④如果需要开机就执行,则需要将这句命令写到 /etc/profile 等文件。
(2) core文件命名和保存路径
①core文件有默认的名称和路径,但为了方便,我们通常会自己命名和指定保存路径;
②可以通过 /proc/sys/kernel/core_pattern 设置 core 文件名和保存路径,方法如下:
echo "/corefile/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
命名的参数列表:
%p - insert pid into filename 添加pid
%u - insert current uid into filename 添加当前uid
%g - insert current gid into filename 添加当前gid
%s - insert signal that caused the coredump into the filename 添加导致产生core的信号
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
%h - insert hostname where the coredump happened into filename 添加主机名
%e - insert coredumping executable name into filename 添加命令名。
(1)方法1: gdb [exec file] [core file] 然后执行bt看堆栈信息:
(2)方法②: gdb -c [core file],然后 file [exec file],最后再使用 bt 查看错误位置:
在进行gdb+core调试的过程中,经常遇到的问题或经常用到的命令:
set print element 0
bt
、(bt bull
打印栈帧信息的同时,打印出局部变量的值)frame n
命令:
gdb ./myapp core-file #启动调试
bt full #打印所有堆栈信息
frame 0 #跳转到指定栈层
info frame # i frame #查看当前程序栈的信息
info args #查看当前程序栈的参数和参数内容
info locals #查看当前程序栈的局部变
p arg #打印变量
示例:
# 实践:打印出原始入参内容/局部变量,这里打印入参再套入程序中模拟是否可以复现问题
frame 1 #先指定frame
print *(struct lh_table *)t
print *(Lane_t *)cur_lane
print *(Lane_t *) 0x75a32268
通常情况下,在调试的过程中,我们需要查看某个变量的值,以分析其是否符合预期,这个时候就需要打印输出变量值。
命令 | 作用 |
---|---|
whatis variable | 查看变量的类型 |
ptype variable | 查看变量详细的类型信息 |
info variables var | 查看定义该变量的文件,不支持局部变量 |
打印字符串
使用x/s
命令打印ASCII字符串,如果是宽字符字符串,需要先看宽字符的长度 print sizeof(str)
。
如果长度为2,则使用x/hs
打印;如果长度为4,则使用x/ws
打印。
命令 | 作用 |
---|---|
x/s str | 打印字符串 |
x/s 0x60000b32e2f6 | 根据某个地址查看字符串内容 |
set print elements 0 | 打印不限制字符串长度/或不限制数组长度 |
call printf(“%s\n”,xxx) | 这时打印出的字符串不会含有多余的转义符 |
printf “%s\n”,xxx | 同上 |
打印数组
命令 | 作用 |
---|---|
print *array@10 | 打印字符串 |
print array[60]@10 | 打印array数组下标从60开始的10个元素,即第60~69个元素 |
set print array-indexes on | 打印数组元素时,同时打印数组的下标 |
打印指针
命令 | 作用 |
---|---|
print ptr | 查看该指针指向的类型及指针地址 |
print /x ptr | 16进制打印该变量的值 |
print *(struct xxx *)ptr | 查看指向的结构体的内容(ptr也可以是地址、可以一直往下套–给个示例) |
打印指定内存地址的值
使用x
命令来打印内存的值,格式为x/nfu addr
,以f
格式打印从addr
开始的n
个长度单元为u
的内存值。
命令 | 作用 |
---|---|
x/8xb array | 以16进制打印数组array的前8个byte的值 |
x/8xw array | 以16进制打印数组array的前16个word的值 |
打印局部变量
命令 | 作用 |
---|---|
info locals | 打印当前函数局部变量的值 (bt full也可以打印出局部变量) |
backtrace full | 打印当前栈帧各个函数的局部变量值,命令可缩写为bt |
bt full n | 从内到外显示n个栈帧及其局部变量 |
bt full -n | 从外向内显示n个栈帧及其局部变量 |
打印结构体
命令 | 作用 |
---|---|
set print pretty on | 每行只显示结构体的一名成员 |
set print null-stop | 不显示’\000’这种 |
参考:Linux中gdb 查看core堆栈信息1,其他内容:显示源代码、源代码内存
切换当前栈
frame n
或
f n
n是一个从0开始的整数,是栈中的层编号。比如:frame 0,表示栈顶,frame 1,表示栈的第二层。
up
表示向栈的上面移动n层,可以不打n,表示向上移动一层。
down
表示向栈的下面移动n层,可以不打n,表示向下移动一层。
上面的命令,都会打印出移动到的栈层的信息。如果你不想让其打出信息。你可以使用这三个命令: select-frame 对应于 frame命令。
up-silently 对应于 up 命令。
down-silently 对应于 down 命令。
查看当前栈层的信息
frame 或 f
会打印出这些信息:栈的层编号,当前的函数名,函数参数值,函数所在文件及行号,函数执行到的语句。
info frame 或 info f
这个命令会打印出更为详细的当前栈层的信息,只不过,大多数都是运行时的内存地址。比如:函数地址,调用函数的地址,被调用函数的地址,目前的函数是由什么样的程序语言写成的、函数参数地址及值、局部变量的地址等等。
x/10x $sp
:查看当前程序栈的内容,打印stack的前10个元素info frame
: 查看当前程序栈的信息info args
:查看当前程序栈的参数info locals
: 查看当前程序栈的局部变量info registers
:查看当前寄存器的值(不包括浮点寄存器) ,info all-registers
(包括浮点寄存器)info catch(exception handlers)
: 查看当前栈帧中的异常处理器nm和gdb查看全局变量的地址是一致的
为什么gdb显示的函数地址和nm不一样?
因为 nm 显示函数开始的地址,而 gdb堆栈跟踪显示函数内部执行的确切位置。确切地说,它应该是堆栈帧中的返回地址,即当堆栈中位于其上方的函数返回时,指向该函数中要执行的下一条指令的指针。
请注意,如果您只是询问 gdb对于通过评估函数指针表达式的函数指针,它应该给出与 nm 相同的地址.
查看函数地址
根据函数地址 找 对应的函数名
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。