Linux基础学习 | gdb调试

GDB(The GNU Project Debugger):GNU项目调试器,允许您查看另一个程序执行时"内部"发生了什么,或者其他程序在崩溃的那一刻在做什么。

文章目录

      • 程序示例一:求斐波那契数列第n项
        • 有关GDB调试文件
          • 1. gdb调试的是可执行文件。
          • 2. gdb在调试时也需要源文件
        • gdb调试第一个程序
          • list 列出源码
          • start 开始调试
          • next 单步调试
          • step 跳转到函数
          • backtrace 列出堆栈信息
          • info 查看详细信息
          • frame 选择函数栈帧
          • print 打印变量的值
          • 找到BUG,开始DEBUG
          • quit 退出gdb调试
      • 程序示例二:练习断点调试
          • run 自动执行程序
          • display 自动显示变量的值
          • break 断点
      • 程序示例三:观察点
          • x 列出内存中的值
          • 数组溢出带来的危害
          • watch 观察点
      • 程序示例四:段错误
          • 在Linux系统下使用gdb调试
          • 使用core文件调试
          • 函数返回时产生段错误
      • 补充:关于fgets()函数的使用
      • gdb 常用的调试命令

安装gdb的方式有很多,并且网上也有很多很详细的安装教程,这里就不再赘述了。下面让我们简单的了解一下gdb的命令。

下面将使用windows PowerShell演示,输入gdb命令,进入gdb程序。
Linux基础学习 | gdb调试_第1张图片
输入help命令查看gdb内支持的调试命令。
Linux基础学习 | gdb调试_第2张图片
使用help+命令 可以查看关于该命令更详细的介绍。
Linux基础学习 | gdb调试_第3张图片

以下将通过调试几个简单的程序,让我们熟悉gdb的使用方式。

程序示例一:求斐波那契数列第n项

先来看一个简单的示例:下列 fib(n) 函数将给出斐波那契数列的第n项元素。

#include 

int fib(int n)
{
     
    int a = 0, b = 1, t;
    while(n--)
    {
     
        t = a + b;
        a = b;
        b = t;
    }
    return a;
}

int main()
{
     
    int n = 0;
    for ( ; n < 10; n++)
    {
     
        printf("斐波那契数列第 %d 项为: %d \n", n ,fib(n));
    }
    return 0;
}

使用gcc编译并运行,输出如下:
Linux基础学习 | gdb调试_第4张图片
可以看到程序完美的运行,并输出了我们想要的结果。

但是这个程序却存在一个BUG,例如我们修改程序中主函数内 n 的初始值为 -1,则程序会出现死循环。

    int n = 0;
    for ( ; n < 10; n++)
    // ...

而要想弄明白,程序在执行期间究竟发生了什么,我们可以使用GDB进行调试。

有关GDB调试文件

在使用gdb调试之前,我们需要知道的是。

1. gdb调试的是可执行文件。

gdb可调试的对象必须是在gcc编译时添加-g参数生成的可执行文件,该可执行程序内含调试所需的信息。

如下图所示,使用-g参数生成含有调试信息的可执行程序
Linux基础学习 | gdb调试_第5张图片
通过两种方式编译的可执行程序都可以正常执行,而区别在于后者比前者多了一部分调试信息。
Linux基础学习 | gdb调试_第6张图片
我们可以看一下两个文件的大小。
Linux基础学习 | gdb调试_第7张图片

2. gdb在调试时也需要源文件

gcc 的-g 选项并不是把源代码嵌入到可执行文件中, 在调试时也需要源文件。

gdb调试命令 list 可简写为 l 是为列出源码,而此命令依赖的是源码文件 test.c 文件。
Linux基础学习 | gdb调试_第8张图片

如果如果我们将 test.c 文件移动到其他目录,l 命令将无法列出源码。
Linux基础学习 | gdb调试_第9张图片

gdb调试第一个程序

下面我们正式开始调试。

list 列出源码

使用gdb+调试文件 进行调试。
Linux基础学习 | gdb调试_第10张图片
也可以直接使用 l +函数 列出函数源码
Linux基础学习 | gdb调试_第11张图片

start 开始调试

start 命令开始执行程序,gdb将停在main 函数中,变量定义之后的第一条语句处,等待我们发命令。
Linux基础学习 | gdb调试_第12张图片
gdb 列出的是即将执行的下一条语句。

next 单步调试

