YaoCheng8667 的个人博客

记录精彩的程序人生

Open Source, Open Mind,
Open Sight, Open Future!
  menu
8 文章
0 浏览
1 当前访客
ღゝ◡╹)ノ❤️

GDB调试工具基本使用方法,多线程,多进程

1. 基本命令及使用方法

1.1 设置断点

命令格式 例子 解释
break(b) + 行号 break n 在第 n 行设置断点
break(b) + 函数名 break func 在 func 函数前设置断点
tbreak + 行号/函数名 tbreak n/func 设置临时断点,到达后自动删除
break + filename: 行号/函数名 break main.c:10 在指定文件设置断点
break + 行号 + if + 条件 break 10 if i == 3 根据指定条件设置断点 ,多用于循环
awatch/watch + 变量 watch i 变量被读出或写入时暂停
clear + 行号 clear n 删除第 n 行对应的断点
info breakpoints/watchpoints - 查看断点/观察点信息
delete + 断点编号 delete 2 删除断点编号为 2 的断点
enable /disable + 断点编号 enable/disable 1 改变对应编号断点状态是否可用

1.2 查看/改变变量信息

命令格式 例子 解释
display + 表达式 diaplay a+b 实时显示表达式的值
whatis + 变量 whatis i 显示变量的类型
print + 变量/表达式 print a+b 打印表达式的值
set + 变量 = val set a = 10 设置某个变量的值
info local - 显示当前局部变量的值
info display - 展现目前显示的表达式的状态
delete/disable/enable display+ 表达式编号 disable display 1 改变编号对应表达式的显示状态

1.3 程序执行与调试

命令格式 例子 解释
run(r) - 开始执行程序
stop - 停止程序运行
step(s) - 单步执行程序,若有函数调用,进入调用函数单步执行
next(n) - 单句执行程序,若有函数调用,执行整个函数
finish - 运行直至当前函数结束
return + 返回值 return 5 直接结束当前线程返回响应值
continue(c) - 执行至下一个断点或程序结束
call + 函数 call + func 在当前位置执行相应的函数

1.4 堆栈相关命令

命令格式 例子 解释
info stack - 查看堆栈的情况
backtrace(bt)(where) - 打印当前栈中栈帧的指针
frame + 栈帧编号 - 打印指定栈帧的情况
up/down - 跳到上一层/下一层函数
until + 行数 until 30 运行到指定行数

Tips: 可以使用 backtrace 和 frame 命令定位到 SegmentFault 的代码位置,很好用。


2. 多线程 GDB 调试

2.1 基本功能及命令介绍

gdb 提供了新线程创建的自动提醒以及在相应线程设置断点的功能,主要包含的命令如下所示:

命令格式 例子 解释
info threads - 查看当前所有线程信息
thread + 线程号 thread n 切换到线程 n 进行调试
thread apply + 线程 id list / all + 命令 thread apply all info local 在一系列线程上执行命令
set sheduler-locking off/on/step - 设置线程的调度锁

线程的调度锁:

  1. off : 不锁定任何线程,所有线程均可以执行,为默认值。
  2. on : 只有当前被调试的线程可以被执行。
  3. step : 使用 step 进行单步执行时,当前线程不会被抢占,而使用 next,continue,finish 等操作不保证不被抢占。

2.2 操作示例

以如下多线程代码为例:main 函数执行后会生成两个新的线程,th1 和 th2.

#include <unistd.h>
#include <iostream>
#include <thread>

using namespace std;

void t1(){
    while(1){
        this_thread::sleep_for(chrono::seconds(5));
        cout<<"thread 1 echo"<<endl;
    }
}

void t2(){
    while(1){
        this_thread::sleep_for(chrono::seconds(5));
        cout<<"thread 2 echo"<<endl;
    }
}

int main(){
    thread th1(t1);
    thread th2(t2);
    th1.detach();
    th2.detach();
    int maxn = 100;
    while(maxn--){
        this_thread::sleep_for(chrono::seconds(5));
        cout<<"main thread"<<endl;
    }
    return 0;
}

