概念:
GDB(GNU Debugger)是一个用于调试程序的强大工具。它是GNU项目的一部分,支持多种编程语言,包括C、C++等。GDB 提供了一组命令和功能,允许跟踪检查程序的内部状态,跟踪代码的执行过程,以及定位和修复程序中的错误。
gdb和gdb sever
GDB(GNU Debugger)
- gdb 用于本地调试程序。它允许程序员查看程序的运行状态、检查变量和内存、设置断点等,以便在代码中找到和修复问题。
- 使用 gdb 时,你在本地计算机上运行 gdb,并且该调试器直接与正在调试的程序进行交互。
GDB Server(gdbserver)
- gdbserver 是 GDB 的另一部分,用于远程调试。它允许你在目标计算机上运行一个小型的 GDB 服务器,然后在本地计算机上运行gdb 与之连接。
- 通过 gdbserver,你可以在嵌入式系统或远程计算机上调试程序,而不需要将整个 GDB 调试器放在目标系统上。
- 这种分离的方法对于嵌入式系统等资源受限的环境非常有用,允许在目标系统上运行轻量级的 gdbserver,而在开发机上运行完整版的 gdb 进行调试。
作用
- 调试程序: GDB的主要作用是帮助程序员识别和解决程序中的错误(bugs)。它允许开发者在程序执行时停下来,检查变量的值,查看函数调用堆栈,设置断点,并逐步执行代码。
- 变量和内存查看:GDB 允许开发者检查程序运行时的变量的值和内存的内容。这对于理解程序的状态以及发现潜在问题非常有用。
- 设置断点: 开发者可以在程序中设置断点,使得程序在执行到达特定的位置时停下来。这有助于逐步调试程序并检查特定的代码段。
- 单步执行:GDB 允许开发者逐步执行程序,一次执行一行代码或一次执行一个函数。这对于追踪程序的执行流程非常有用。
- 追踪函数调用:GDB 能够跟踪程序中的函数调用,显示函数调用关系,帮助开发者理解程序的执行路径。
- 查找内存错误:GDB 能够帮助开发者查找程序中的内存错误,如访问未分配内存、内存溢出等问题。
- 多线程调试:GDB 支持调试多线程程序,允许开发者查看和调试不同线程的执行状态。
- 核心转储分析:当程序发生崩溃时,GDB 可以分析核心转储文件,帮助开发者定位问题的根本原因。
GDB和IDE差别
- 各有好处,并且IDE在不考虑环境的情况下,更容易上手
- 基于 Linux 服务器等的无图形界面开发,使用 Vim+GDB 可以在任意一台电脑上直接调试,不用花时间安装复杂的 IDE 环境。
主要包含如下区别:
- 命令行界面 vs 图形用户界面
- 功能的可视化和图形化展示
- 集成性和便利性
- 快捷键和工具栏
- 平台和语言支持
总体而言,使用 GDB 和使用 IDE 中的调试工具之间的选择通常取决于个人偏好、项目需求以及开发环境。
gdb 调试段错误代码demo
#include <stdio.h>
void accessInvalidMemory() {
int *ptr = NULL; // 故意将指针设置为NULL
*ptr = 42; // 试图访问NULL指针
}
int main() {
accessInvalidMemory(); // 调用会导致Segmentation fault的函数
return 0;
}
定位流程与操作
@ubuntu:$ gcc -g Segmentation_fault.c -o Segmentation_fault
@ubuntu:GDB_debug$ gdb ./Segmentation_fault -q
Reading symbols from ./Segmentation_fault...done.
(gdb) b main
Breakpoint 1 at 0x617: file Segmentation_fault.c, line 9.
(gdb) list
1 #include <stdio.h>
2
3 void accessInvalidMemory() {
4 int *ptr = NULL; // 故意将指针设置为NULL
5 *ptr = 42; // 试图访问NULL指针
6 }
7
8 int main() {
9 accessInvalidMemory(); // 调用会导致Segmentation fault的函数
10 return 0;
(gdb) r
Starting program: GDB_debug/Segmentation_fault
Breakpoint 1, main () at Segmentation_fault.c:9
9 accessInvalidMemory(); // 调用会导致Segmentation fault的函数
(gdb) s
accessInvalidMemory () at Segmentation_fault.c:4
4 int *ptr = NULL; // 故意将指针设置为NULL
(gdb) n
5 *ptr = 42; // 试图访问NULL指针
(gdb) n
Program received signal SIGSEGV, Segmentation fault.
0x000055555555460a in accessInvalidMemory () at Segmentation_fault.c:5
5 *ptr = 42; // 试图访问NULL指针
(gdb) bt
#0 0x000055555555460a in accessInvalidMemory () at Segmentation_fault.c:5
#1 0x0000555555554621 in main () at Segmentation_fault.c:9
(gdb) n
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
(gdb) n
The program is not being run.
结论
查看调用栈
(gdb) bt
#0 0x000055555555460a in accessInvalidMemory () at Segmentation_fault.c:5
#1 0x0000555555554621 in main () at Segmentation_fault.c:9
问题出在:函数accessInvalidMemory,代码的第五行
说明与解释:
- bt是backtrace的缩写,可以查看调用栈
- r是run的缩写
- n是next的缩写
- s是step的缩写
- b 是break的缩写
- quit 退出gdb调试,缩写为q
为什么不一直用next?还用step?
- next执行当前函数的所有指令,而step可以让进入段错误函数后再第一行停下来,可以定位到具体到某一行出现的问题
GDB多线程调试
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *thread_function(void *arg) {
for (int i = 0; i < 5; ++i) {
printf("Thread %ld: Iteration %d\n", (long)arg, i);
sleep(1);
}
return NULL;
}
int main() {
pthread_t thread1, thread2;
// 创建两个线程
pthread_create(&thread1, NULL, thread_function, (void *)1);
pthread_create(&thread2, NULL, thread_function, (void *)2);
// 等待线程结束
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
return 0;
}
编译
gcc -g multithread_demo.c -o multithread_demo -lpthread
执行
gdb ./mu