linux学习6,没有图形界面和鼠标,还能怎么找bug?gdb工具介绍
sinye56 2024-10-29 16:19 13 浏览 0 评论
前面几节,我们一起下载和编译了 linux 内核,也利用 busybox 制作了文件系统让编译好的内核成功跑起来了。本节介绍一下 linux 下的单步调试工具,方便我们后续调试 linux 内核。
linux 下程序开发不可避免遇到 bug
在程序开发中,调试和修复 bug 通常会占相当比例的时间,应该没有程序员能够一次性写出绝对完美的代码,除非程序只需要打印 hello world。在《C语言入门第12节,看书都懂,真让写代码却不知道如何入手咋办》一节,我们介绍过“增量式”开发,使用这种模式开发过程中,如果发现了错误,能够在很小的代码范围内查找并解决。然而不幸的是,并不是所有情况下都是“增量式”开发,比如接手别人的有 bug 的代码时,或者帮程序媛妹子们查找 bug 时,bug 可不是一下子就能找到的。
有些 bug 固然一眼就能看出,例如手误写错了初值。但是,有些 bug 可不容易看出,即使最终发现原来辛辛苦苦找到的 bug 原来如此低级。
程序员可能花了一周时间才发现,造成程序产生巨大错误的原因是,自己把应该是 a<=b 语句写成 a<b 了。
有时候找 bug 我们会希望,如果程序能够每执行一行,就停下来一次就好了,这样我们就能够知道这一行代码的执行结果究竟对不对了。程序员们通常称这种调试方法为“单步调试”。事实上,这并不难做到,相信习惯使用 IDE 开发的朋友们都应该使用过单步调试。
很多程序员从 windows 开发转向 linux 开发,最大的不适应就是惯用的 IDE 没法使用了,甚至连图形界面都没了,鼠标居然也用不上了。于是又去找能够在 linux 下使用的 IDE,最好还能和 windows 下的 IDE 功能一样。其实,在哪种环境下开发,只是习惯问题,如果我们一开始就是在 linux 这种没有界面,不用鼠标的环境下做程序开发,突然让在 windows 下开发,肯定也是不适应的。既然坚定了自己的嵌入式开发生涯,就没必要一直迁就于 windows。何况,并不是所有的 linux 环境都允许我们安装 IDE 的。
linux 下开发也能单步调试吗
linux 没有界面,鼠标也几乎没用,也能单步调试吗?答案显然是肯定的,GDB 工具的调试功能绝对不亚于各种 IDE。
gdb 就是一个调试工具,一般来说,GDB主要帮助你完成下面四个方面的功能:
- 启动程序,可以按照我们自定义的要求随心所欲的运行程序。
- 可让被调试的程序在指定的调置的断点处停住。(断点可以是条件表达式)
- 当程序被停住时,可以检查此时程序中所发生的事。
- 可以改变程序的某些行为,将一个 bug 产生的影响修正从而测试其他 bug。
gdb 工具的使用
我们还是从实例出发,介绍 GDB 工具的使用。现在需要开发一个程序:
要求先定义一个 square 函数用于求整数的平方值,然后在 main 函数中打印从 0 到 9 的平方值
这个要求是容易实现的,请看下面的 C 语言程序:
#include <stdio.h> int square(int i) { int s; s = i*i; return s; } int main() { int j, k; for(j=1; j<10; j++){ //不小心把j=0写成j=1了 k = square(j); printf("square(%d) = %d\n", j, k); } return 0; }
因为手误,程序员小明不小心把j=0写成j=1了。现在,我们使用 GDB 工具来单步调试这个程序,首先,编译这个程序:
# gcc t.c -g
-g 参数可以在编译过程中保留调试信息(主要是各种符号),便于调试。使用 GDB 的方式是简单的:
# gdb a.out
这样,我们就进入了 gdb 的环境中:
输入 run,也可以输入缩写 r,按回车:
(gdb) run Starting program: /lccRoot/C/tmp/a.out square(1) = 1 square(2) = 4 square(3) = 9 square(4) = 16 square(5) = 25 square(6) = 36 square(7) = 49 square(8) = 64 square(9) = 81 [Inferior 1 (process 20996) exited normally] (gdb)
可以看出,gdb 的 run 命令和它的字面意思一样,就是让程序跑起来。可以看出,程序少输出了一条内容。现在使用“单步调试”法来定位错误。首先,需要下断点(程序运行时,遇到断点会停下来),因为假设我们并不知道错误在哪里,所以断点就下在程序入口,也即 main 函数处:
(gdb) b main Breakpoint 1 at 0x40054b: file t.c, line 14. (gdb) r Starting program: /lccRoot/C/tmp/a.out Breakpoint 1, main () at t.c:14 14 for(j=1; j<10; j++){ (gdb) l 9 10 int main() 11 { 12 int j, k; 13 14 for(j=1; j<10; j++){ 15 k = square(j); 16 printf("square(%d) = %d\n", j, k); 17 } 18 (gdb)
下好断点后,输入 r 让程序跑起来。能够发现,这次程序并没有直接运行到最后,而是停在了第 14 行,输入 l(list),可以把第 14 行附近的代码打印出来。接着,我们输入 n(next),就可以一行一行的执行代码了。
(gdb) n 15 k = square(j); (gdb) n 16 printf("square(%d) = %d\n", j, k); (gdb) n square(1) = 1 14 for(j=1; j<10; j++){ (gdb)
到这里,发现程序输出的是 square(1) = 1,这显然是不对的,对照一下代码,错误的位置就很明显了。gdb 的单步调试不仅能发现错误,还能帮助我们理解代码:
(gdb) n 15 k = square(j); (gdb) s square (i=2) at t.c:6 6 s = i*i; (gdb) p s $4 = 32767 (gdb) p i $5 = 2 (gdb) n 7 return s; (gdb) p s $6 = 4
执行到 k=square(j); 时,输入 s(step into)就可以进入 square 函数的代码块,这时程序停在了 s=i* i; 处,输入 p s,可以把此时内存里 s 的值打印出来。发现 s 居然等于 32767,打印出 i,等于 2,这是为什么呢?因为此时 s=i* i; 还没有执行,s 作为局部变量没有定义初值,所以此时它的值是未知的。输入 n,程序执行完 s=i* i;,发现 s 等于 4,正常了。
关于 C 语言的局部变量,可参考《局部变量为何只能在函数中使用,必须初始化吗》一节。
输入 bt:
(gdb) bt #0 square (i=2) at t.c:7 #1 0x000000000040055e in main () at t.c:15 (gdb)
可以看出,此时程序是从 main 函数进入 square 函数的。gdb 的功能显然还不止于此,本节仅作为抛砖引玉的作用。以后我们调试 linux 内核时,会继续使用 GDB 的。
GDB 的常用命令
gdb 全速运行
- (gdb)run,也可只输入 r,程序会开始运行,停在第一个断点处。
- (gdb)continue,也可只输入 c,程序继续运行,停在下一个断点处。
gdb 添加搜索代码源文件路径
- (gdb)directory [path],也可输入 dir [path],将 path 添加到搜索目录中。
gdb 打印代码
- gdb + 可执行程序即可进入 gdb 调试:
- 输入 l 可以打印出代码,如果想输出指定行号的代码,可以加上数字,例如 l 5。
gdb 断点
下断点有两种方式:
* (gdb)break(也可只输入b) + 函数名
* (gdb)break + 行号
* (gdb)break + 条件。这个命令必须在变量i被定义之后才会成功运行,为了解决这个问题,首先在变量 i 被定义的后一行设置中断,然后使用run命令运行程序,程序暂停后就可以使用watch i==99设置断点了。例如 break 7 if i==99, watch i==99
查看断点
- 显示当前gdb的断点信息:(gdb) info break
- 删除指定的某个断点:(gdb) delete breakpoint 1
- 该命令将会删除编号为1的断点,如果不带编号参数,将删除所有的断点:(gdb) delete breakpoint
- 禁止使用某个断点:(gdb) disable breakpoint 1,该命令将禁止断点1,同时断点信息的 (Enb)域将变为 n
- 允许使用某个断点:(gdb) enable breakpoint 1,该命令将允许断点1,同时断点信息的 (Enb)域将变为 y
- 清除源文件中某一代码行上的所有断点:(gdb)clear number
- >注:number 为源文件的某个代码行的行号
变量检查赋值编辑
- whatis:识别数组或变量的类型
- ptype:比whatis的功能更强,他可以提供一个结构的定义
- set variable = value:将值赋予变量
- print variable = value or p variable = value : 除了显示一个变量的值外,还可以用来赋值
单步执行编辑
- next 不进入的单步执行
- step 进入的单步执行如果已经进入了某函数,而想退出该函数返回到它的调用函数中,可使用命令finish
函数调用编辑
- call name 调用和执行一个函数
- (gdb) call gen_and_sork(1234,1,0)
- (gdb) call printf(“abcd”)
- =4
- finish 结束执行当前函数,显示其返回值(如果有的话)
欢迎在评论区一起讨论,质疑。文章都是手打原创,每天最浅显的介绍C语言、linux等嵌入式开发,喜欢我的文章就关注一波吧,可以看到最新更新和之前的文章哦。
相关推荐
- Xfce桌面环境升级截图工具,支持AVIF和JPEG XL格式
-
IT之家5月20日消息,Xfce团队近日将屏幕截图工具xfce4-screenshooter更新至1.10.4版本,允许用户将截图保存为AVIF和JPEGXL两种无损图像格...
- pyautogui:截图及定位功能_如何截图定位
-
截图函数PyAutoGUI可以截取屏幕截图,将其保存到文件中,并在屏幕中查找图像。如果您有一个小图像,例如需要单击并希望在屏幕上找到它的按钮,这将非常有用。这些功能由PyScreeze模块提供,该模块...
- 如何在Ubuntu上释放/boot分区的空间?
-
前几天,我收到一个警告,/boot分区已经几乎满了,没有剩余空间了。?来源:linux.cn?作者:AnkushDas?译者:郑?(本文字数:2868,阅读时长大约:4分钟)前几天,...
- 新Linux木马发现:每30秒截图并用麦克风录音
-
近期在Linux平台发现的木马程序能够定期截图,并可通过已连接的麦克风来录音,不同于通常设计用来攻击Linux端服务器的恶意程序。该木马程序最早被俄罗斯安全公司DoctorWeb发现,他们将其称之为...
- 使用 Rust 实现屏幕截图功能_rust怎么设置快捷键
-
前言在一些网安产品应用程序中,屏幕截图是常见的需求。对于想要使用Rust来进行屏幕截图的开发者,今天我们将介绍如何使用Rust实现屏幕截图功能,捕获屏幕内容并保存为图像文件。本文的目标本文件将...
- Linux中的截图工具 - Flameshot_linux里面比较好的截图工具
-
Flameshot[1]是一款功能强大但易于使用的屏幕截图软件,中文名称火焰截图。Flameshot简单易用并有一个CLI版本,所以你也可以从命令行来进行截图。Flameshot是一个Lin...
- linux/unix中如何使用sed一次替换多个模式
-
文本处理是我们使用linux/unix必须掌握的一项技能。前两篇文章介绍了如何删除文本文件中的空行以及如何删除文本文件中包含指定字符串的行。本文主要介绍如何使用sed命令一次替换多个模式。以下是测试中...
- Linux环境中的shell echo命令使用技巧
-
简介:在平常编写LinuxShell脚本的时候,常常会用到echo命令。这echo命令的作用呢,就是在显示器上显示出一段文字。这个命令挺简单的,可它在脚本编程还有日常的系统管理里,那可是...
- 7 个有趣的 Linux 命令,我10年linux经验都没见过一个
-
Linux命令行可以是高级用户和系统管理员的法宝,Linux不仅仅是枯燥乏味的系统,Linux是由喜欢玩乐的人开发的,他们创建了一系列好玩的命令,当你想要微笑时,试着自己玩这些。1、蒸汽机车(s...
- Linux文本处理三剑客之sed命令全面解析,一文就够了
-
作用sed命令是一个非交互式的行文本编辑器,它能对文件内容进行编辑,默认每次处理文本文件中所匹配到一行内容到模式空间,然后用后面的命令进行操作,操作完成之后,会把模式空间里面的内容输出到屏幕上,然后把...
- Linux下常用命令及使用方法详解(可收藏!!!)
-
总结有哦福利哦ls文件属性:-:普通文件d:目录文件b:块设备c:字符设备文件l:符号连接文件p:命...
- Python也有“管道”操作,你知道吗?
-
在编程世界中,"管道"是一项充满魅力的功能。像Unix和Linux中的管道一样,Python也有“管道”操作。本文将深入研究Python的管道操作,提供一些示例来帮助你理解和掌握它。什么是管...
- 运维人员常用的 Linux 命令汇总_运维人员常用的 linux 命令汇总表
-
作为运维人员,这些常用命令不得不会,掌握这些命令,工作上会事半功倍,提供工作效率。一.文件和目录1.cd命令,用于切换当前目录,它的参数是要切换到的目录的路径,可以是绝对路径,也可以是相对路径。cd...
- Linux sed 命令详解_linux教程:sed命令的用法
-
简介sed的全称是:StreamEditor流编辑器,在Linux中是一个强大的文本处理工具,可以处理文件或标准输入流。基本语法sed[options]'command'file通过...
- Linux tail命令用法_tail在linux
-
tail命令是Linux/Unix中的一个命令行工具,用于查看文件的最后几行或动态实时查看文件(tail-f)。语法tail[option(s)][file(s)]主要选项--n:指定显示文...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- oracle忘记用户名密码 (59)
- oracle11gr2安装教程 (55)
- mybatis调用oracle存储过程 (67)
- oracle spool的用法 (57)
- oracle asm 磁盘管理 (67)
- 前端 设计模式 (64)
- 前端面试vue (56)
- linux格式化 (55)
- linux图形界面 (62)
- linux文件压缩 (75)
- Linux设置权限 (53)
- linux服务器配置 (62)
- mysql安装linux (71)
- linux启动命令 (59)
- 查看linux磁盘 (72)
- linux用户组 (74)
- linux多线程 (70)
- linux设备驱动 (53)
- linux自启动 (59)
- linux网络命令 (55)
- linux传文件 (60)
- linux打包文件 (58)
- linux查看数据库 (61)
- linux获取ip (64)
- linux进程通信 (63)