一些准备工作
最基本的,安装好 gdb 程序。
以GDB调试C++程序为例,在调试前,需要将待调试的程序编译为带有可调试信息的可执行文件。
如果是使用 g++ 命令编译程序,指定 -g 选项,例如:g++ -g main.cpp -o main
如何是使用 cmake 构建项目,则在 CMakeLists.txt 文件中,在 add_executable
命令后面添加 target_compile_options
命令,例如:
cmake_minimum_required(VERSION 3.15)
project(DEMO)
add_executable(main main.cpp)
target_compile_options(main PUBLIC -g)
生成了带有可调试信息的可执行文件后,就可以使用 gdb 对程序调试了,以如下程序为简单示例,介绍常用的gdb调试命令。
// utils.h
#ifndef UTILS_H
#define UTILS_H
#include <vector>
using namespace std;
void printArr(const vector<int>& nums, int len);
#endif
// utils.cpp
#include "utils.h"
#include <iostream>
void printArr(const vector<int> &nums, int len)
{
for (int i = 0; i < len; ++i) {
cout << nums[i] << " ";
}
cout << endl;
}
#include <vector>
#include <stdlib.h>
using namespace std;
#include "utils.h"
int main(int argc, char *argv[])
{
int len = argc - 1;
vector<int> nums(len);
for (int i = 1; i < len; ++i) {
nums[i] = atoi(argv[i]);
}
printArr(nums, len);
return 0;
}
常用命令
启动gdb
gdb program
,gdb 命令后跟着可执行程序名,进入 gdb 程序命令行交互,开始对该程序进行调试;需要注意的是,这只是启动了gdb 程序,被调试的程序还没有被执行,我们在启动 gdb 程序后,需要使用一系列命令来调试程序,下面将介绍一些常用的命令;
输入 quit
或 q
退出 gdb 程序,q 为 quit 的缩写,大多数命令都有其对应的缩写命令;
查看程序源代码
在 gdb 程序中,使用 list
或 l
命令查看程序源代码。
输入回车表示执行上一次执行的命令,即再次输入 l 命令,向下继续查看源代码;
list
命令默认显示 10 行代码,可以使用 set listsize number
或 set list number
命令设置一次显示的代码行数,使用 show listsize
或 show list
命令查看 使用 list
命令一次显示多少行;
使用 help command
命令可以查看该命令的描述和一些可选项等;
list 命令默认查看的是 main 函数所在的文件中的源代码,如上图 help list
命令显示所示,可以使用 list 命令查看指定的文件中的源代码、指定文件中函数的源代码、指定文件中指定行的上下源代码;
查看指定的文件及指定行的上下文源代码,l FILENAME:LINENUM
,例如 l utils.cpp:5
;
查看指定的文件及其特定的函数的源代码,l FILENAME:FUNCTION
,例如 l utils.cpp:printArr
;
需要注意的是,使用 list 命令查看除可执行文件的其他文件的源代码时,文件名后面需要指定行号或函数名;
使用 list -
命令,表示向前查看源代码;
设置命令行参数
若程序需要从命令行输入参数,那在调试前,就需要先设置好传入程序的命令行参数,使用命令 set args
设置传入的参数,当有多个参数时,每个参数之间用空格隔开;可以使用 show args
查看设置好的参数;
处理 set args
命令可以设置命令行参数,在 run
或 start
命令的后面也可以跟着命令行参数,表示已这些参数启动程序;例如: run 1 2 3 4 5
在gdb中运行待调试的程序
使用命令 run/r
或 start
运行待调试的程序;start
命令会执行到 main 函数的第一行,然后暂停住,等待输入其他的 gdb 命令,而执行 run/r
命令,若没有设置断点,程序没有bug,则程序会顺利执行完毕;
(上文中已经设置了命令行参数)
输入命令 continue/c
,程序会从断点处继续运行,直到遇到下一个断点或程序执行完毕;
设置、删除、查看断点
设置断点
break/b
命令用于设置断点,有以下几种形式:
- 在当前文件中的指定行设置断点,例如,在当前文件的第10行设置断点,
break 10
; - 在当前文件中的指定函数上设置断点,当程序的执行逻辑执行到该函数时,就会触发该断点,例如,在main函数的第一行设置断点,
break main
; - 在指定文件中的指定行设置断点,例如,
b utils.cpp:9
; - 在指定文件中的指定函数上设置断点,当程序的执行流进入到该函数体时,才会触发该断点,例如,
b utils.cpp:printArr
; - 在上面四种形式的基础上,设置条件断点,但设置的条件为true时,断点触发,通常在断点处的某个变量设置一个指定值;形式为:
b 10 if val==10
,例如在本示例程序中,b utils.cpp:7 if i==2
;
查看断点信息
info/i break/b
命令用于查看设置的断点信息;
Num
表示断点编号;Type
breakpoint, watchpoint or catchpoint;Disp(Disposition)
当断点命中时,断点是否被标记为 disabled or deleted;Enb
断点是否有效;Address
断点在程序中的位置,作为内存地址;What
断点在文件中的信息,在哪个文件,那行,或者哪个函数中;
删除断点
delete/d
命令用于删除断点,有以下形式:
d number
删除指定编号的断点;d num1-num2
删除编号为 num1 到 num2 的断点,这些断点必须时存在的;d num1 num3 num4
删除多个指定编号的断点;
使断点失效、恢复失效断点
disable/dis
命令用于使断点失效,enable
命令用于恢复失效的断点;disable 和 enable 命令和删除断点的命令的使用形式相同;
查看变量值
print/p expr
命令用于输出变量的值,默认会根据变量的类型匹配输出的格式;
使用 display
命令设置的变量或表达式,没当程序暂停时,会自动打印这些设置的变量值;undisplay、disable display、enable display
命令用于删除、失效和恢复设置的变量;使用形式和删除、失效、恢复断点类似;
设置变量值
set var
命令用于设置变量的值,例如,set var len=3
单步调试
使用 next/n
和 step/s
命令进行单步调试;使用 step/s
命令调试时,当遇到函数时,会进入到函数体内部,而 next/n
不会进入;
当单步调试进入到函数体内部时,可以使用 finish
命令跳出这个函数体,前提该函数体内部没有断点;
使用 until
命令可以提前退出一个循环体,前提是在这个循环体内没有有效的断点,且必须在循环体的开始位置或结束位置使用该命令;