【总结分享】web端GDB调试的公式化流程与定位/解决bug的方法分析

纪郅炀
🔖︎ 6 订阅
👍︎ 16 点赞
🔝︎ 已置顶

相比于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,那么就在终端中顺序输入以下命令:

  1. make test lab=3_4 && make dbg
  2. b mips_init
  3. c
  4. tui enable

你会进入以下界面:

image.png

看上去是不是好多了?这样一来就进入了具体的调试界面,如果想要界面更好看、更直观的话请阅读下一部分,如果不在乎GDB的TUI在终端中偶尔的抽风请跳过下一部分去看GDB教程提炼与问题定位方法。

升级版流程总结

刚才的流程虽然简单,但是存在一个问题,在终端窗口中MOS的输出会和GDB的输出重叠到一起,看上去不直观的同时还相当容易出显示上的bug。那么该怎么解决这个问题呢?阅读MOS调试说明,我们可以发现两个好东西:dbg_pts和connect。利用助教们实现好的这两个目标,我们可以做到MOS输出和GDB输出分离。

在终端中输入以下命令:

  1. tmux
  2. ctrl+b+(先松一下手)%
  3. make test lab=3_4 && make dbg_pts
  4. ctrl+b+(先松一下手)\(\leftarrow\)
  5. make connect
  6. b mips_init
  7. c
  8. tui enable

如果一切正常,你会看到如图所示的界面(这个图里是执行了几条语句之后的样子),看上去更加简洁明了。

image2.png

不要忘了结束后关掉tmux!
关掉流程为(注,这部分内容在p0指导书里)

  1. ctrl+D(退出tmux窗口)
  2. tmux ls(在退出后的界面中输入这个,记下输出结果的编号)
  3. tmux kill-session -t 编号

GDB教程提炼与问题定位方法

在完成以上步骤后,就进入了真正的debug环节。接下来的首要任务是找到程序出问题的第一句话,即找出来究竟是在哪句话上程序第一次报告了问题。只需要记住几条命令:

  1. n,next,执行完这句话,不会进入这句话内部的执行过程
  2. s,step,执行这句话,如果有内部细节那么进入内部执行过程
  3. c,continue,执行到下一个断点
  4. b,breakpoint,添加断点。有两种用法,b 行号和b 函数名
  5. f,finish,执行完当前所在的函数体,返回到上一层。
  6. p,print,用法为p 表达式,输出执行到当前所在的这句话时,表达式的值。
  7. q,quit,退出当前的调试进程。

更具体的,下图是IDEA的调试工具里的几个常用选项,它们分别对应q,c,n,s,f,现在是不是感觉更亲切了?

image1.png

具体该怎么找到程序第一次出问题的地方呢?

  1. (现在是在mips_init中)n,n还是璃月雅言的n
  2. 如果出了问题,记下来是在哪句话出的问题,重复一遍进入到mips_init的流程
  3. n,n,还是璃月雅言的n。不过这次要在出问题的那句话之前及时住手
  4. s,这样就进入了更进一步的层次
  5. 重复以上步骤,直到第四步出问题的那句话是一个简单的操作,不会进入下一层函数。

解决问题方法

使用上述流程,我们很容易确定程序是在哪一句话头一次出了问题。那么,我们该如何解决问题呢?或者说,怎么根据问题位置推测出问题所在呢?

  1. 问题出现的位置有可能就是问题存在的位置。可以使用瞪眼法的升级版——神威·奥义·瞪眼法。观察出问题的地方的上下文逻辑,确认这个函数是否能实现我们的想法
  2. 但是,还有一种可能,即问题出在之前的某一处地方,只是在另一个地方爆发了出来。这个时候需要p大法,输出问题爆发处一切能输出的东西,观察有哪些的值是明显异常,不符合我们想法的。这些异常变量能帮助我们确定程序的哪些部分没能实现预想的功能。然后,是的,还是瞪眼法。

可以发现,在真正解决问题时,GDB并没有太大的用处。接下来我想分析一下瞪眼法这个解决问题的最后一块拼图。(为什么不总结printk输出大法呢?因为没必要,它能干的我GDB都能干)

瞪眼法

或许有个抽象的名字,但是瞪眼法绝对不是一个抽象的方法。以下有两种运用场景:

  1. 课下,每个测试点都是公开的,且是循序渐进的。完全可以通过测试点的对错情况确认bug的大致位置,然后逐行阅读代码,找到漏洞所在。
  2. 课上,每个任务的代码修改量相比整个系统而言都是微不足道的。可能出问题的代码只会局限在很小的一个范围内,因此完全可以逐行阅读刚加上的代码,思考这些代码是否能实现构想的功能——感谢勤劳的OS助教,OS的课下测试强度比CO强了不知多少,这让我们在课上时可以大胆假定课下代码不会出错。赞美OS助教!

当然,在OS这个具体的场景下,瞪眼法也是有专门的技巧的。

  1. 区分练习代码和提供的代码。完全可以认为提供的代码是不会出错的,因此哪怕是在课下,可能出错的地方也就几处练习,这样能大大缩小debug需要遍寻的范围
  2. 学长智慧与同学智慧,不做过多解释

回复主题帖

王哲(助教)
👍︎ 0 点赞

同学编写的这份 GDB 教程写的很干练!对于想了解更多关于 GDB 用法的说明的同学们,也可以参考假期预习 - GDB:程序的解剖术 中的介绍。

关于 tmux 用法,其实在教程中是有给出的,可以查看一下 0.6.1 节。

image.png


纪郅炀 回复 王哲(助教)
👍︎ 4 点赞

是的,是我的问题,我是根据假期预习的部分编写的tmux部分内容

顺带想提一个小建议,希望能进一步合并假期预习、p0ppt、p0指导书的内容,很多知识点只在这三个东西的某一个上有。这三个参考资料涉及的知识点集合能够画成一张标准的韦恩图,容易给查阅资料或自行学习带来一些困扰

0%