使用 next 进行单步调试。简写为n,执行该命令程序将每次执行一步,并继续列出下一行将要执行的命令。
Linux基础学习 | gdb调试_第13张图片

step 跳转到函数

使用 step 命令将进入当前语句所在的函数中。
Linux基础学习 | gdb调试_第14张图片
可以看到,函数传入的参数 n = -1 。我们使用 l 命令列出当前函数的源码。

backtrace 列出堆栈信息

backtrace 命令(简写为 bt)可以查看函数调用的栈帧:
在这里插入图片描述
上图#0表示,当前在 fib 函数的栈中,#1表示当前 fib 函数是由 main 函数调用的。 fib 函数的栈帧编号为 0,main 函数的栈帧编号为 1。

info 查看详细信息

有关 info 命令可参考:gdb info

可以用 info命令(简写为 i)查看fib 函数局部变量的值:
Linux基础学习 | gdb调试_第15张图片
当前准备执行语句 int a = 0, b = 1, t; 但还未执行,因此 a、b、t 的值都是随机值。而执行完此语句后,a 与 b 都被赋予了新的值。

frame 选择函数栈帧

如果我们想查看当前main函数栈帧中的变量,可以使用 frame 命令切换到main函数的栈帧中。
Linux基础学习 | gdb调试_第16张图片
栈帧的编号可以通过 bt 命令查看。

print 打印变量的值

如果想查看某个变量的值,我们可以使用print 命令。
Linux基础学习 | gdb调试_第17张图片

如上图所示,这里我们需要注意两点:

  • 这里的$1 表示 gdb 保存着这些中间结果,$后面的编号会自动增长。如上图中,我们连续的执行p命令,$后的编号一直增长。
    在命令中可以用$1、 $2、 $3 等编号代替相应的值
  • 如果我们想通过一个p命令输出多个变量的话,类似 p a bp a,b 都是不行的,我们应使用 p { a, b} 这种方法。
    Linux基础学习 | gdb调试_第18张图片
    另外,print 命令还可以用于设置变量的值,print key=value 等同于 set variable key=value
找到BUG,开始DEBUG

此时我们执行了一次循环,通过前边学到的 info 命令与 p 命令 ,我们可以轻易的查看到程序执行到此处时,各变量的状态。
Linux基础学习 | gdb调试_第19张图片
通过上图的结果,我想我们已经知道程序错在哪里了。

错误分析:我们传入的参数 n 为负数时,经过 while(n–)的循环不会停止,而此负数也将会一直的进行自减一的操作,直至负数溢出至0才会结束。

debug:由于我们的程序没有进行参数有效性的检查,和程序逻辑的设计不过严密,而使得程序出现了BUG。

quit 退出gdb调试

使用quit 命令将退出gdb的调试状态。
Linux基础学习 | gdb调试_第20张图片

现在将程序修改为以下代码:

#include 

int fib(int n)
{
     
    if(n < 0) return 0; // 参数有效性检查 //非负

    int a = 0, b = 1, t;
    while(n > 0)
    {
     
        t = a + b;
        a = b;
        b = t;
        n -= 1; //
    }
    return a;
}

int main()
{
     
    int n = -1;
    for ( ; n < 10; n++)
    {
     
        printf("斐波那契数列第 %d 项为: %d \n", n ,fib(n));
    }
    return 0;
}

将修改后的程序重新编译运行。
Linux基础学习 | gdb调试_第21张图片
至此,我们的第一个gdb调试已经完成。

程序示例二:练习断点调试

下面是一个将输入的数字字符转换为整数输出的程序。

#include 
int main(void)
{
     
    int sum = 0, i = 0;
    char input[5];      // 保存输入字符的数组
    while (1)
    {
     
        printf("输入:");
        fflush(stdout);
        // 输入
        scanf("%s", input);
        // 将输入的字符串转换为数字
        for (i = 0; input[i] != '\0'; i++)
        {
     
            sum = sum*10 + input[i] - '0';
        }
        // 打印结果
        printf("输出:%d\n", sum);
    }

    return 0;
}

编译运行后,我们发现第一次运行时的结果正常,而第二次的运行结果却是错误的。
Linux基础学习 | gdb调试_第22张图片
为了解决这个bug,我们再次使用gdb进行调试。

重新编译,生成带有调试信息的可执行程序版本。
Linux基础学习 | gdb调试_第23张图片

run 自动执行程序

