11-12 调试利器 GDB
阅读量
0
阅读人次
0
使用GDB Server和VS Code
在嵌入式板子上,直接使用gdb,需要记住各种gdb命令,时间久了就忘的差不多了。在条件许可的情况下,GDB Server和VS Code配合使用,会非常方便调试。
这里加上了useExtendedRemote
和setupCommands
两个字段,用到了GDB Server的extended-remote模式,使得我们可以不用每次 在板子上通过GDB Server运行程序或者附加到已经运行的程序,然后再使用VS Code + GDB调试。而是直接通过VSCode集成方便的启动调试程序。
{
"version": "0.2.0",
"configurations": [
{
"name": "AppDebug",
"type": "cppdbg",
"request": "launch",
"miDebuggerPath": "arm-linux-gnueabihf-gdb",
"miDebuggerServerAddress": "192.168.8.127:8080",
"program": "${workspaceFolder}/build/GateFace",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"logging": {
"engineLogging": false
},
"MIMode": "gdb",
"useExtendedRemote": true,
"setupCommands": [
{
"text": "set remote exec-file /sdcard/GateFace",
"description": "设置嵌入式单板加载的程序",
"ignoreFailures": false
}
]
}
]
}
然后在板子上执行:
./gdbserver --multi 192.168.8.127:8080
就可以很方便的使用VS Code的 运行和调试 功能了。
如果希望手动在单板上运行调试程序或附加到已经运行的调试程序,useExtendedRemote
和setupCommands
两个字段去除即可。
target extended-remote 192.168.8.127:8080
set remote exec-file /sdcard/GateFace
源码编译
下载GDB源码包,这里以 gdb-14.1.tar.xz
为例。下载后进行解压然后进入源码目录:
tar xvf ./gdb-14.1.tar.xz
cd gdb-14.1/
首先构建gdb(构建出来的 aarch64-linux-gnu-gdb 是运行在 PC 环境下的,所以使用的编译器不是交叉编译工具,而是本地 gcc ):
sudo apt install libgmp-dev libmpfr-dev texinfo
./configure --enable-static=yes --target=aarch64-linux-gnu --prefix=/opt/aarch64-v01c01-linux-gnu-gcc/lib/gdb-14.1
make -j6 && make install
然后构建运行于目标调试板的 gdbserver(可以删除 gdb-14.1 目录,重新解压源码以保持环境干净):
export CC=aarch64-linux-gnu-gcc
./configure --enable-static=yes --host aarch64-linux-gnu --target=aarch64-linux-gnu --disable-gdb --prefix=/opt/aarch64-v01c01-linux-gnu-gcc/lib/gdb-14.1
make all-gdbserver -j6 && make install-gdbserver
在使用时 gdb 会报错:
Remote 'g' packet reply is too long (expected 788 bytes, got 796 bytes): ......
如果出现此报错,则降低gdb版本尝试。推荐使用和交叉编译工具链gcc版本相同的gdb源码。
11 调试利器GDB(上)
什么是GDB?
GNU项目中的调试器(gnu debuger);能够跟踪程序的执行,也能够恢复程序崩溃前的状态。
为什么需要GDB?
软件不是一次性开发完成的(是软件就有bug,是程序就有问题);调试是软件开发过程中不可或缺的技术(调试工具很重要)。
GDB的常规应用
- 自定义程序的启动方式(指定影响程序运行的参数)
- 设置条件断点(在条件满足时暂停程序的执行)
- 回溯检查导致程序异常结束的原因(Core Dump)
- 动态改变程序执行流(定位问题的辅助方式)
GDB的启动方式
- 直接启动
- gdb
- gdb test.out
- gdb test.out core
- 动态连接:
gdb test.out pid
GDB应用示例一
kylin@hp-vm:~$ gdb //启动
(gdb) file test.out //载入目标程序,等价于gdb test.out
(gdb) set args arg1 arg2 //设置命令行参数
(gdb) run //执行目标程序
GDB应用示例二
kylin@hp-vm:~$ gdb //启动
(gdb) attach pid //链接到目标进程,链接成功后目标进程将停止执行。等价于gdb test.out pid
(gdb) continue //恢复执行
使用GDB进行断点调试
-
断点类型
- 软件断点:由非法指令异常实现(软件实现)
- 硬件断点:由硬件特性实现(数量有限)
- 数据断点:由硬件特性实现(数量有限)
-
软件断点的相关操作
- 通过函数名设置断点
- break func_name[if var = value]
- tbreak func_name[if var = value]
- 通过文件名行号设置断点
break file_name:line_num[if var = value]
tbreak file_name:line_num[if var=value]
- 通过函数名设置断点
软件断点的相关操作
操作 | 命令 |
---|---|
断点查看 | info breakpoints |
断点删除 | delete 1 2 n delete breakpoints |
断点状态改变 | enable 1 2 n enable breakpoints disable 1 2 n disable breakpoints |
调试时的常用操作
操作 | 命令 |
---|---|
变量查看 | print name |
变量设置 | set var name = value |
执行下一行代码 | next |
连续执行n行代码 | next n |
执行进入函数 | step |
强制当前函数返回 | return [value] |
运行至当前函数返回 | finish |
执行至目标行 | until line |
跳转执行 | jump line |
硬件断点及其应用
- 当代码位于只读存储器(Flash)时,只能通过硬件断点调试
- 硬件断点需要硬件支持,数量有限
- GDB中通过hbreak命令支持硬件断点
- hbreak与break使用方式完全一致
编程实验:使用GDB进行断点调试
//func.c
#include <stdio.h>
int* g_pointer;
void func() {
*g_pointer = (int)"D.T.Software";
return;
}
//test.c
#include <stdio.h>
#include <unistd.h>
extern int* g_pointer;
extern void func();
void test_1() {
printf("test_1() : %p\n", test_1);
}
void test_2() {
printf("test_2() : %p\n", test_2);
}
void test_3() {
printf("test_3() : %p\n", test_3);
}
int main(int argc, char *argv[]) {
typedef void(TFunc)();
TFunc* fa[] = {test_1, test_2, test_3};
int i = 0;
printf("main() : begin...\n");
for(i=0; i<argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
for(i=0; i<100; i++) {
fa[i%3]();
sleep(argc > 1);
}
printf("g_pointer = %p\n", g_pointer);
func();
printf("main() : end...\n");
return 0;
}
小结
- GDB是GNU项目中的调试器,能够跟踪或改变程序的执行
- GDB能够根据Core Dump回溯检查导致程序异常结束的原因
- GDB同时支持软件断点,硬件断点和数据断点
- GDB是嵌入式开发中必须掌握的重要工具
12 调试 利器GDB(下)
数据断点
- GDB中支持数据断点的设置
- watch命令用于监视变量是否被改变(本质为硬件断点)
- watch命令的用法:watch var_name
GDB中的内存查看
- GDB中可以检查任意内存区域中的数据
- 命令语法: x /Nuf expression
- N - 需要打印的单元数
- u - 每个单元的大小
- f - 数据打印的格式
示例 : x /4bx 0x804a024
-
x命令中参数u对应的单位
格式 打印方式 b 单字节 h 双字节 w 四字节 g 八字节 -
GDB中的打印格式
格式 打印方式 x 十六进制 d 有符号十进制 u 无符号十进制 o 八进制 t 二进制 a 地址 c 字符 f 浮点数 -
示例:判断系统大小端
(gdb) set var=1
(gdb)print /a &var
$1 = 0x804a024<var>
(gdb) x /4bx 0x804a024
0x804a024<var>: 0x01 0x00 0x00 0x00
(gdb) x /1bx 0x804a024
0x804a024<var>:0x01
编程实验一:变量断点和内存查看
//watch.c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
int g_var = 0;
void* thread_func(void* args) {
sleep(5);
g_var = 1;
}
int main() {
int i = 0;
pthread_t tid = 0;
pthread_create(&tid, NULL, thread_func, NULL);
for(i=0; i<10; i++) {
printf("g_var = %d\n", g_var);
sleep(1);
}
}