使用GDB调试Linux软件

GDB调试在QT软件开发中的使用(X86架构、WIN/LINUX平台) GNU Debugger,简称GDB,是一个程序调试工具。GDB具备各种调试功效,能针对计算机程序的执行进行追踪与警告,使用GDB的调试人员可以监督及修改程序的内部变量值,甚至监督与修改独立于主程序运作外,以独立个体类型调用(调用使用)的函数。 阅读详情
<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>

使用GDB调试Linux软件
GNU调试器介绍


作者:David Seager 写作时间:01 Feb 2001
翻译:王锐 翻译时间:5 November 2005


绝大部分Linux爱好者在shell中使用GNU调试器或者叫做gdb。Gdb让您能够看到一个程序的内部结构,指出变量的值,设置断点并在源码中进行单步执行。它是一个解决代码中的问题的非常好的工具。在这篇文章中,我将告诉您这个工具有多么酷和多么有用。
编译
在您开始之前,您要调试的程序必须先进行编译,并把调试信息编译进去。这样gdb才能找到变量、代码行和函数。要做到这一点,只要在使用gcc(或g )编译的时候额外加一个“-g”的属性即可,如:
gcc -g eg.c -o eg


运行gdb
Gdb从shell运行,使用命令“gdb”,后面跟上程序名字作为参数,例如“gdb eg”,或者您可以使用file命令加载一个程序用于调试,如“file eg”。这两种方法都假定您是在程序所在的文件下执行命令的。一旦加载成功,就可以在gdb中使用“run”命令来启动程序了。


调试例子
如果没有任何错误您的程序会完成执行,在某个点上gdb可以获得控制权。但是如果出错了呢?这种情况下gdb会取得控制权并中断程序,允许您检查每一样东西的状态并有希望找到原因。为了表现这一场景,我们使用一个例子程序:

#include
int wib(int no1, int no2)
{
int result, diff;
diff = no1 - no2;
result = no1 / diff;
return result;
}
int main(int argc, char *argv[])
{
int value, div, result, i, total;
value = 10;
div = 6;
total = 0;
for(i = 0; i < 10; i )
{
result = wib(value, div);
total = result;
div ;
value--;
}
printf("%d wibed by %d equals %d/n", value, div, total);
return 0;
}

这个程序运行10次循环,使用函数wib()计算一个累加值,最后打印出结果。
把这段程序输入到您最喜欢使用的文本编辑器中,注意空行数量也要一样多,存为“eg1.c”,使用“gcc -g eg1.c -o eg1”编译并使用“gdb eg1”启动gdb。使用“run”运行这个程序会导致输出如下消息:

Program received signal SIGFPE, Arithmetic exception.
0x80483ea in wib (no1=8, no2=8) at eg1.c:7
7 result = no1 / diff;
(gdb)

Gdb指示程序在第7行获得一个算术异常并打印出了行号和函数wib()的参数值,这些值非常有用。使用命令“list”看看第7行周围的源代码,“list”命令每次显示10行代码。再打一次“list”(或按下回车键,回车键表示重复前一次的命令)会把下一个10行代码列出来。从gdb消息中可以看出在第7行进行变量“diff”除变量“no1”运算的时候出错了。
使用“print”命令再加上变量名称看看变量的值。我们可以通过输入“print no1”和“print diff”查看“no1”和“diff”的值,结果如下:

(gdb) print no1
$5 = 8
(gdb) print diff
$2 = 0

Gdb显示“no1”等于8而“diff”等于0。根据这些值和第7行代码我们可以推出这个算术异常是由于除0引起的。列表显示变量“diff”在第6行计算出来,我们可以计算一下这个值,使用“print no1-no2”。Gdb告诉我们wib函数的两个参数都是8,那么我们可能想要检查调用了wib()函数的main()函数以看看这件事是什么时候发生的。我们还可以允许我们的程序自然死亡,只要告诉gdb让程序继续执行就可以了,使用命令“continue”。