使用run命令可以让程序自动的向下执行。
Linux基础学习 | gdb调试_第24张图片
而单单只使用 run 命令,我们是无法进行调试的。run 命令等同于直接运行程序,一般配合断点来使用,是程序执行到断点处停下,等待我们的调试。

display 自动显示变量的值

通过分析程序的功能可知,程序在接收到输入后,将进行一系列操作将字符数组中的数字字符转换成一个整数, 最终存储在 sum 变量中。

因此, sum 变量的值是我们重点观察的对象。

我们可以使用 display 命令使得每次停下来的时候都显示当前 sum 的值。
Linux基础学习 | gdb调试_第25张图片
undisplay 命令可以取消跟踪显示,变量 sum 的编号是 1,可以用 undisplay 1 命令取消它的跟踪显示。

break 断点

通过之前 run 运行可知第一次的执行结果是正确的。那么我们需要跳过第一次的执行,直接对第二次的执行进行调试。

而通过断点这个功能即可做到。断点可以让程序在自动运行时,达到某种条件后就中断下来,等待我们的调试。

  • 添加断点
    使用break + 行号 命令可以给某一行打上断点。
    Linux基础学习 | gdb调试_第26张图片

  • 查看断点
    每个断点都有一个编号,我么使用info 命令可以查看已经设置的断点。可简写为 i b
    在这里插入图片描述

  • 删除断点
    如果我们想删除某个断点,只需要使用delete命令加断点编号即可删除 。delete breakpoints 2
    在这里插入图片描述

  • 禁用断点
    某些时候,我们想临时取消某个断点,但有不想删除这个断点。那么我们可以选择禁用它。使用命令 disable 命令。可简写为dis
    Linux基础学习 | gdb调试_第27张图片

  • 恢复断点
    我们使用命令 enable 可以重新启用已经禁用的断点。可简写为en
    Linux基础学习 | gdb调试_第28张图片

  • 函数名加断点
    断点也可以通过 b+函数名 的方法直接给函数加断点。
    在这里插入图片描述

  • 条件满足才生效的断点
    我们还可以设置断点在满足某个条件时才激活例如我们仍然在循环开头设置断点, 但是仅当 sum 不等于 0 时才中断, 然后用 run 命令(简写为 r)重新从程序开头连续运行:

使用 start 让程序重新开始调试并停在mian函数内首行位置。 添加断点并执行。
Linux基础学习 | gdb调试_第29张图片
可以看到第一次执行并没有触发断点,而第二次执行开始时触发了断点。
Linux基础学习 | gdb调试_第30张图片
第一次执行并没有触发sum != 0这个断点,而第二次却触发了这个断点。

通过分析源码,我们发现bug产生的原因是 sum 每次执行完都没有重新初始化造成的。

程序示例三:观察点

通过之前的断点调试,我们知道需要添加 sum=0 这条语句到 while(1) 之后。将程序修改为如下样式:

#include 
int main(void)
{
     
    int sum = 0, i = 0;
    char input[5]; 
    while (1)
    {
     
        sum = 0;
        scanf("%s", input);
        for (i = 0; input[i] != '\0'; i++)
        {
     
            sum = sum*10 + input[i] - '0';
        }
        printf("input=%d\n", sum);
    }

    return 0;
}

我们都知道使用 scanf 函数是不安全的,它在向变量中写入数据时不会检查变量的大小,使用不当就会产生数据溢出。

例如我们当前的程序,在输入字符长度超过input数组的空间时,sacnf也任然会执行。
Linux基础学习 | gdb调试_第31张图片
可以看到当我们输入 12345 时,输出结果竟然是 123407 。看来我们有必要再进行一波调试了。
Linux基础学习 | gdb调试_第32张图片
很明显,input数组的长度只有5个,而这里的字符串“12345”加上末尾的一个’\0’一共占据了6个字节的空间。

x 列出内存中的值

关于 x 命令。参考:https://www.jianshu.com/p/589308dd36dc
x/ 为examine命令缩写

n:是正整数,表示需要显示的内存单元的个数,即从当前地址向后显示n个内存单元的内容,
一个内存单元的大小由第三个参数u定义。

f:表示addr指向的内存内容的输出格式,s对应输出字符串,此处需特别注意输出整型数据的格式:
  x 按十六进制格式显示变量.
  d 按十进制格式显示变量。
  u 按十进制格式显示无符号整型。
  o 按八进制格式显示变量。
  t 按二进制格式显示变量。
  a 按十六进制格式显示变量。
  c 按字符格式显示变量。
  f 按浮点数格式显示变量。

