目录

gdb调试程序常用指令

1. 启动GDB

2. 设置断点和运行程序

3. 单步执行和检查变量

4. 观察点(Watchpoints)

5. 查看调用栈和帧

6. 修改变量值

7. 调试核心文件

8. 调试多线程程序

多线程调试深入分析

1. 基本概念

2. 常用命令

3. 示例代码

4. 调试多线程程序

5. 综合调试示例

gdb 调试工具的所有指令以及解释


gdb调试程序常用指令

GDB(GNU Debugger)是一个功能强大的调试工具,广泛用于调试C、C++等编程语言的程序。深入理解GDB的使用可以大大提高程序开发和调试的效率。下面我们通过多个具体的例子来详细介绍GDB的使用方法和技巧。

1. 启动GDB

首先,确保编译时使用了 -g 选项以包含调试信息:

$ gcc -g example.c -o example

启动GDB:

$ gdb example

2. 设置断点和运行程序

示例代码:

#include <stdio.h>

void greet() {
    printf("Hello, World!\n");
}

int main() {
    greet();
    printf("Goodbye, World!\n");
    return 0;
}

在main函数入口设置断点并运行:

(gdb) break main
Breakpoint 1 at 0x40052e: file example.c, line 9.
(gdb) run
Starting program: /path/to/example

Breakpoint 1, main () at example.c:9
9       greet();

3. 单步执行和检查变量

单步执行并检查变量:

(gdb) step
greet () at example.c:5
5       printf("Hello, World!\n");

(gdb) step
Hello, World!
6   }

(gdb) step
main () at example.c:10
10      printf("Goodbye, World!\n");

(gdb) print 0
$1 = 0

4. 观察点(Watchpoints)

示例代码:

#include <stdio.h>

int counter = 0;

void increment() {
    counter++;
}

int main() {
    increment();
    increment();
    return 0;
}

在变量counter上设置观察点:

(gdb) break main
Breakpoint 1 at 0x40052e: file example.c, line 11.
(gdb) run
Starting program: /path/to/example

Breakpoint 1, main () at example.c:11
11      increment();

(gdb) watch counter
Hardware watchpoint 2: counter

(gdb) continue
Continuing.

Hardware watchpoint 2: counter

Old value = 0
New value = 1
increment () at example.c:6
6       counter++;

5. 查看调用栈和帧

查看调用栈和切换帧:

(gdb) backtrace
#0  increment () at example.c:6
#1  0x000000000040052f in main () at example.c:12

(gdb) frame 0
#0  increment () at example.c:6
6       counter++;

(gdb) frame 1
#1  0x000000000040052f in main () at example.c:12
12      increment();

6. 修改变量值

示例代码:

#include <stdio.h>

int main() {
    int a = 5;
    int b = 10;
    printf("a + b = %d\n", a + b);
    return 0;
}

在运行时修改变量值:

(gdb) break main
Breakpoint 1 at 0x40052e: file example.c, line 5.
(gdb) run
Starting program: /path/to/example

Breakpoint 1, main () at example.c:5
5       int a = 5;

(gdb) next
6       int b = 10;

(gdb) print a
$1 = 5

(gdb) set var a = 20

(gdb) print a
$2 = 20

(gdb) continue
a + b = 30

7. 调试核心文件

生成核心文件:

$ ulimit -c unlimited
$ ./example
Segmentation fault (core dumped)

使用GDB调试核心文件:

$ gdb example core

示例代码:

#include <stdio.h>

int main() {
    int *p = NULL;
    *p = 5;  // 引发段错误
    return 0;
}

调试核心文件查看崩溃原因:

(gdb) run
Starting program: /path/to/example

Program received signal SIGSEGV, Segmentation fault.
0x0000000000400526 in main () at example.c:5
5       *p = 5;
(gdb) backtrace
#0  0x0000000000400526 in main () at example.c:5

8. 调试多线程程序

示例代码:

#include <pthread.h>
#include <stdio.h>

void* thread_func(void* arg) {
    printf("Thread %d\n", *(int*)arg);
    return NULL;
}

int main() {
    pthread_t t1, t2;
    int id1 = 1, id2 = 2;
    pthread_create(&t1, NULL, thread_func, &id1);
    pthread_create(&t2, NULL, thread_func, &id2);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    return 0;
}

调试多线程程序:

(gdb) break main
(gdb) run
(gdb) info threads
(gdb) thread 2
(gdb) backtrace
(gdb) continue

通过这些具体的例子,我们可以看到GDB在设置断点、单步执行、检查和修改变量、查看调用栈、调试多线程程序等方面的强大功能。熟练掌握这些技巧可以大大提高调试效率和程序的可靠性。

多线程调试深入分析

多线程程序的调试相对复杂,因为多个线程同时执行,导致调试过程变得更加难以控制和理解。GDB提供了一些专门用于多线程调试的命令和功能,下面通过详细说明和举例来加深对多线程调试的理解。

1. 基本概念

  • 线程(Thread):一个线程是一个独立执行的最小单元。
  • 主线程(Main Thread):创建其他线程的主程序中的线程。
  • 线程ID(Thread ID):GDB为每个线程分配一个唯一的ID来区分不同的线程。

2. 常用命令

  • info threads:列出所有线程及其状态。
  • thread [id]:切换到指定的线程。
  • thread apply [id|all] [command]:对一个或多个线程执行GDB命令。

