附加作业-命令行调试器的使用

平时我们各种图形界面的调试器用多了,这次我来演示一下命令行调试器。 

为什么使用命令行调试器?

    平时的程序都能有图形界面的调试器,可是我们可以想像,第一个图形界面的调试器也需要调试,所以命令行的调试器还是很有存在的意义的。并且对于一些非常底层的东西,比如操作系统内核,一般都只提供基本的调试工具,那就是命令行。

    为了体现出区别,我直接就演示一个绝对高端洋气上档次的。具有以下特性:

        1.被调试的程序是操作系统内核,就是我们上学期操作系统的课程设计。

        2.被调试的程序是mips指令集的,所以使用的工具链都是针对mips的,都是我自己下载了源代码后配置并编译的,平台为OS X 10.9

        3.被调试的程序在虚拟机中运行,所以我们需要使用gdb进行远程调试 

    以上三点中每一点都和平时的调试工作相去甚远,由此我们也能充分体会到gdb的强大。

 

使用的工具链:

编译器为mips-sde-elf-gcc,产生方法为手工编译gcc源代码,配置target参数为mips-sde-elf

 

为了调试内核,就需要给内核一个运行的环境,在此,我们使用了qemu的mips模拟器qemu-system-mips

 

首先,我们需要在编译的时候加上生成调试信息的参数-g,这点在Makefile脚本里面就可以进行设置。

 

编译完成后,就要进行载入内核操作。为此,我写了两个脚本,一个用来实际运行,一个用来进行调试。

 

我们这里进入调试状态,先运行脚本debug.sh

 

然后再开一个终端,打开gdb,载入被调内核:

 

连接上qemu模拟器:

(gdb) target remote:1234

由链接脚本可以指定,程序的入口,这个内核的入口是”_start”,代码如下所示:

我们要观察这个函数运行的情况,就需要设置一个断点:

添加断点直接输入b _start即可完成设置断点的操作: 

然后按c就可开始执行:

很快就运行到了入口处,这时候我们输入l就可查看周围代码

 

要查看当前寄存器的值,输入info registers即可:

 

 

定位错误

假设我运行到了一个地方,出错了,如下图:

 

如果这句话出错了,和我们预期的结果不对。首先我们就需要知道targetPde指的是哪儿,这时候我们可以打印变量,p targetPde就行

 

下面就要查看运行的上下文,看究竟是哪一步产生了预料之外的数据,这时候我们可以使用bt指令,这个指令功能确实变态:

 

从中我们就可以看出,每个函数调用的位置,以及调用的函数,以及调用时的参数。有了这个以后,查错就变得十分方便。

出了能断在某个函数的入口,还能断在任意一行,或者某条指令。还有功能更强大的,复杂的条件断点。这些都能在说明文档中找到。下图为查看当前断点:

 

在程序执行时,使用n能够单步执行但不更进一步进入函数(step over),使用s就是进入函数(step into)。ni就是不进入函数的单步汇编,si就是进入函数的单步汇编(不输入指令直接按回车就是重复上一次操作):

 

最后,所有可见的错误都调通了,我们就顺利的在qemu中看到执行的结果了:

 

    这就基本上是我使用gdb的方法了。从我个人认为,调试器的工作就是帮我定位错误,重现错误,更重要的是怎么解决错误以及怎么从头开始就避免错误。特别对于底层程序,有时候没有那么顺手的工具可以使用,这个时候就是对于经验和智慧的考验。

    我喜欢这种挑战。

posted on 2013-12-09 15:05  不想嚣张  阅读(523)  评论(0编辑  收藏  举报

导航