u:就是指以多少个字节作为一个内存单元-unit,默认为4。u还可以用被一些字符表示:
  如b=1 byte, h=2 bytes,w=4 bytes,g=8 bytes.

:表示内存地址。
  • 例如 x/20xw 显示20个单元,16进制,4字节每单元

我们使用 x 命令查看input中的值,这里的x/7bx 。 这里 7 表示连续打印 7 组,b 表示每个字节一组, x 表示按十六进制格式打印

Linux基础学习 | gdb调试_第33张图片
使用断点直接在循环内 i == 4 处进行调试。并实时监视 i、sum、input[i] 的值。
Linux基础学习 | gdb调试_第34张图片
下面我们进行单步调试:
Linux基础学习 | gdb调试_第35张图片
可以看到,在 i == 4 时,sum 计算的到的结果为 12345 。而在 i = 5 时,本来因该是 0x00 的 input[5] 变成了 0x05 。再次查看input中连续的7个内存空间可发现,input[5]的值被修改了。
在这里插入图片描述
则表达式 sum = sum*10 + input[i] - '0'; 的值为
sum = 12345 * 10 + 5 - 48 = 123450 + (-43) = 123407

数组溢出带来的危害

那么问题来了,input[5]的位置为什么会变成 0x05 呢?

继续向下执行,每次执行都查看 input 中的值,我们发现 input[5] 位置的数据其实就是 i 变量的值。
Linux基础学习 | gdb调试_第36张图片
由此,我们可以猜想。input[5] 的值其实是被 i 变量修改了,因为他们所占同一个内存地址。使用 p &i p &input[5] 查看它们各自的地址。
在这里插入图片描述
可以看到果然如我们猜想的那样,i 与 input[5] 所占同一片空间。如下图示:
Linux基础学习 | gdb调试_第37张图片

上述我们通过分析代码,猜想input[5]与i共用一片内存空间,使得 i 的值改变从未诱发 input[5] 的值改变。从而加以验证。

watch 观察点

而如果代码复杂,我们无法通过肉眼观察得知某个存储单元是在哪里被改动的。这个时候我们就可以使用观察点( Watchpoint)来踪。

使用 watch 命令设置观察点,跟踪 input[5]的位置。

这里我们使用strat重新开始调试:并删除之前的断点和追踪点。
Linux基础学习 | gdb调试_第38张图片
我们继续之前的调试操作,只不过这次我们对 input[5] 添加一个观察点
Linux基础学习 | gdb调试_第39张图片
下面使用c 一直执行,观察 input[5] 的改变情况:
Linux基础学习 | gdb调试_第40张图片
通过观察我们便可发现,每次在for循环开始时,input[5] 的值都会加1,而循环体内的 i 也是每次for循环开始时加1,我们很容易将这两者结合起来。

如上,通过使用观察点我们也能发现这个bug所在的位置。

这里补充一点删除观察点的命令,通过图示我们可以看到,断点和观察点都可以通过info breakpoints 命令查看。
Linux基础学习 | gdb调试_第41张图片
而观察点也可以通过info watchpoints 命令单独查看。
在这里插入图片描述
对应的删除方法都可以使用delete 命令
Linux基础学习 | gdb调试_第42张图片
或者直接使用 del+编号 删除。

debug:修改该bug的方法主要是针对如何避免 input 数组溢出。我们可以使用scanf_s()函数,或者 fgets() 函数进行输入。

  • 小练习:下面程序中,使用了一个for循环初始化 arr 数组,但是我们在运行的时候却发现该循环似乎会一直运行,永远不会停止。能否使用上面学到的gdb方法进行调试,并debug。
#include 
#include 

int main()
{
     
	int i;
	int arr[5];
	for( i = 0; i <= 5; i++)
	{
     
		arr[i] = 0;
		// ...
		printf("%d %d\n",i, arr[i]);
		Sleep(400);
	}
	return 0;
}

程序示例四:段错误

#include 

int main(void)
{
     
	int man = 0;
	scanf("%d", man);
	return 0;
}

Linux基础学习 | gdb调试_第43张图片
段错误,属于运行时错误。

百科给出的段错误定义为:指访问的内存超出了系统所给这个程序的内存空间产生段错误

