Caffeinated 6.828:实验工具指南
创始人
2024-03-02 01:05:02
0

熟悉你的环境对高效率的开发和调试来说是至关重要的。本文将为你简单概述一下 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 的线程相关命令,比如 threadinfo 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-cCtrl-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-noxqemu-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.asmobj/kern/kernel.asmobj/user/hello.asm、等等。

引导加载器、内核、和用户程序的汇编代码列表。

  • obj/kern/kernel.symobj/user/hello.sym、等等。

内核和用户程序的符号表。

  • obj/boot/boot.outobj/kern/kernelobj/user/hello、等等。

内核和用户程序链接的 ELF 镜像。它们包含了 GDB 用到的符号信息。

GDB

完整的 GDB 命令指南请查看 GDB 手册。下面是一些在 6.828 课程中非常有用的命令,它们中的一些在操作系统开发之外的领域几乎用不到。

  • Ctrl-c

在当前指令处停止机器并打断进入到 GDB。如果 QEMU 有多个虚拟的 CPU,所有的 CPU 都会停止。

  • c(或 continue

继续运行,直到下一个断点或 Ctrl-c

  • si(或 stepi

运行一个机器指令。

  • b functionb file:line(或 breakpoint

在给定的函数或行上设置一个断点。

  • b * addr(或 breakpoint

在 EIP 的 addr 处设置一个断点。

  • set print pretty

启用数组和结构的美化输出。

  • info registers

输出通用寄存器 eipeflags、和段选择器。更多更全的机器寄存器状态转储,查看 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)。这个命令将关注的线程切换到 n,n 是从 0 开始编号的。

  • info threads

列出所有的线程(即:CPU),包括它们的状态(活动还是停止)和它们在什么函数中。

QEMU

QEMU 包含一个内置的监视器,它能够有效地检查和修改机器状态。想进入到监视器中,在运行 QEMU 的终端中按入 Ctrl-a c 即可。再次按下 Ctrl-a c 将切换回串行控制台。

监视器命令的完整参考资料,请查看 QEMU 手册。下面是 6.828 课程中用到的一些有用的命令:

  • xp/ N x paddr

显示从物理地址 paddr 处开始的 N 个词的十六进制转储。如果 N 省略,默认为 1。这是 GDB 的 x 命令模拟的物理内存。

  • info registers

显示机器内部寄存器状态的一个完整转储。实践中,对于段选择器,这将包含机器的 隐藏 段状态和局部、全局、和中断描述符表加任务状态寄存器。隐藏状态是在加载段选择器后,虚拟的 CPU 从 GDT/LDT 中读取的信息。下面是实验 1 中 JOS 内核处于运行中时的 CS 信息和每个字段的含义:

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 选题:lujun9972 译者:qhwdw 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

相关内容

【UNIX 环境编程】GC...
💭 写在前面:本文将介绍如何使用 G...
2025-05-31 02:18:08
不要在gdb中倾倒变量
在GDB中不要直接倾倒变量,而是使用GDB提供的命令来查看变量的值...
2025-01-11 02:00:43
编译库以使GDB自动找到源...
要使GDB自动找到源代码,需要编译库时包含调试信息。下面是一个示例...
2024-12-08 09:01:03
编写一个重复的gdb作业
要编写一个重复的gdb作业,你可以使用gdb的命令行参数来自动化执...
2024-12-07 14:01:37
变量的内存地址与gdb的“...
这种情况可能出现在使用了优化编译选项的情况下,例如使用了-O2或更...
2024-12-02 08:31:55
avr-gdbconnec...
可能是由于手动结束 GDB 会话或不当关闭程序,导致 GDB 需要...
2024-11-13 05:30:26

热门资讯

Helix:高级 Linux ... 说到 基于终端的文本编辑器,通常 Vim、Emacs 和 Nano 受到了关注。这并不意味着没有其他...
使用 KRAWL 扫描 Kub... 用 KRAWL 脚本来识别 Kubernetes Pod 和容器中的错误。当你使用 Kubernet...
JStock:Linux 上不... 如果你在股票市场做投资,那么你可能非常清楚投资组合管理计划有多重要。管理投资组合的目标是依据你能承受...
通过 SaltStack 管理... 我在搜索Puppet的替代品时,偶然间碰到了Salt。我喜欢puppet,但是我又爱上Salt了:)...
Epic 游戏商店现在可在 S... 现在可以在 Steam Deck 上运行 Epic 游戏商店了,几乎无懈可击! 但是,它是非官方的。...
《Apex 英雄》正式可在 S... 《Apex 英雄》现已通过 Steam Deck 验证,这使其成为支持 Linux 的顶级多人游戏之...
如何在 Github 上创建一... 学习如何复刻一个仓库,进行更改,并要求维护人员审查并合并它。你知道如何使用 git 了,你有一个 G...
2024 开年,LLUG 和你... Hi,Linuxer,2024 新年伊始,不知道你是否已经准备好迎接新的一年~ 2024 年,Lin...
什么是 KDE Connect... 什么是 KDE Connect?它的主要特性是什么?它应该如何安装?本文提供了基本的使用指南。科技日...
Opera 浏览器内置的 VP... 昨天我们报道过 Opera 浏览器内置了 VPN 服务,用户打开它可以防止他们的在线活动被窥视。不过...