首先可以在 26 行设置断点,执行到断点处程序已经开启三个线程。
使用 info thread 可以查看目前的线程数量有 3 个。

15844587091.png

执行 next(n) 后可以看出默认状态下几个线程是并行执行的。
之后设置线程调度锁打开可以看出除主线程之外的线程在调试时均被阻塞。
15844587981.png

使用 thread 命令切换到 thread 2 后可以看出其他线程也被阻塞。

47f6e45e3851906fc378d4f236e15e3.png

使用 thread apply + 线程号组 可以对多个线程执行操作,这里使用了 info local 命令查看线程的局部变量。

fb4b27a294d1c54a3f7b3ba0a280cb7.png


3. 多进程 GDB 调试

3.1 follow-fork-mode 模式

命令格式 例子 解释
follow-fork-mode + parent/child - 设置 fork 之后是调试父进程还是子进程,默认为父进程
detach-on-fork on/off - 设置调试的父子进程的状态
info inferiors - 查看当前所有进程信息(gdb 称任何执行中的进程为 inferior)
inferior + 进程号 inferior 2 切换到指定的 inferior
detach + 进程号 detach 2 允许指定进程号的进程正常运行

detach-on-fork:

  1. on : 断开并调试 follow-fork-mode 指定的进程。
  2. off : gdb 将控制父进程和子进程。follow-fork-mode 指定的进程将被调试,另一个进程置于暂停(suspended)状态。

3.2 attach 进程号

gdb 可以通过 attach 对正在执行的程序进行调度,它允许开发人员中断程序 并查看其状态,之后还能让这个程序正常地继续执行。

3.3 操作示例

以如下多进程代码为例,子进程中将产生除 0 的浮点错误。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int inchild(){
    int a = 100,b = 0,c;
    c = a/b;
    return c;
}

int main(){
    pid_t pid;

    pid = fork();
    if (pid < 0){
        printf("fork err\n");
        exit(-1);
    }else if (pid == 0){// in child process
	printf("in child process.\n");
        sleep(30);
        int i = 100;
        while(i--){
            sleep(1);
            if(i == 90) 
                inchild(); 
        }
    }else{// in parent process
        sleep(6);
        waitpid(-1,NULL,0);
        exit(0);
    }
}

(1)follow_fork_mode 调试

0249af5f2951375e87074a7daf55123.png

  • 在 fork 前设置断点,并将 detach-on-fork 设为 off ,开启单步调试。
  • fork 之后 gdb 对于新的进程会给出提示。
  • 使用 info inferiors 可以查看当前被 gdb 控制的进程。
  • 由上图可以看出将 detach-on-fork 设为 off 之后子进程处于暂停状态。

c0eb55156ebcef16f4d604b66f79a10.png

  • 使用 inferior 2 进入子进程并 continue 执行完成。
  • 发现 inchild 函数中报了浮点错误。

(2)attach 指定进程号调试

268c137c9df7a45c659ea4e2182741a.jpg

  • 后台执行程序并使用 ps 命令可以得到子进程的进程号

8ef88b828e2be168caebd40f7adc681.png

  • 在 gdb 中可以 使用 attach+ 进程号附着到指定进程进行调试
  • 使用 backtrace 跟踪栈帧可以确定当前程序执行位置
  • 使用 stop 可以暂停程序,此时可以查看某些变量的值改变变量的值或其他调试工作。

4ae65cf79cc8d7e418f7b996b1b9cfe.png

  • 使用 continue 继续执行程序可以跟踪到子进程中的浮点错误。

本文参考及引用

https://www.cnblogs.com/euphie/p/9781482.html

https://cloud.tencent.com/developer/article/1142947


标题:GDB调试工具基本使用方法,多线程,多进程
作者:YaoCheng8667
地址:https://ycisme.xyz/articles/2020/03/17/1584411838434.html