(gdb) continue
Continuing.
Program terminated with signal SIGFPE, Arithmetic exception.
The program no longer exists.

使用断点
为了看到main()函数中在发生什么,我们可以在特定的行上或程序代码的一个函数上设置断点,gdb会在执行到那里时中断。我们可以通过“break main”在main()函数中设置一个断点,或者指定任何一个我们感兴趣的函数名字。为了达到我们的目的,我们要使程序在执行到调用wib()函数前中断。使用“list main”命令会打印出从main()函数开始的源代码,再打一个回车键会在第21行看到对函数wib()的调用。我们使用“break 21”在第21行设置一个断点。Gdb会有如下响应:

(gdb) break 21
Breakpoint 1 at 0x8048428: file eg1.c, line 21.

这表示它已经在我们请求的行设置了一个断点。“run”命令会使程序执行到断点处返回。gdb会产生一个消息显示它在哪个断点处中断并且此处是程序中的哪个位置:

Breakpoint 1, main (argc=1, argv=0xbffff954) at eg1.c:21
21 result = wib(value, div);

使用“print value”和“print div”会显示对于第一次wib()调用来说变量是10和6,使用“print i”会显示0。您会很高兴地看到gdb显示所有变量只需使用“info locals”,为您省了许多次敲击键盘的动作。
从前面的研究知道问题发生在“value”和“div”相等的时候,所以使用“continue”继续执行直到再一次到达断点1。对于这次迭代“info locals”显示value等于9,div等于7。
我们可以不使用continue而使用“next”,这个命令可以单步执行程序,这样我们可以看看“value”和“div”是怎样改变的。Gdb会有如下响应:

(gdb) next
22 total = result;

多次使用会车键会显示一个加和一个减,如下:

(gdb)
23 div ;
(gdb)
24 value--;

另外两个回车把我们带到21行,准备调用wib()。“info locals”显示现在“div”等于“value”,预示着即将到来的问题。如果有兴趣还可以通过单步执行进入到wib()函数内部再次看看这个除法错误,使用“step”命令就可以了(与“step”相对的是“next”命令,这个命令会跳过函数调用),接下来使用“next”获得一个“result”的计算值。
既然我们已经完成了调试,可以通过“quit”命令退出gdb。因为程序还在运行,这个动作会终止程序,gdb会有提示以确认您想要终止。
更多断点和检查点
在上面的例子中我们在21行设置了一个断点,因为我们对调用wib()函数前的“value”值什么时候等于“div”值感兴趣。我们要连续执行程序两次才能到达这个点,然而通过给断点设置条件我们可以使gdb只在“value”确实等于“div”的时候中止。在定义断点的时候设置条件,我们可以指定“break <line number> if <conditional expression>”。把eg1重新载入gdb并使用如下命令:

(gdb) break 21 if value==div
Breakpoint 1 at 0x8048428: file eg1.c, line 21.

如果一个断点如1号断点已经在21行定义好了,我们可以使用“condition”命令在这个断点上设置条件:

(gdb) condition 1 value==div

使用“run”运行eg1.c,gdb会在“value”等于“div”时中断,避免了手工打印“continue”直到它们相等为止。当调试C程序时断点条件可以是任何合法的C表达式,事实上您用哪种语言哪种语言的合法表达式就可以使用。在条件中指定的变量必须在您设置断点的行的变量作用域范围内,否则这个表达式就没有意义。
可以使用“condition”命令指定一个断点断点但是不加表达式从而使这个断点成为非条件型表达式。例如“condition 1”设置断点1为非条件型。
要看看当前定义了哪些断点以及它们的条件,使用命令“info break”:

