【Linux】gdb/cgdb调试工具
目录
前言:
一、安装gdb/cgdb
二、调试可执行程序
1.release版本和debug版本的区别
2.调试程序(命令讲解)
2.1 l n(罗列第n行代码)
2.2 r(运行代码)
2.3 b n(在第n行设置断点)
2.4 info b(查看断点信息)
2.5 d n(删除ID为n断点)
2.6 disable n/enable n(禁用断点/启用断点)
2.7 n / s(逐过程/逐语句)
2.8 display 变量名/undisplay 变量名(监视变量并常显示/取消显示)
2.9 p 变量名(显示一次变量值)
2.10 bt(查看函数堆栈)
2.11 until(执行到指定行)
3.使用cgdb
4.使用cgdb发现bug(及命令补充)
4.1 c(跳至下个断点运行)
4.2 finish(立即执行完当前函数)
4.3 info locals(自动窗口)
4.4 watch(监视可能改变的值)
4.5 set var(改变设置的值)
5.条件断点
5.1 新增条件断点
5.2 给条件断点新增条件
三、命令汇总
总结:
前言:
在 Linux 环境中,GDB(GNU Debugger)是一个强大的调试工具。但是这里建议使用cgdb(后面说)。本篇就要来给大家讲解这两种调试工具,并掌握他们的命令。
一、安装gdb/cgdb
当然,要想使用gdb,我们要先安装gdb。
我们使用gdb --version来查看版本。
没有就:
sudo yum install -y gdb
但是gdb不好使用,我们推荐使用另一个工具cgdb,还是要先安装:
sudo yum install -y cgdb
二、调试可执行程序
我们知道,一个可执行程序分为两种,一种是debug(含有调试信息),一种是release(取消调试信息)。
gcc/g++编译程序默认生成release版本。要生成debug版本需要加上-g选项。
我们创建一个code.c文件,并写入以下代码:
#include<stdio.h>
int sum(int s, int e)
{
int sum = 0;
int i = s;
for (; i < e; ++i)
{
sum += i;
}
return sum;
}
int main()
{
printf("process is runing\n");
int start = 1;
int end = 100;
int result = sum(start, end);
printf("process is done, result: %d\n", result);
return 0;
}
1.release版本和debug版本的区别
我们先使用gcc -o myexe code.c生成一个release版本的myexe程序;之后使用gcc -o myexe-debug code.c生成一个debug版本的myexe-debug程序。
gcc -o myexe code.c
gcc -o myexe-debug code.c -g
之后使用readelf命令来显示ELF(Executable and Linkable Format)格式目标文件的信息工具。其中-S选项为显示节头表信息。
所以我们在makefile中这样写:
此时就可以gdb myexe了。
2.调试程序(命令讲解)
GDB(GNU Debugger)是一个强大的调试工具,主要用于调试程序。此时我们就利用它开始调试。
注意:必须是debug版本的可执行程序!
此时就进入了gdb交互模式,直接q会进行退出。
2.1 l n(罗列第n行代码)
l 1命令从第一行开始显示,之后一共显示10行,每次点击回车会自动执行上一次的命令。
可以使用l命令罗列源代码。 gdb会记录最新的一条命令,直接回车就是执行。
2.2 r(运行代码)
我们在gbd中可以直接r运行代码。
2.3 b n(在第n行设置断点)
我们平时喜欢在VS中设置一个断点,之后可以F5,开始执行并调试。
当我们想打断点时,可以 b 行数 (这里b相当于breakpoint),比如在19行打上断点。
打断点的方式有多种,比如:
2.4 info b(查看断点信息)
info b可以去查看断点信息,d可以删除断点(要按照断点编号删除),比如此时我们打上多个断点:
2.5 d n(删除ID为n断点)
在2.4中有示例。
2.6 disable n/enable n(禁用断点/启用断点)
有时候我们并不想直接删除断点,而是禁用断点,比如VS中(当然也可开启):
gdb也可以:
2.7 n / s(逐过程/逐语句)
r也可以直接运行到断点处(也就是执行并调试)。在VS中可以逐过程或者逐语句:
gdb也可以:
所以, 在gdb中同样可以使用n(逐过程不进入函数内部)和s(逐语句进入函数内部)去进行调试。
2.8 display 变量名/undisplay 变量名(监视变量并常显示/取消显示)
其实VS中有很多窗口,比如自动窗口、监视窗口、内存窗口等。gbd中也可以常显示我们要监视的变量,也就是display 变量名(比如:display i。监视i变量)。
我们也可以使用 undisplay 变量名编号 取消常显示。
2.9 p 变量名(显示一次变量值)
当我们只是想临时的看一个变量,可以使用p,打印此时我们要观察变量的值。
2.10 bt(查看函数堆栈)
bt是调用堆栈,也就是看那些函数在堆栈中。
2.11 until(执行到指定行)
此时我们在循环内部执行代码,要执行99次,这样效率就会很低下,所以我们可以使用until直接跳转到制定行数。
3.使用cgdb
我们接下来使用cgdb对刚才的debug版本程序调试:
可以发现cgdb可以把代码可视化出来。
4.使用cgdb发现bug(及命令补充)
此时我们故意往代码中添加一些错误:
#include<stdio.h>
int sum(int s, int e)
{
int sum = 0;
int i = s;
for (; i < e; ++i)
{
sum += i;
}
return sum;
}
int Add(int x, int y)
{
return x + y;
}
int div(int x, int y)
{
int z = x /y;
return z;
}
int main()
{
printf("process is runing\n");
int start = 1;
int end = 100;
int result = sum(start, end);
printf("process is done, result: %d\n", result);
printf("process is done, Add: %d\n", Add(10, 20));
printf("process is done, div: %d\n", div(10, 0));
return 0;
}
此时直接运行程序崩溃, 我们就需要进行调试了。
此时我们在3个函数上面分别设置一个断点(sum函数、Add函数、div函数),调试sum函数之后程序没有崩溃。
4.1 c(跳至下个断点运行)
要在下一个断点处直接运行就是用c命令,直接在下一个断点处继续运行。
VS中继续点F5进入下一个断点执行,此时我们就准确的知道了到底是在哪里崩溃的。
此时我们就准确的知道了到底是在哪里崩溃的 ,再次运行cgdb并直接设置断点,最终确定了问题。
4.2 finish(立即执行完当前函数)
这里一共有3个函数,当我们设置好断点进入函数以后,我们想立即执行完当前所在函数,可以使用finish立即执行完当前函数。
总结:断点 + finish + until + c 这几个命令可以对大的代码块进行debug。
此时我们修正代码:
4.3 info locals(自动窗口)
VS中有一个自动窗口(就是自动看当前执行步骤中有哪些变量并打印出来对应的值),gdb对应的也就是info locals。
4.4 watch(监视可能改变的值)
当我们在调试的时候,要监视哪些值有没有发生变换,就可以在调试的时候使用watch来监视对应变量。
4.5 set var(改变设置的值)
这个就是当我们在调试的时候,比如当时是从1(s变量)加到99(e变量),此时我们可能会感觉太大了,我们再调试的时候设置e的值,比如此时我们求1加到4的值就把e设置为5。
5.条件断点
在VS中,我们可以给一个断点设置一个条件,之后就称为条件断点。
在cgdb中也可以, 条件断点添加的两种方式:
- 新增
- 给已有断点追加
5.1 新增条件断点
finish无法直接完成函数,而是停在条件断点处。
5.2 给条件断点新增条件
使用condition给以有断点新增条件:
三、命令汇总
刚才我们把所有的命令都讲解了一遍并也给出了图解,这里我们把所有的命令汇总一下。
- list(或l) 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
- list(或l) 函数名:列出某个函数的源代码。 r或run:运行程序。
- n 或 next:单条执行。
- s或step:进入函数调用
- break(或b) 行号:在某一行设置断点
- break(或b) 函数名:在某个函数开头设置断点
- info(或i) break(或b) :查看断点信息。
- finish:执行到当前函数返回,然后挺下来等待命令
- print(p):打印表达式的值,通过表达式可以修改变量的值或者调用函数
- p 变量:打印变量值。
- set var:修改变量的值
- continue(或c):从当前位置开始连续而非单步执行程序
- run(或r):从开始连续而非单步执行程序
- delete(或d) breakpoints:删除所有断点
- delete(或d) breakpoints n:删除序号为n的断点
- delete(或d) n:删除序号为n的断点
- disable breakpoints:禁用所有断点
- enable breakpoints:启用所有断点
- info(或i) breakpoints:参看当前设置了哪些断点
- display 变量名:跟踪查看一个变量,每次停下来都显示它的值
- undisplay 变量名:取消对先前设置的那些变量的跟踪
- until X行号:执行至X行
- breaktrace(或bt):查看各级函数调用及参数
- info(或i) locals:查看当前栈帧局部变量的值(自动窗口)
- watch 变量名:监视运行时会发生变化的值
- quit(或q):退出gdb
总结:
本篇命令较多,大多都是记忆成本,但是我们还是需要学会使用的。
大家都要加油学习,知识很多,但是要相信水滴石穿,聚沙成塔,最终会在顶峰相会!