【Linux系统编程】调试器-gdb/cgdb
- [1. Debug和Release](#1. Debug和Release)
- [2. gdb/cgdb基本命令和使用](#2. gdb/cgdb基本命令和使用)
-
- [2.1 list/l 命令](#2.1 list/l 命令)
- [2.2 break/b、info break/b、delete/d breakpoints](#2.2 break/b、info break/b、delete/d breakpoints)
- [2.3 r/run、n/next、s/step](#2.3 r/run、n/next、s/step)
- [2.4 print/p、display、undisplay、until](#2.4 print/p、display、undisplay、until)
- [2.5 finish、continue/c](#2.5 finish、continue/c)
- [2.6 disable breakpoints、 enable breakpoints](#2.6 disable breakpoints、 enable breakpoints)
- [2.7 set var](#2.7 set var)
- [2.8 watch](#2.8 watch)
- [2.9 条件断点](#2.9 条件断点)
1. Debug和Release
gdb/cgdb是一个强大的调试工具,支持多种编程语言,主要用于C/C++程序调试。
我们以下面的代码为例去认识gdb/cgdb。
cpp
// mycmd.c
#include <stdio.h>
//int flag = 0; // 故意错误
//int flag = -1;
int flag = 1;
int Sum(int s, int e)
{
int result = 0;
int i = s;
for(; i <= e; i++)
{
result += i;
}
return result * flag;
}
int main()
{
int start = 1;
int end = 100;
printf("I will begin\n");
int n = Sum(start, end);
printf("running done, result is: [%d~%d]=%d\n", start, end, n);
return 0;
}
我们用gcc去直接编译该代码生成的二进制程序是Release版本的。
我们用命令readelf -S mycmd | grep -i debug去检查可执行文件或目标文件(即mycmd)中是否包含调试信息,以及调试信息的详细情况。
发现mycmd中没有任何调试信息。
要想让gcc编译mycmd.c生成debug版本的二进制程序要加-g选项
gcc -g mycmd.c -o mycmd-debug
发现mycmd-debug确实有调试信息。

而且从文件大小也能看出mycmd-debug确实有调试信息。

既然gdb是调试器,那么分别对mycmd和mycmd-debug用gdb调试,也能看出来mycmd是没有调试信息的。

2. gdb/cgdb基本命令和使用
2.1 list/l 命令
list/l 行号 :显示源代码,从上次位置开始,每次列出10行
list/l 函数名 :列出指定函数的源代码
list/l ⽂件名:⾏号 :列出指定⽂件的源代码

quit:退出gdb

是不是感觉如果通过list查看行号,然后调试的话特别麻烦?
所以可以使用cgdb,cgdb和gdb的命令是完全一样的。不同的是cgdb可以直接看着代码去调试。


2.2 break/b、info break/b、delete/d breakpoints
break/b [文件名]:行号 :在指定⾏号设置断点
break/b 函数名 :在函数开头设置断点
info break/b :查看当前所有断点的信息

delete/d breakpoints :删除所有断点
delete/d breakpoints n :删除序号为n的断点
注意:删除时 n 指的是序号,不是行号

2.3 r/run、n/next、s/step
r/run :从程序开始连续执⾏
n/next :单步执⾏,不进⼊函数内部
s/step :单步执⾏,进⼊函数内部

2.4 print/p、display、undisplay、until
print/p 表达式 :打印表达式的值
p 变量 :打印指定变量的值
display 变量名 :跟踪显示指定变量的值(每次停⽌时)
undisplay 编号 :取消对指定编号的变量的跟踪显示
until 行号 执⾏到指定⾏号
什么命令也不输入直接执行,会执行上次输入的命令

2.5 finish、continue/c
finish :执⾏到当前函数返回,然后停⽌
continue/c :从当前位置开始连续执⾏程序

2.6 disable breakpoints、 enable breakpoints
disable breakpoints :禁用所有断点
enable breakpoints :启用所有断点

为什么不直接删除断点,而选择禁用断点,防止此次调试debug未彻底,以便以后再次debug。
2.7 set var
set var 变量=值 :修改变量的值
为了更好地演示,先把flag从1改成0
cpp
// mycmd.c
#include <stdio.h>
int flag = 0; // 故意错误
//int flag = -1;
//int flag = 1;
int Sum(int s, int e)
{
int result = 0;
int i = s;
for(; i <= e; i++)
{
result += i;
}
return result * flag;
}
int main()
{
int start = 1;
int end = 100;
printf("I will begin\n");
int n = Sum(start, end);
printf("running done, result is: [%d~%d]=%d\n", start, end, n);
return 0;
}
改完后发现1~100的和为0,假如我们不知道是flag导致的错误,此时我们要用cgdb进行调试。

我们调试到这里,发现是因为flag=0,导致的函数返回值为0,进而导致输出结果为0,我们如果结束调试把flag改为1再测试程序是否正确的话太慢了,所以可以用命令set var flag=1,在调试中让flag=1,观察结果是否正确,加快了调试效率。

发现结果正确,所以得出结论是因为flag错误而导致的程序结果错误。

2.8 watch
执行时监视⼀个表达式(如变量)的值。如果监视的表达式在程序运行期间的值发⽣变化,GDB会暂停程序的执行,并通知使⽤者。
如果你有⼀些变量不应该修改,但是你怀疑它修改导致了问题,你可以watch它,如果变化了,就会通知你.

2.9 条件断点
添加条件断点
b 行号/文件名:行号/函数名 if 条件

给已经存在的断点新增条件
condition 编号 条件