(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048428 in main at eg1.c:21
stop only if value == div
breakpoint already hit 1 time

要满足什么条件,多少次命中,以及这个断点是否是可用的在“Enb”列显示出来。可以使用命令“disable <breakpoint number>”把断点置为不可用,使用“enable <breakpoint number>”置为可用或者使用“delete <breakpoint number>”整个删除,例如“disable 1”阻止了在第1号断点上的中断。
如果我们对于什么时候“value”等于“div”更感兴趣,我们可以设置一个不同类型的断点,叫做检查点(watch)。一个检查点会在特定的表达式改变了值时中断程序执行,但是它必须是在表达式中的变量在作用范围之内时设置。要在作用范围内获得“value”和“div”,我们可以在main()函数上设置一个断点并且运行程序,当main()断点命中后设置我们的检查点。重启启动gdb并载入eg1,使用如下命令:

(gdb) break main
Breakpoint 1 at 0x8048402: file eg1.c, line 15.
(gdb) run
...
Breakpoint 1, main (argc=1, argv=0xbffff954) at eg1.c:15
15 value = 10;

当“div”改变时要保持跟踪,我们可以使用“watch div”,但是因为我们想要“div”等于“value”时中断,所以使用如下命令:

(gdb) watch div==value
Hardware watchpoint 2: div == value

当“div==value”从0(false)到1(true)变化时继续执行会导致Gdb中断:

(gdb) continue
Continuing.
Hardware watchpoint 2: div == value
Old value = 0
New value = 1
main (argc=1, argv=0xbffff954) at eg1.c:19
19 for(i = 0; i < 10; i )

“info locals”命令显示“value”确实等于“div”,并且都是8。
使用“info watch”(这个命令等同于“info break”)列出定义好的检查点,检查点可以使用和断点同样的语法变为可用状态、不可用状态和删除。
核心文件
在gdb下运行程序使得查找bug更加容易,但是在调试器之外程序经查会死掉,只留下一个核心文件。Gdb可以载入核心文件并让您可以在它死掉前检查程序状态。
在gdb外运行我们的例子程序eg1会导致内核崩溃:

$ ./eg1
Floating point exception (core dumped)

要带着一个核心文件启动gdb,通过shell使用命令“gdb eg1 core”或者“gdb eg1 -c core”。Gdb会载入核心文件,eg1的程序列表,显示程序怎样终止的并提供一条消息,非常像我们之前在gdb下运行程序那样。

...
Core was generated by `./eg1'.
Program terminated with signal 8, Floating point exception.
...
#0 0x80483ea in wib (no1=8, no2=8) at eg1.c:7
7 result = no1 / diff;

这时我们可以使用“info locals”、“print”、“info args”和“list”查看由除0导致的值。命令“info variables”会打印出所有程序变量的值,但是要费很长时间因为gdb从C库开始打印变量而不是从我们的程序代码开始。为了更容易地发现在叫做“wib()”函数中发生了什么,我们可以使用gdb的栈命令。
栈跟踪
程序“call stack”是一个函数列表。每个函数和它的变量都指定了一个“frame”,而最近调用的函数则在0号frame中(叫做底层frame)。要打印栈,使用命令“bt”(是“backtrace”的缩写)。

(gdb) bt
#0 0x80483ea in wib (no1=8, no2=8) at eg1.c:7
#1 0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21

这显示了函数wib()是由main()在21行调用的(立即使用“list 21”可以证明这一点),wib()在0号frame中,而main()在1号frame中。因为wib()在0号frame中,这是程序内执行到发生算术错误的时候所在的函数。
当您使用“info locals”命令时gdb打印出当前frame的局部变量,默认情况下是中断的函数(0号frame)。当前的frame使用命令“frame”打印出来。要查看main函数中的变量(在1号frame中),我们可以通过命令“frame 1”转到1号frame下,然后再使用“info locals”:

(gdb) frame 1
#1 0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21
21 result = wib(value, div);
(gdb) info locals
value = 8
div = 8
result = 4
i = 2
total = 6

这显示了错误发生在第三次循环(i等于2)时,此时“value”等于“div”。
可以通过显式指定frame号或命令“frame up”或“frame down”来转换frame,使用“frame up”下上移,使用“frame down”向下移。要获得更多关于一个frame的信息诸如它的地址和程序语言,您可以使用命令“info frame”。
Gdb栈命令在程序执行时起作用,就像核心文件一样,所以对于复杂的程序您可以在它运行时跟踪到这个程序是怎样进入函数的。
绑定到其它进程上
为了调试核心文件或程序,gdb可以绑定到一个正在运行的进程上(这个进程的程序必须编译进了调试信息)并进入到进程内。这是通过指定您想要绑定gdb的程序进程ID而不是核心文件名实现的,这里有个例子程序,这个例子程序会进行循环并休眠:

#include
int main(int argc, char *argv[])
{
int i;
for(i = 0; i < 60; i )
{
sleep(1);
}
return 0;
}

使用“gcc -g eg2.c -o eg2”编译并使用“./eg2 &”运行它。当它在后台运行起来的时候把进程ID的信息找到,在这里是1283:

./eg2 &
[3] 1283

启动gdb并指定您的pid,在这个例子中使用“gdb eg2 1283”。Gdb会寻找一个叫做“1283”的核心文件并且当没有找到它时会绑定并进入到1283进程中,无论它在哪里运行(在这里可能是在sleep()中):

...
/home/seager/gdb/1283: No such file or directory.
Attaching to program: /home/seager/gdb/eg2, Pid 1283
...
0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6
(gdb)

这里所有的常用gdb命令都可以用。我们可以使用“backtrace”查看我们程序所在位置与main()的关系以及main()的frame号是多少,然后转换到那个frame,并找出我们进入for循环多少次了:

(gdb) backtrace
#0 0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6
#1 0x400a877d in __sleep (seconds=1) at ../sysdeps/unix/sysv/linux/sleep.c:78
#2 0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
(gdb) frame 2
#2 0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
7 sleep(1);
(gdb) print i
$1 = 50

当我们完成任务后可以使用“detach”命令离开而让进程继续执行或者使用“kill”命令终止这个进程。我们还可以在进程1283下绑定到eg2,首先使用“file eg2”载入文件,然后使用命令“attach 1283”绑定。
其它灵巧的跟踪
Gdb允许您在不退出调试环境的情况下运行shell命令,使用“shell [commandline]”实现,对于在调试时修改源代码是很有用的。
最后您可以在程序运行时修改变量值,使用“set”命令。再次在gdb下运行“eg1”,在第7行设置一个条件断点(这是计算result的地方),使用命令“break 7 if diff==0”并运行程序。当gdb中断执行后可以把“diff”置为非0以使程序能够运行完成:

Breakpoint 1, wib (no1=8, no2=8) at eg1.c:7
7 result = no1 / diff;
(gdb) print diff
$1 = 0
(gdb) set diff=1
(gdb) continue
Continuing.
0 wibed by 16 equals 10
Program exited normally.

结论
GNU调试器放到任何程序员的武器库中都是一个功能非常强大的工具。我只是讲述了它可以做的一小部分工作,要找到更多的话,我建议您阅读GNU调试器的手册。

C语言调试 1.调试的概念以及调试器的选择 在编写代码的过程中,相信大家肯定遇到过这样的情况:代码能够编译通过,没有语法错误,但是运行结果却不对,反复检查了很多遍,依然不知道哪里出了问题。这个时候,就需要调试程序了。 所谓调试(Debug),就是让代码一步一步慢慢执行,跟踪程序的运行过程。比如,可以让程序停在某个地方,查看当前所有变量的值,或者内存中的数据;也可以让程序一次只执行一条或者几条语句,看看 阅读详情

相关推荐

二维otsu算法python_OpenCV-Python系列之OTSU算法

上一个教程中,我们谈到了关于图像二值化的两种方法,一种是固定阈值法,另一种是自适应阈值法,总的来说,自适应阈值法在某些方面要由于固定阈值法,但还没完,这次我们将隆重介绍我们的重量级选手,也就是OTSU算法(又称为大津算法和最大类间方差法)。最大类间方差法是1979年由日本学者大津提出的,是一种自适应阈值确定的方法,又叫大津法,简称OTSU,是一种基于全局的二值化算法,它是根据图像的灰度特性,将图像...

weixin_28834169的博客 1426

调试:设置断点

程序 编写程序: #include <stdio.h> int main(int argc, char** argv) { unsigned long long int n, sum; n = 1; sum = 0; while (n <= 100) { sum = sum + n; n = n + 1; } return 0; } 将程序编译成可执行文件: # 使用 GDB 调试某个可执行文件,该文

OceanStar的博客 5505

在CoppeliaSim里实现一个最简单的强化学习

前言 上一篇文章中介绍了CoppeliaSim的一些基本知识和一个简单的用python去控制CoppeliaSim里面四旋翼无人机运动的一个例子。文章链接 :https://blog.youkuaiyun.com/qq_37051669/article/details/115324851 本文将继续介绍如何利用CoppeliaSim的物理环境去实现一个最简单的强化学习。 如何实现强化学习 强化学习(Reinforcementlearning)是机器学习中的一个领域,强调如何基于环境而行动,以取得最大化的预期利.

qq_37051669的博客 3765

Gdb调试器(读书笔记)

Gdb调试器调试是所有程序员都会面临的问题。如何提高程序员的调试效率,更好更快地定位程序中的问题从而加快程序开发的进度,是大家共同面对的。就如读者熟知的Windows下的一些调试工具,如VC自带的如设置断点、单步跟踪等,都受到了广大用户的赞赏。那么,在Linux下有什么很好的调试工具呢?本文所介绍的Gdb调试器是一款GNU开发组织并发布的UNIX/Linux下的程序调试工具。虽然,它没有图

流氓D的专栏 1438

从.har文件到Postman集合:一键转换的完整教程(附避坑指南)

本文详细介绍了如何将.har文件高效转换为Postman集合的完整教程,涵盖基础操作到高级技巧,包括认证信息处理、环境变量提取等痛点问题的解决方案。通过抓包工具记录的.har文件,开发者可以快速生成Postman集合,提升API调试与测试效率,特别适合前后端分离开发模式下的团队协作。

weixin_26795617的博客 384

CTFmisc-------Jack

每一个文件里面都有不一样的base64编码,我尝试将其解密,发现解出来的是乱码,所以我想到真正的flag藏在这些字符串中,所以我们要写代码把字符串提取出来。提取出来后,得到很多的base64的编码,要一个一个的解码很麻烦,猜到里面有flag字样的base64编码,所以我们将flag进行base64编码,1.下载得到Jack文件夹,里面有很多的txt文件。发现这个字符串是众多编码中唯一一个没有==然后从那些base编码中寻找Zmxh字段。

woshicainiao666的博客 1万+

使用 GDB 调试 Linux 软件

使用 GDB 调试 Linux 软件 简介: Linux 的大部分特色源自于 shell 的 GNU 调试器,也称作 gdb。gdb 可以让您查看程序的内部结构、打印变量值、设置断点,以及单步调试源代码。它是功能极其强大的工具,适用于修复程序代码中的问题。在本文中,David Seager 将尝试说明 gdb 有多棒,多实用。 编译 开始调试之前,必须用程序中

Rain的专栏 797

企业级托管钱包架构设计与MPC密钥管理:基于Go语言的生产级实践

本文探讨了2026年Web3生态中企业级托管钱包的技术架构与实现方案。文章指出,传统私钥管理模式已无法满足机构级资产管理需求,而基于多方安全计算(MPC)和门限签名(TSS)的技术方案成为主流选择。研究分析了托管钱包的四层架构设计,详细解读了MPC的核心技术原理,包括分布式密钥生成(DKG)和签名协议演进。通过Go语言实现案例,展示了密钥分片管理、DKG流程和交易签名等核心模块的代码实现。文章还提出了生产部署的安全原则、容灾设计和监控体系,并讨论了密钥撤销等现存挑战及TEE融合等未来趋势。研究为构建安全、高

分享AI、区块链技术及应用,未来已来 81

使用GDB调试LINUX软件

转自 http://www.ibm.com/developerworks/cn/linux/sdk/gdb/ Linux 的大部分特色源自于 shell 的 GNU 调试器,也称作 gdb。gdb 可以让您查看程序的内部结构、打印变量值、设置断点,以及单步调试源代码。它是功能极其强大的工具,适用于修复程序代码中的问题。在本文中,David Seager 将尝试说明 gdb 有多棒,多实用

jinrongbo的专栏 413

Arduino与Proteus仿真实例-HG7881(L9110)驱动直流电机仿真

HG7881(L9110)驱动直流电机仿真 HG7881 (L9110) 双通道电机驱动器模块。 HG7881 (L9110) 是一款紧凑型电机驱动芯片,支持 2.5-12V 电压范围,800mA 连续电流。 这些芯片具有内置输出钳位二极管以保护敏感的微控制器电子设备。 它们适用于非常小的机器人项目。 每个 HG7881 (L9110) 芯片都能够使用两个数字控制输入驱动单个直流电机。 一个输入用于选择电机方向,而另一个用于控制电机速度。 速度是通过使用 PWM 脉冲宽度调制来控制的。 电机驱动器通常具有

视觉与物联智能 3050

基于YOLOv8的应急通道占用监测系统设计与实现

通过详细的数据集选择、模型训练、代码实现等内容,本博客为您提供完整的系统搭建过程,帮助您构建高效的监测系统。然而,在一些建筑物和公共场所中,应急通道经常被不当占用,导致紧急情况下无法及时使用,严重影响人员的疏散效率。YOLOv8是YOLO系列中的最新版本,它通过引入深度学习的优化和改进,在保证检测速度的同时,显著提升了检测精度。同时,随着深度学习技术的不断发展,检测精度与速度的提升将使得该系统在更广泛的场景中应用。:如果已有监控设备,可以采集应急通道的实时视频,并进行标注,生成占用监测数据集。

YOLO 797

【Linux软件包管理器yum、编辑器vim和调试器gdb的简单使用】

文章目录一级目录编辑器-vim的使用vim的基本模式命令模式 一级目录 编辑器-vim的使用 vim是一款多模式编辑器,并且vim是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行,也可以运行于x window、 mac os、windows。并且从定位上来说vim编辑器和记事本差不多。 我们可以在Linux系统命令行下输入vim就会有vim的信息出现,如下图: vim的基本模式 vim其实有很多种模式,下面我们先来分析下面三种模式: 命令模

平凡的指针的博客 1224

基于STM32、HAL库的 CH9102X USB转UART收发器 驱动程序设计

/ 或 UART_HWCONTROL_RTS_CTS。// 其他函数实现类似,使用HAL_UART_Transmit_IT, HAL_UART_Receive_IT等。UART_TXD <---> USARTx_RX (STM32接收)UART_RXD <---> USARTx_TX (STM32发送)* @param huart: UART句柄指针。* @param huart: UART句柄指针。* @param huart: UART句柄指针。* @param huart: UART句柄指针。

corlin6688的博客 506

arm9260 linux编译,关于linux9260烧写软件和gdb调试器的使用

9260的烧写软件SAM-BA有自己的linux版本,大家可以到网站上去下载,这里作一个烧录笔记,由于9260的usb设备端口需要一个usbserial的linux驱动,所以我们每次使用之前都要加载一次驱动,linux4sam的网站上告诉你先卸载usbserial是担心你使用了别的usb device设备,所以,如果你没有重新启动电脑的话,你将只需要加载一次而已。多次烧写。root@yongtao...

weixin_42583683的博客 207

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

上一篇: Java设计模式之计数代理模式
下一篇: Ajax 陷阱(Ajax Gotchas)
java169
博客等级 码龄18年 27粉丝 2568原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值