GDB是一个强大的代码调试工具,很多时候printf能帮助我们打印错误,但是如果栈调用复杂,或者遇到奇奇怪怪的其他错误,就没有办法了。对于xv6的gdb调试,资料可谓是参差不齐,而且很多地方只描述了一半的方法,也让我花了一晚上的时间去找答案,调试是一个积累的过程,该文章会持续更新。

运行

终端运行:

1
make qemu-gdb

新建终端运行:

1
riscv64-unknown-elf-gdb kernel/kernel
此时会出现一个警告:
1
2
warning: File "/home/linux/Desktop/MIT_xv6/xv6-labs-2020/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
大概意思是你的gdb初始化被某个路径阻断了,因此需要在家目录新建一个.gitinit:
1
vim ~/.gitinit

输入:

1
add-auto-load-safe-path /home/linux/Desktop/MIT_xv6/xv6-labs-2020/.gdbinit
即把xv6的初始化文件定向到家目录,前提是不会影响其他使用gdb调试的程序。

基本使用:调试user文件

第一次使用gdb是在用户文件的find,为了找到这个c文件的错误:首先gdb默认是调式内核kernel里的文件,通过file定向到user文件,在gdb窗口执行:

1
file user/_find

打断点,这里在main的入口打:

1
2
3
b main
#如果需要在某行(如第八)打,就是
b user/find.c:8
运行至断点:
1
c
该窗口会阻塞在Continuing,在qemu输入命令:
1
find . ls
然后gdb端会更新argc,确认argc参数和你输入的参数是否一致(第一次好像有内存污染

后面就是n(next)和s(step)的问题了,n和s都代表单步操作,但是n遇到函数会把函数当成整体不会进入,而s会进入函数内部查看调用细节。这部分试几次就会了,例如: 输入n,程序会在main函数之间执行,然后如果遇到你感兴趣的函数,点击s,就去到函数内部了,这时候继续点n就能看到该函数的调用细节了, gdb支持回车重复上次的指令,注意不要一直ssss,这样一不小心就去到头文件的底层了(例如无路可走:Cannot find bounds of current function)。

可视化显示

如果你的layout是正常的(不正常查阅环境配置Q3),可以在gdb中使用layout可视化正在调试的代码:

1
2
3
4
layout asm  #汇编
layout src #源码
layout split #汇编+源码
layout reg #寄存器