一旦一个程序发生了越界访问,cpu就会产生相应的保护,于是segmentation fault就出现了。段错误应该就是访问了不可访问的内存,这个内存区要么是不存在的,要么是受到系统保护的,还有可能是缺少文件或者文件损坏。

使用gdb调试可以查找段错误,接上图程序显示产生段错误,我们使用bt命令列出堆栈信息。
在这里插入图片描述
在 #0 栈帧中显示段错误出现在 ungetwc () 函数中,而在 #2 栈帧中显示的是 0x00000000 in ?? () 。这是什么意思?

我们发现,似乎在使用windows 下的MinGW版的gdb时并不能很好的完成段错误的调试,下面将演示使用Linux环境下的gdb调试。

关于堆栈信息直接显示为 0x000000,在StackOverflow上找到了答案。

这意味着包含调用者返回地址的框架指针或堆栈已损坏,因此GDB无法向后跟踪到调用者。 并且您的程序跳转到0x00000000中的非法地址,然后崩溃了。 您可以运行Valgrind来查看崩溃之前发生的事情。——StackOverflow

另外,在先前的调试中发现进程是由于收到了SIGSEGV信号结束的,而SIGSEGV信号默认handler的动作是打印”段错误"的出错信息,并产生Core文件。因此我们也可以调试Core文件。

在Linux系统下使用gdb调试

下面将采用Linux系统演示使用Core文件进行调试。
Linux基础学习 | gdb调试_第44张图片
bt 堆栈信息中显示,出现问题是在 #0 的_IO_vfscanf_internal 函数中,而函数的调用是在 #1 堆栈中调用的,在往上可逆推至main函数中。 并且,按照 #2 中显示的在main函数的第6行调用点。通过f 2 查看main函数帧中的第6行,可以发现异常出现在 scanf("%d", man); 语句上。
Linux基础学习 | gdb调试_第45张图片

通过观察第5行的man值初始化为0, 而使用scanf函数时,我们忘记添加 & 符号,使得我们输入的数据向着man保存的地址中写人,即我们向 0x000000 写入了数据从而引发了段错误。

至此我们已经找到了此程序产生bug的原因,而需要注意的是,当前程序的bug其实是可避免的,如果我们在编译时使用-Wall选项的话。
在这里插入图片描述
在这里插入图片描述
可以看到不论是Windows下的MinGW还是Linux下的gcc均给出了警告提示

一个好的习惯是打开 gcc 的-Wall 选项,让 gcc 提示所有的警告信息,不管是严重的还是
不严重的,然后把这些问题从代码中全部消灭。——《一站式学习 C 编程》

使用core文件调试

之前提到可以利用 Core 文件进行调试,下面在Linux系统上演示如何通过Core文件调试程序。

使用cman signal查看有关signal的信号。有关cman命令请参考:Linux下的cman中文帮助手册配置
Linux基础学习 | gdb调试_第46张图片

  1. 执行 a.out 程序,程序执行产生了段错误。不过却并没有产生 core 文件。
    Linux基础学习 | gdb调试_第47张图片

  2. ulimit命令 用来限制系统用户对shell资源的访问,其中 -c 参数指定core文件上限(设定core文件的最大值,单位为区块)。
    如下图,本机设定的core上限为0,我们修改一下其上限大小为1000 。
    在这里插入图片描述

  3. 再次执行程序,发现程序在发生段错误的时候产生了Core文件。
    Linux基础学习 | gdb调试_第48张图片

使用 gdb 调试core文件
Linux基础学习 | gdb调试_第49张图片

函数返回时产生段错误

紧接着示例三的程序,为了防止输入非数字字母也会进行转换,这里我们进行字符判断。

#include 

int main(void)
{
     
	int sum = 0, i = 0;
	char input[5];
	scanf("%s", input);
	for (i = 0; input[i] != '\0'; i++) 
	{
     
		if (input[i] < '0' || input[i] > '9') 
		{
     
			printf("Invalid input!\n");
			sum = -1;
			break;
		}
		sum = sum*10 + input[i] - '0';
	}
	printf("input=%d\n", sum);
	return 0;
}

Linux基础学习 | gdb调试_第50张图片
这次我们输入一个很长的字符串
Linux基础学习 | gdb调试_第51张图片
这里程序又出现了一个段错误。使用gdb调试,如图,在windows下使用MinGW的gdb似乎无法调试段错误问题,因此接下来将继续使用Linux环境调试。
Linux基础学习 | gdb调试_第52张图片
在Linux下调试显示在main函数第20行出现问题。
Linux基础学习 | gdb调试_第53张图片
l 20 列出第20行的代码,如图,第20行代码为 } ,因此绝不可能是第20行引发的段错误。 那么我们可以看下一行,参考

