1. GDB的简单介绍
GDB是由GNU软件系统社区提供的调试工具,同GCC配套组成了一套完成的开发环境,GDB是Linux和许多类Unix系统中的标准开发环境。 具备以下四点功能:
- 启动程序,可以按照自定义方式运行程序;
- 可以让程序停在断点处(断点可以是条件表达式);
- 当程序停止,可以检查此时程序中所发生的事(变量的值等);
- 可以改变程序,将一个BUG产生的影响修正从而测试其他BUG
2.GDB命令
#启动和退出
gdb 可执行程序
quit
#给程序设置参数、获取参数
set args 10 20
show args
#GDB使用帮助
help
#查看当前文件代码
list/l (从默认位置显示)
list/l 行号 (从指定行显示)
list/l 函数名 (从指定函数显示)
#查看非当前文件代码
list/l 文件名:行号
list/l 文件名:函数名
#设置显示的行数
show list/listsize
set list/listsize行数
3.调试基本流程
#include <stdio.h>
#include <stdlib.h>
int test(int a);
int main(int argc, char* argv[]) {
int a, b;
printf("argc = %d\n", argc);
if(argc < 3) {
a = 10;
b = 30;
} else {
a = atoi(argv[1]);
b = atoi(argv[2]);
}
printf("a = %d, b = %d\n", a, b);
printf("a + b = %d\n", a + b);
for(int i = 0; i < a; ++i) {
printf("i = %d\n", i);
// 函数调用
int res = test(i);
printf("res value: %d\n", res);
}
printf("THE END !!!\n");
return 0;
}
int test(int a) {
int num = 0;
for(int i = 0; i < a; ++i) {
num += i;
}
return num;
}
一个简单的程序,来自 牛客大学的视频。这个函数会接收外界的参数,然后进行一些简单操作。
首先得通过-g
生成具备调试信息的可执行文件,-g
表示在编译的时候加入调试信息,该调试信息可以被调试器(GDB)调试。同时生成一个不含调试信息的test1,通过文件大小对比,可以看出含有调试信息的test大小为10K,明显大于8.6K。通过rm test1
删除test1文件。
通过启动命令gdb test
进入GDB模式,终端出现了很多信息。
通过set args 10 20
给可执行程序输入两个参数,并通过show args
显示参数。
通过help 命令
可以查看命令的帮助。
通过list 行号
可以查看对应行的代码,可以看到显示出来的10行代码,20行处于中间位置。
通过list 函数名
可以查看对应函数的代码,可以看到显示出来的10行代码,main函数处于中间位置。
通过set list 5
将显示的代码行数设置为5(默认是10行)。
4.断点操作命令
斜杠代表可选,不是命令的一部分。
#设置断点
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
存在这样几个文件,bubble.cpp、select.cpp、main.cpp、sort.h。
//bubble.cpp
#include "sort.h"
#include <iostream>
using namespace std;
void bubbleSort(int *array, int len) {
for (int i = 0; i < len - 1; i++) {
for (int j = 0; j < len - 1 - i; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
//select.cpp
#include "sort.h"
#include <iostream>
using namespace std;
void selectSort(int *array, int len) {
for (int j = 0; j < len - 1; j++) {
for (int i = j + 1; i < len; i++) {
if (array[j] > array[i]) {
int temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
}
}
//sort.h
#ifndef SORT_H_
#define SORT_H_
void bubbleSort(int *array, int len);
void selectSort(int *array, int len);
#endif
//main.cpp
#include <iostream>
#include "sort.h"
using namespace std;
int main() {
int array[] = {12, 27, 55, 22, 67};
int len = sizeof(array) / sizeof(int);
bubbleSort(array, len);
// 遍历
cout << "冒泡排序之后的数组: ";
for(int i = 0; i < len; i++) {
cout << array[i] << " ";
}
cout << endl;
cout << "===================================" << endl;
int array1[] = {25, 47, 36, 80, 11};
len = sizeof(array1) / sizeof(int);
selectSort(array1, len);
// 遍历
cout << "选择排序之后的数组: ";
for(int i = 0; i < len; i++) {
cout << array1[i] << " ";
}
cout << endl;
return 0;
}
通过gcc编译器生成可执行文件main,记得带上编译选项-g。
通过list
查看代码,通过break 行号
打下断点,中断会显示断点的编号、地址、所在文件、所在行号。通过info break
可以查看所有断点的信息,包括编号、类型、是否激活等信息。
5.GDB调试命令
#运行GDB程序
start(程序停在第一行)
run(遇到断点才停)
#继续运行,到下一个断点停
c/continue
#向下执行一行代码(不会进入函数体)
n/next
#变量操作
p/print 变量名(打印变量值)
ptype 变量名(打印变量类型)
#向下单步调试(遇到函数进入函数体)
s/step
finish(跳出函数体)
#自动变量操作
display num(自动打印指定变量的值)
i/info display
undisplay 编号
#其他操作
set var 变量名=变量值
until (跳出循环)
通过start
命令让程序停在了第一行,也就是main函数。
通过c/continue
命令,让程序继续运行,直到遇到下一个断点;如果没有设置断点,就直接程序运行到结束。
在11行打个断点,输入命令run
,程序直接运行到断点处。
通过b 16
在16行打下断点,16行是一个for循环内容。通过continue
运行到第16行,进入循环体。通过print i
可以查看变量i的值。
6. GDB多进程调试
使用GDB调试的时候,GDB默认只能跟踪一个进程,可以在fork函数之前,通过指令设置GDB调试工具跟踪父进程或子进程,默认是跟踪父进程,通过下述命令可以更改。
set follow-fork-mode parent //默认情况
set follow-fork-mode child //跟踪子进程
set detach-on-fork on
set detach-on-fork off
默认调试模式是on,表示调试当前进程时,其他的进程继续运行;如果是off,调试当前进程的时候其他进程被GDB挂起。
info inferiors //查看调试的进程
inferior id //切换当前调试的进程
detach inferiors id //使进程脱离GDB调试