半小时入坑GDB

246 阅读3分钟

B站讲解

B站链接

什么是GDB

GDB 是由 GNU 软件系统社区提供的调试工具;

Those programs might be executing on the same machine as GDB (native), on another machine (remote), or on a simulator. GDB can run on most popular UNIX and Microsoft Windows variants, as well as on Mac OS X.

一般来说,GDB 主要帮助你完成下面四个方面的功能:

  1. 启动程序,可以按照自定义的要求随心所欲的运行程序
  2. 可让被调试的程序在所指定的调置的断点处停住(断点可以是条件表达式)
  3. 当程序被停住时,可以检查此时程序中所发生的事
  4. 可以改变程序,将一个 BUG 产生的影响修正从而测试其他 BUG

快速开始 编译c++代码

指令 gcc -g -Wall program.cpp -o program

  • -g 选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机 器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证 gdb 能找到源文件。

  • -Wall在尽量不影响程序行为的情况下选项打开所有 warning,也可以发现许多问题,避免一些不必要的 BUG。

GDB常见命令

启动、退出、查看代码

  • 启动和退出
gdb 可执行程序
quit
  • 给程序设置参数/获取设置参数
set args 10 20
show args
  • 查看文件代码
list/l (从默认位置显示)
list/l 行号 (从指定的行显示)
list/l 函数名(从指定的函数显示)
list/l 文件名:行号
list/l 文件名:函数名
  • 设置显示的行数
show list/listsize
set list/listsize 行数
  • GDB 使用帮
help

GDB 命令 - 断点操作

设置断点
b/break 行号
b/break 函数名
b/break 文件名:行号
b/break 文件名:函数
常看断点i/info b/break
删除断点d/del/delete 断点编号
设置断点无效dis/disable 断点编号
设置断点生效ena/enable 断点编号
设置条件断点(一般用在循环的位置)b/break 10 if i==5

调试命令

  1. 运行GDB程序
start(程序停在第一行)
run(遇到断点才停)
  1. 继续运行,到下一个断点停
c/continue
  1. 向下执行一行代码(不会进入函数体)
n/next
  1. 变量操作
p/print 变量名(打印变量值)
ptype 变量名(打印变量类型)
  1. 向下单步调试(遇到函数进入函数体)
s/step
finish(跳出函数体)
  1. 自动变量操作
display 变量名(自动打印指定变量的值)
i/info display
undisplay 编号
  1. 其它操作
set var 变量名=变量值 (循环中用的较多)
until (跳出循环)

调试正在运行的程序

根据xxx获取进程信息:  ps -ef | grep xxx 
根据xxx进程进行调试:  gdb -p xxx进程

进阶调试

  • 查看当前运行的进程:ps aux | grep book
  • 查看当前达行的轻量级进程:ps -aL | grep book
  • 查看主线程和子线程的关系:pstree -p主线程id

多进程调试

  • set follow-fork-mode child(parent)
  • info inferiors 查看调试的进程
  • inferiors 进程编号 切换当前调试的进程
  • set detach-on-fork [on|off] Set whether gdb will detach the child of a fork.
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main() {

    int num = 10;
    // 创建子进程
    pid_t pid = fork();

    // 判断是父进程还是子进程
    if(pid > 0) {
        // printf("pid : %d\n", pid);
        // 如果大于0,返回的是创建的子进程的进程号,当前是父进程
        printf("i am parent process, pid : %d, ppid : %d\n", getpid(), getppid());
        printf("parent num : %d\n", num);
        num += 10;
        printf("parent num += 10 : %d\n", num);

    } else if(pid == 0) {
        // 当前是子进程
        printf("i am child process, pid : %d, ppid : %d\n", getpid(),getppid());
       
        printf("child num : %d\n", num);
        num += 100;
        printf("child num += 100 : %d\n", num);
    }

    // for循环
    for(int i = 0; i < 2; i++) {
        printf("i : %d , pid : %d\n", i , getpid());
        sleep(1);
    }
    return 0;
}

多线程调试

  • 查看线程: info threads
  • 切换线程: thread线程id
  • 只运行当前线程: set scheduler-locking on
  • 运行全部的线程: set scheduler-locking off
  • 指定某线程执行某gdb命令: thread apply线程id
  • 全部的线程执行某gdb命令: thread apply all cmd
#include<iostream>
#include<thread>
#include<cstdio>
using namespace std;
const int len = 3;
void func(){
    for(int i=0;i<2;i++){
        cout<<" i: "<<i<<" thread id : "<< this_thread::get_id()<<endl;
    }
}
int main(){
    thread t[len];
    for(int i=0;i<len;i++){
        t[i]=thread(func);
    }
    for(int i=0;i<len;i++){
        t[i].join();
    }
    return 0;
}