相比于OO,OS的debug过程总是显得痛苦。在CLI的情景下,在C语言和汇编语言交错的环境中,我们怎样才能又快又准确地定位bug所在呢?在教学内容/实验环境/MOS调试说明中,有对MOS调试的详细介绍。本文旨在在该部分内容的基础上进一步补充,为同学们提供一份跟着做一定能成的调试攻略。本文内容包括:GDB调试的公式化和瞪眼法的详细分析。
GDB
极简版流程总结
习惯了UI界面下的debug?眷恋IDEA中强大的调试器?很可惜,在上机的环境中,GDB就是我们最强大的debug工具。虽然通过命令行交互的它看上去总是令人害怕,但是不要担心,我从教程中提炼总结了使用GDB进行debug的一般步骤和最常用指令,照着做就能轻松找到所有bug。
假设,要debug的测试点是make test lab=3_4 && make run
,那么就在终端中顺序输入以下命令:
- make test lab=3_4 && make dbg
- b mips_init
- c
- tui enable
你会进入以下界面:
看上去是不是好多了?这样一来就进入了具体的调试界面,如果想要界面更好看、更直观的话请阅读下一部分,如果不在乎GDB的TUI在终端中偶尔的抽风请跳过下一部分去看GDB教程提炼与问题定位方法。
升级版流程总结
刚才的流程虽然简单,但是存在一个问题,在终端窗口中MOS的输出会和GDB的输出重叠到一起,看上去不直观的同时还相当容易出显示上的bug。那么该怎么解决这个问题呢?阅读MOS调试说明,我们可以发现两个好东西:dbg_pts和connect。利用助教们实现好的这两个目标,我们可以做到MOS输出和GDB输出分离。
在终端中输入以下命令:
- tmux
- ctrl+b+(先松一下手)%
- make test lab=3_4 && make dbg_pts
- ctrl+b+(先松一下手)\(\leftarrow\)
- make connect
- b mips_init
- c
- tui enable
如果一切正常,你会看到如图所示的界面(这个图里是执行了几条语句之后的样子),看上去更加简洁明了。
不要忘了结束后关掉tmux!
关掉流程为(注,这部分内容在p0指导书里)
- ctrl+D(退出tmux窗口)
- tmux ls(在退出后的界面中输入这个,记下输出结果的编号)
- tmux kill-session -t 编号
GDB教程提炼与问题定位方法
在完成以上步骤后,就进入了真正的debug环节。接下来的首要任务是找到程序出问题的第一句话,即找出来究竟是在哪句话上程序第一次报告了问题。只需要记住几条命令:
- n,next,执行完这句话,不会进入这句话内部的执行过程
- s,step,执行这句话,如果有内部细节那么进入内部执行过程
- c,continue,执行到下一个断点
- b,breakpoint,添加断点。有两种用法,b 行号和b 函数名
- f,finish,执行完当前所在的函数体,返回到上一层。
- p,print,用法为p 表达式,输出执行到当前所在的这句话时,表达式的值。
- q,quit,退出当前的调试进程。
更具体的,下图是IDEA的调试工具里的几个常用选项,它们分别对应q,c,n,s,f
,现在是不是感觉更亲切了?
具体该怎么找到程序第一次出问题的地方呢?
- (现在是在mips_init中)n,n还是璃月雅言的n
- 如果出了问题,记下来是在哪句话出的问题,重复一遍进入到mips_init的流程
- n,n,还是璃月雅言的n。不过这次要在出问题的那句话之前及时住手
- s,这样就进入了更进一步的层次
- 重复以上步骤,直到第四步出问题的那句话是一个简单的操作,不会进入下一层函数。
解决问题方法
使用上述流程,我们很容易确定程序是在哪一句话头一次出了问题。那么,我们该如何解决问题呢?或者说,怎么根据问题位置推测出问题所在呢?
- 问题出现的位置有可能就是问题存在的位置。可以使用瞪眼法的升级版——神威·奥义·瞪眼法。观察出问题的地方的上下文逻辑,确认这个函数是否能实现我们的想法
- 但是,还有一种可能,即问题出在之前的某一处地方,只是在另一个地方爆发了出来。这个时候需要p大法,输出问题爆发处一切能输出的东西,观察有哪些的值是明显异常,不符合我们想法的。这些异常变量能帮助我们确定程序的哪些部分没能实现预想的功能。然后,是的,还是瞪眼法。
可以发现,在真正解决问题时,GDB并没有太大的用处。接下来我想分析一下瞪眼法这个解决问题的最后一块拼图。(为什么不总结printk输出大法呢?因为没必要,它能干的我GDB都能干)
瞪眼法
或许有个抽象的名字,但是瞪眼法绝对不是一个抽象的方法。以下有两种运用场景:
- 课下,每个测试点都是公开的,且是循序渐进的。完全可以通过测试点的对错情况确认bug的大致位置,然后逐行阅读代码,找到漏洞所在。
- 课上,每个任务的代码修改量相比整个系统而言都是微不足道的。可能出问题的代码只会局限在很小的一个范围内,因此完全可以逐行阅读刚加上的代码,思考这些代码是否能实现构想的功能——感谢勤劳的OS助教,OS的课下测试强度比CO强了不知多少,这让我们在课上时可以大胆假定课下代码不会出错。赞美OS助教!
当然,在OS这个具体的场景下,瞪眼法也是有专门的技巧的。
- 区分练习代码和提供的代码。完全可以认为提供的代码是不会出错的,因此哪怕是在课下,可能出错的地方也就几处练习,这样能大大缩小debug需要遍寻的范围
- 学长智慧与同学智慧,不做过多解释
最后修改于:
最后回复于:
回复主题帖
同学编写的这份 GDB 教程写的很干练!对于想了解更多关于 GDB 用法的说明的同学们,也可以参考假期预习 - GDB:程序的解剖术 中的介绍。
关于 tmux 用法,其实在教程中是有给出的,可以查看一下 0.6.1 节。
最后修改于:2024-04-18 19:56:42
是的,是我的问题,我是根据假期预习的部分编写的tmux部分内容
顺带想提一个小建议,希望能进一步合并假期预习、p0ppt、p0指导书的内容,很多知识点只在这三个东西的某一个上有。这三个参考资料涉及的知识点集合能够画成一张标准的韦恩图,容易给查阅资料或自行学习带来一些困扰
最后修改于:2024-04-18 22:37:17