TranslateProject/published/201811/20180907 6.828 lab tools guide.md
2018-11-30 23:50:53 +08:00

223 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Caffeinated 6.828:实验工具指南
======
熟悉你的环境对高效率的开发和调试来说是至关重要的。本文将为你简单概述一下 JOS 环境和非常有用的 GDB 和 QEMU 命令。话虽如此,但你仍然应该去阅读 GDB 和 QEMU 手册,来理解这些强大的工具如何使用。
### 调试小贴士
#### 内核
GDB 是你的朋友。使用 `qemu-gdb target`(或它的变体 `qemu-gdb-nox`)使 QEMU 等待 GDB 去绑定。下面在调试内核时用到的一些命令,可以去查看 GDB 的资料。
如果你遭遇意外的中断、异常、或三重故障,你可以使用 `-d` 参数要求 QEMU 去产生一个详细的中断日志。
调试虚拟内存问题时,尝试 QEMU 的监视命令 `info mem`(提供内存高级概述)或 `info pg`(提供更多细节内容)。注意,这些命令仅显示**当前**页表。
(在实验 4 以后)去调试多个 CPU 时,使用 GDB 的线程相关命令,比如 `thread``info threads`
#### 用户环境(在实验 3 以后)
GDB 也可以去调试用户环境,但是有些事情需要注意,因为 GDB 无法区分开多个用户环境或区分开用户环境与内核环境。
你可以使用 `make run-name`(或编辑 `kern/init.c` 目录)来指定 JOS 启动的用户环境,为使 QEMU 等待 GDB 去绑定,使用 `run-name-gdb` 的变体。
你可以符号化调试用户代码,就像调试内核代码一样,但是你要告诉 GDB哪个符号表用到符号文件命令上因为它一次仅能够使用一个符号表。提供的 `.gdbinit` 用于加载内核符号表 `obj/kern/kernel`。对于一个用户环境,这个符号表在它的 ELF 二进制文件中,因此你可以使用 `symbol-file obj/user/name` 去加载它。不要从任何 `.o` 文件中加载符号,因为它们不会被链接器迁移进去(库是静态链接进 JOS 用户二进制文件中的,因此这些符号已经包含在每个用户二进制文件中了)。确保你得到了正确的用户二进制文件;在不同的二进制文件中,库函数被链接为不同的 EIP而 GDB 并不知道更多的内容!
(在实验 4 以后)因为 GDB 绑定了整个虚拟机,所以它可以将时钟中断看作为一种控制转移。这使得从底层上不可能实现步进用户代码,因为一个时钟中断无形中保证了片刻之后虚拟机可以再次运行。因此可以使用 `stepi` 命令,因为它阻止了中断,但它仅可以步进一个汇编指令。断点一般来说可以正常工作,但要注意,因为你可能在不同的环境(完全不同的一个二进制文件)上遇到同一个 EIP。
### 参考
#### JOS makefile
JOS 的 GNUmakefile 包含了在各种方式中运行的 JOS 的许多假目标。所有这些目标都配置 QEMU 去监听 GDB 连接(`*-gdb` 目标也等待这个连接)。要在运行中的 QEMU 上启动它,只需要在你的实验目录中简单地运行 `gdb ` 即可。我们提供了一个 `.gdbinit` 文件,它可以在 QEMU 中自动指向到 GDB、加载内核符号文件、以及在 16 位和 32 位模式之间切换。退出 GDB 将关闭 QEMU。
* `make qemu`
在一个新窗口中构建所有的东西并使用 VGA 控制台和你的终端中的串行控制台启动 QEMU。想退出时既可以关闭 VGA 窗口,也可以在你的终端中按 `Ctrl-c``Ctrl-a x`
* `make qemu-nox`
`make qemu` 一样,但仅使用串行控制台来运行。想退出时,按下 `Ctrl-a x`。这种方式在通过 SSH 拨号连接到 Athena 上时非常有用,因为 VGA 窗口会占用许多带宽。
* `make qemu-gdb`
`make qemu` 一样,但它与任意时间被动接受 GDB 不同,而是暂停第一个机器指令并等待一个 GDB 连接。
* `make qemu-nox-gdb`
它是 `qemu-nox``qemu-gdb` 目标的组合。
* `make run-nam`
(在实验 3 以后)运行用户程序 _name_。例如,`make run-hello` 运行 `user/hello.c`
* `make run-name-nox`,`run-name-gdb`, `run-name-gdb-nox`
(在实验 3 以后)与 `qemu` 目标变量对应的 `run-name` 的变体。
makefile 也接受几个非常有用的变量:
* `make V=1 …`
详细模式。输出正在运行的每个命令,包括参数。
* `make V=1 grade`
在评级测试失败后停止,并将 QEMU 的输出放入 `jos.out` 文件中以备检查。
* `make QEMUEXTRA=' _args_ ' …`
指定传递给 QEMU 的额外参数。
#### JOS obj/
在构建 JOS 时makefile 也产生一些额外的输出文件,这些文件在调试时非常有用:
* `obj/boot/boot.asm`、`obj/kern/kernel.asm`、`obj/user/hello.asm`、等等。
引导加载器、内核、和用户程序的汇编代码列表。
* `obj/kern/kernel.sym`、`obj/user/hello.sym`、等等。
内核和用户程序的符号表。
* `obj/boot/boot.out`、`obj/kern/kernel`、`obj/user/hello`、等等。
内核和用户程序链接的 ELF 镜像。它们包含了 GDB 用到的符号信息。
#### GDB
完整的 GDB 命令指南请查看 [GDB 手册][1]。下面是一些在 6.828 课程中非常有用的命令,它们中的一些在操作系统开发之外的领域几乎用不到。
* `Ctrl-c`
在当前指令处停止机器并打断进入到 GDB。如果 QEMU 有多个虚拟的 CPU所有的 CPU 都会停止。
* `c`(或 `continue`
继续运行,直到下一个断点或 `Ctrl-c`
* `si`(或 `stepi`
运行一个机器指令。
* `b function``b file:line`(或 `breakpoint`
在给定的函数或行上设置一个断点。
* `b * addr`(或 `breakpoint`
在 EIP 的 addr 处设置一个断点。
* `set print pretty`
启用数组和结构的美化输出。
* `info registers`
输出通用寄存器 `eip`、`eflags`、和段选择器。更多更全的机器寄存器状态转储,查看 QEMU 自己的 `info registers` 命令。
* `x/ N x addr`
以十六进制显示虚拟地址 addr 处开始的 N 个词的转储。如果 N 省略,默认为 1。addr 可以是任何表达式。
* `x/ N i addr`
显示从 addr 处开始的 N 个汇编指令。使用 `$eip` 作为 addr 将显示当前指令指针寄存器中的指令。
* `symbol-file file`
(在实验 3 以后)切换到符号文件 file 上。当 GDB 绑定到 QEMU 后,它并不是虚拟机中进程边界内的一部分,因此我们要去告诉它去使用哪个符号。默认情况下,我们配置 GDB 去使用内核符号文件 `obj/kern/kernel`。如果机器正在运行用户代码,比如是 `hello.c`,你就需要使用 `symbol-file obj/user/hello` 去切换到 hello 的符号文件。
QEMU 将每个虚拟 CPU 表示为 GDB 中的一个线程,因此你可以使用 GDB 中所有的线程相关的命令去查看或维护 QEMU 的虚拟 CPU。
* `thread n`
GDB 在一个时刻只关注于一个线程CPU。这个命令将关注的线程切换到 nn 是从 0 开始编号的。
* `info threads`
列出所有的线程CPU包括它们的状态活动还是停止和它们在什么函数中。
#### QEMU
QEMU 包含一个内置的监视器,它能够有效地检查和修改机器状态。想进入到监视器中,在运行 QEMU 的终端中按入 `Ctrl-a c` 即可。再次按下 `Ctrl-a c` 将切换回串行控制台。
监视器命令的完整参考资料,请查看 [QEMU 手册][2]。下面是 6.828 课程中用到的一些有用的命令:
* `xp/ N x paddr`
显示从物理地址 paddr 处开始的 N 个词的十六进制转储。如果 N 省略,默认为 1。这是 GDB 的 `x` 命令模拟的物理内存。
* `info registers`
显示机器内部寄存器状态的一个完整转储。实践中,对于段选择器,这将包含机器的 _隐藏_ 段状态和局部、全局、和中断描述符表加任务状态寄存器。隐藏状态是在加载段选择器后,虚拟的 CPU 从 GDT/LDT 中读取的信息。下面是实验 1 中 JOS 内核处于运行中时的 CS 信息和每个字段的含义:
```c
CS =0008 10000000 ffffffff 10cf9a00 DPL=0 CS32 [-R-]
```
* `CS =0008`
代码选择器可见部分。我们使用段 0x8。这也告诉我们参考全局描述符表0x8&4=0并且我们的 CPL当前权限级别是 0x8&3=0。
* `10000000`
这是段基址。线性地址 = 逻辑地址 + 0x10000000。
* `ffffffff`
这是段限制。访问线性地址 0xffffffff 以上将返回段违规异常。
* `10cf9a00`
段的原始标志QEMU 将在接下来的几个字段中解码这些对我们有用的标志。
* `DPL=0`
段的权限级别。一旦代码以权限 0 运行,它将就能够加载这个段。
* `CS32`
这是一个 32 位代码段。对于数据段(不要与 DS 寄存器混淆了),另外的值还包括 `DS`,而对于本地描述符表是 `LDT`
* `[-R-]`
这个段是只读的。
* `info mem`
(在实验 2 以后)显示映射的虚拟内存和权限。比如:
```
ef7c0000-ef800000 00040000 urw
efbf8000-efc00000 00008000 -rw
```
这告诉我们从 0xef7c0000 到 0xef800000 的 0x00040000 字节的内存被映射为读取/写入/用户可访问,而映射在 0xefbf8000 到 0xefc00000 之间的内存权限是读取/写入,但是仅限于内核可访问。
* `info pg`
(在实验 2 以后)显示当前页表结构。它的输出类似于 `info mem`,但与页目录条目和页表条目是有区别的,并且为每个条目给了单独的权限。重复的 PTE 和整个页表被折叠为一个单行。例如:
```
VPN range Entry Flags Physical page
[00000-003ff] PDE[000] -------UWP
[00200-00233] PTE[200-233] -------U-P 00380 0037e 0037d 0037c 0037b 0037a ..
[00800-00bff] PDE[002] ----A--UWP
[00800-00801] PTE[000-001] ----A--U-P 0034b 00349
[00802-00802] PTE[002] -------U-P 00348
```
这里各自显示了两个页目录条目、虚拟地址范围 0x00000000 到 0x003fffff 以及 0x00800000 到 0x00bfffff。 所有的 PDE 都存在于内存中、可写入、并且用户可访问,而第二个 PDE 也是可访问的。这些页表中的第二个映射了三个页、虚拟地址范围 0x00800000 到 0x00802fff其中前两个页是存在于内存中的、可写入、并且用户可访问的而第三个仅存在于内存中并且用户可访问。这些 PTE 的第一个条目映射在物理页 0x34b 处。
QEMU 也有一些非常有用的命令行参数,使用 `QEMUEXTRA` 变量可以将参数传递给 JOS 的 makefile。
* `make QEMUEXTRA='-d int' ...`
记录所有的中断和一个完整的寄存器转储到 `qemu.log` 文件中。你可以忽略前两个日志条目、“SMM: enter” 和 “SMM: after RMS”因为这些是在进入引导加载器之前生成的。在这之后的日志条目看起来像下面这样
```
4: v=30 e=0000 i=1 cpl=3 IP=001b:00800e2e pc=00800e2e SP=0023:eebfdf28 EAX=00000005
EAX=00000005 EBX=00001002 ECX=00200000 EDX=00000000
ESI=00000805 EDI=00200000 EBP=eebfdf60 ESP=eebfdf28
...
```
第一行描述了中断。`4:` 只是一个日志记录计数器。`v` 提供了十六进程的向量号。`e` 提供了错误代码。`i=1` 表示它是由一个 `int` 指令(相对一个硬件产生的中断而言)产生的。剩下的行的意思很明显。对于一个寄存器转储而言,接下来看到的就是寄存器信息。
注意:如果你运行的是一个 0.15 版本之前的 QEMU日志将写入到 `/tmp` 目录,而不是当前目录下。
--------------------------------------------------------------------------------
via: https://pdos.csail.mit.edu/6.828/2018/labguide.html
作者:[csail.mit][a]
选题:[lujun9972][b]
译者:[qhwdw](https://github.com/qhwdw)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://pdos.csail.mit.edu
[b]: https://github.com/lujun9972
[1]: http://sourceware.org/gdb/current/onlinedocs/gdb/
[2]: http://wiki.qemu.org/download/qemu-doc.html#pcsys_005fmonitor