如果某个函数的局部变量发生访问越界,有可能并不立即产生段错误,而是在函数返回时产生段错误 ——《一站式学习 C 编程》

20行代码即为main函数的最后一行代码,也就是说,这个段错误在main函数执行完返回的时候发生。

这里发生段错误的原因为,输入的数据过大,将input数组溢出,甚至修改了main函数的栈底数据。(函数在被调用时开辟栈帧,在自己开辟的栈帧中 (栈底位置)保留调用方函数的信息(下一行指令),以便当前函数运行结束后能正常回退至上级函数)。
Linux基础学习 | gdb调试_第54张图片

补充:关于fgets()函数的使用

对于scanf的输入问题我们可以使用 fgets() 函数替代(注:scanf_s() 函数是微软实现的函数,gcc上并没有其实现),但fgets()在使用上有一些需要注意的地方。

有关fgets()函数的使用,这里设计了一个简单的程序,我们通过gdb调试查看fgets()函数究竟做了些什么。

#include 

int main(void)
{
     
	while(1)
	{
     
		char input[10] = "1234567";
		fgets(input, 5, stdin);
		input[4] = '\0';
		printf("%s\n", input);
	}
	return 0;
}

编译并调试
在这里插入图片描述
第一次调试。输入 123456,最终input中被写入 12340670
Linux基础学习 | gdb调试_第55张图片
第二次调试的时候,因为第一没有读取完,输入缓冲区内还有数据,因此无需我们进行输入便自动向下进行。
Linux基础学习 | gdb调试_第56张图片
通过以上调试,我们发现fgets有以下几个特点:

  • fgets函数在读取时,如果设置读取n个长度,则实际读取 n-1 个字符。并将 n 位置设为 ‘\0’ 字符。
  • fgets函数可以读取回车符,即 ‘\n’ 符号。
  • fgets函数将没读取完的数据,任然留在缓冲区内。

而事实上,fgets()函数,当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止。

针对以上几点,我们修改代码如下:

#include 
#include 

int main(void)
{
     
	const int buf_len = 5;		// fgets() 接收的长度
	while(1)
	{
     
		char input[10] = "1234567";
		
		rewind(stdin);	// 刷新输入缓冲区
		fgets(input, buf_len, stdin);		// 最多接收4个输入
		
		// 消除 \n
		int len = strlen(input);			
		if(input[len-1] == '\n') input[len-1] = '\0';
		
		printf("%s\n", input);
	}
	return 0;
}

Linux基础学习 | gdb调试_第57张图片


gdb 常用的调试命令

命令 描述
backtrace(或 bt) 查看各级函数调用及参数
finish 连续运行到当前函数返回为止,然后停下来等待命令
frame(或 f) 帧编号 选择栈帧
info(或 i) locals 查看当前栈帧局部变量的值
list(或 l) 列出源代码,接着上次的位置往下列,每次列 10 行
list 行号 列出从第几行开始的源代码
list 函数名 列出某个函数的源代码
next(或 n) 执行下一行语句
print(或 p) 打印表达式的值,通过表达式可以修改变量的值或者调用函数
quit(或 q) 退出 gdb 调试环境
break(或 b)行号 在某一行设置断点
break 函数名 在某个函数开头设置断点
break … if … 设置条件断点
continue(或 c) 从当前位置开始连续运行程序
delete breakpoints 断点号 删除断点
display 变量名 跟踪查看某个变量,每次停下来都显示它的值
disable breakpoints 断点号 禁用断点
enable 断点号 启用断点
info(或 i) breakpoints 查看当前设置了哪些断点
run(或 r) 从头开始连续运行程序
undisplay 跟踪显示号 取消跟踪显示
start 开始执行程序,停在 main 函数第一行语句前面等待命令
step(或 s) 执行下一行语句,如果有函数调用则进入到函数中
finish 连续运行到当前函数返回为止,然后停下来等待命令
watch 设置观察点
info(或 i) watchpoints 查看当前设置了哪些观察点
x 从某个位置开始打印存储单元的内容, 全部当成字节来看,而不区分哪个字节属于哪个变量
gdb a.out core 使用core文件调试

你可能感兴趣的:(#,Linux基础学习)