3. 示例代码

以下是一个简单的多线程程序,用于演示如何使用GDB进行调试:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

void* thread_func(void* arg) {
    int id = *(int*)arg;
    for (int i = 0; i < 5; i++) {
        printf("Thread %d: %d\n", id, i);
        sleep(1);
    }
    return NULL;
}

int main() {
    pthread_t t1, t2;
    int id1 = 1, id2 = 2;
    pthread_create(&t1, NULL, thread_func, &id1);
    pthread_create(&t2, NULL, thread_func, &id2);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    return 0;
}

编译并运行程序:

$ gcc -g -pthread example.c -o example
$ ./example

4. 调试多线程程序

启动GDB并设置断点:

$ gdb example
(gdb) break thread_func
Breakpoint 1 at 0x40052d: file example.c, line 7.
(gdb) run
Starting program: /path/to/example

查看线程信息:

(gdb) info threads
  Id   Target Id         Frame 
* 1    Thread 0x7ffff7fb6740 (LWP 1234) "example" main () at example.c:15
  2    Thread 0x7ffff7fb5700 (LWP 1235) "example" thread_func (arg=0x7fffffffe4c) at example.c:7
  3    Thread 0x7ffff7fb4700 (LWP 1236) "example" thread_func (arg=0x7fffffffe4c) at example.c:7

切换到特定线程并检查栈帧:

(gdb) thread 2
[Switching to thread 2 (Thread 0x7ffff7fb5700 (LWP 1235))]
(gdb) backtrace
#0  thread_func (arg=0x7fffffffe4c) at example.c:7
#1  0x00007ffff7bc6fa3 in start_thread (arg=<optimized out>) at pthread_create.c:486
#2  0x00007ffff78ed4cf in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

单步执行特定线程:

(gdb) step
Thread 2: 0
(gdb) step
Thread 2: 1

对所有线程执行命令:

(gdb) thread apply all backtrace
Thread 3 (Thread 0x7ffff7fb4700 (LWP 1236)):
#0  thread_func (arg=0x7fffffffe4c) at example.c:7
#1  0x00007ffff7bc6fa3 in start_thread (arg=<optimized out>) at pthread_create.c:486
#2  0x00007ffff78ed4cf in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Thread 2 (Thread 0x7ffff7fb5700 (LWP 1235)):
#0  thread_func (arg=0x7fffffffe4c) at example.c:7
#1  0x00007ffff7bc6fa3 in start_thread (arg=<optimized out>) at pthread_create.c:486
#2  0x00007ffff78ed4cf in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

Thread 1 (Thread 0x7ffff7fb6740 (LWP 1234)):
#0  main () at example.c:15

设置线程特定断点:

(gdb) break thread_func thread 2
(gdb) continue
Continuing.

Thread 2 hit Breakpoint 1, thread_func (arg=0x7fffffffe4c) at example.c:7
7       for (int i = 0; i < 5; i++) {

5. 综合调试示例

以下是一个综合示例,展示了如何使用GDB调试多线程程序的各个方面:

  1. 启动GDB并设置断点:

    $ gdb example
  2. 设置断点并运行程序:

    (gdb) break thread_func
    (gdb) run
  3. 程序暂停时查看线程:

    (gdb) info threads
  4. 切换到特定线程:

    (gdb) thread 2
  5. 查看调用栈:

    (gdb) backtrace
  6. 单步执行:

    (gdb) step
  7. 对所有线程执行命令:

    (gdb) thread apply all backtrace
  8. 继续执行程序:

    (gdb) continue

通过这些详细步骤,我们可以全面了解如何使用GDB调试多线程程序,掌握线程切换、查看调用栈、单步执行等技巧,以更有效地定位和解决多线程程序中的问题。

gdb 调试工具的所有指令以及解释

命令描述
help显示帮助信息。
help <command>显示指定命令的帮助信息。
list (l)显示源代码。
list <start> <end>显示指定行范围内的源代码。
break <function>在指定函数设置断点。
break <file>:<line>在指定文件的指定行设置断点。
clear <breakpoint-number>清除指定编号的断点。
delete <breakpoint-number>删除指定编号的断点。
info breakpoints显示所有断点的信息。
run (r)运行程序直到第一个断点被触发或程序结束。
continue (c)继续执行程序直到下一个断点被触发或程序结束。
step (s)单步执行,即使进入函数调用。
next (n)单步执行,但不进入函数调用。
finish继续执行直到当前函数返回。
stepi单步执行机器指令。
nexti单步执行机器指令,但不进入子函数调用。
print <expression>打印表达式的值。
display <expression>每次停止时显示表达式的值。
undisplay <display-number>停止显示指定编号的表达式。
info args显示当前函数的参数。
info locals显示局部变量的值。
info frame显示当前栈帧的信息。
backtrace (bt)显示当前调用栈的跟踪信息。
frame <frame-number>切换到指定的栈帧。
set variable <var> = <value>设置变量的值。
where显示当前位置的信息。
quit (q)退出 GDB。

这个表格只包括了一些基本的命令。GDB 还有很多其他高级特性,例如条件断点、信号处理、脚本化调试等。如果您需要了解更详细的命令列表或高级用法,请查阅官方文档或使用 help 命令获取更多帮助。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