难度:easy

In many cases, print statements will be sufficient to debug your kernel, but sometimes being able to single step through some assembly code or inspecting the variables on the stack is helpful.
To learn more about how to run GDB and the common issues that can arise when using GDB, check out this page.
To help you become familiar with gdb, run make qemu-gdb and then fire up gdb in another window (see the gdb bullet on the guidance page). Once you have two windows open, type in the gdb window.

在许多情况下,使用打印语句可能足以调试你的内核,但有时能够逐步执行一些汇编代码或检查堆栈上的变量会很有帮助。

通过实验了解有关如何运行 GDB 以及在使用 GDB 时可能出现的常见问题

 qemu虚拟机启动gdb调试模式

make qemu-gdb

该命令用于启动QEMU虚拟机并与GDB连接以进行调试的命令:

MIB 6.S081 System calls(1)using gdb_体系结构

启动QEMU虚拟机:make qemu-gdb 命令启动了一个QEMU虚拟机实例。

(QEMU是一个开源的虚拟化工具,它可以模拟多种处理器体系结构,并在模拟环境中运行操作系统和应用程序。)
同时启用GDB调试模式,使得QEMU虚拟机处于可调试状态。
QEMU虚拟机启动,将等待GDB连接,以便执行调试操作。

在另一个相同目录下的终端输入:

$ gdb-multiarch -x .gdbinit
  • 1.

gdb-multiarch -x .gdbinit 这个命令的作用是使用多体系结构(multi-architecture)版本的GDB,同时执行一个包含GDB命令的脚本文件 .gdbinit。各个部分的含义如下:

gdb-multiarch:这是GDB的一个命令,表示启动多体系结构版本的GDB。多体系结构版本的GDB支持在不同的CPU架构上进行调试。

-x .gdbinit:这是一个选项,指示GDB在启动时执行指定的脚本文件 .gdbinit。

MIB 6.S081 System calls(1)using gdb_运维_02

实验要求使用gdb回答几个问题

1. Looking at the backtrace output, which function called syscall?(哪一个函数是syscall)

为了找到问题答案,进入gdb后,在syscall处打一个断点

(gdb)  b syscall

MIB 6.S081 System calls(1)using gdb_运维_03

一直运行到断点处。 

(gdb)c

MIB 6.S081 System calls(1)using gdb_服务器_04

使用layout src命令用于切换源代码窗格的布局。将GDB调试会话中的当前布局更改为源代码布局。在终端中显示源代码,并显示与调试相关的其他信息,如程序计数器(PC)位置、变量、断点等。

(gdb) layout src

MIB 6.S081 System calls(1)using gdb_运维_05

使用backtrace追踪syscall调用过程。

(gdb)backtrace

在GDB中,backtrace命令用于打印当前调用堆栈的内容。它显示了当前被调用的函数以及调用链的信息,即从程序执行的起点到当前代码所处位置的函数调用路径。

了解程序的执行流程,显示了函数调用的顺序和每个函数被调用时的位置。这对于分析代码中的错误、找出程序崩溃的原因或者了解程序的执行路径非常有用。

MIB 6.S081 System calls(1)using gdb_服务器_06

可以看到usertrap()调用了syscall函数(问题1答案)

MIB 6.S081 System calls(1)using gdb_linux_07

查看kernel/trap.c源代码可以得知usertrap()确实调用了syscall()

2. What is the value of p->trapframe->a7 and what does that value represent? (Hint: look user/initcode.S, the first user program xv6 starts.)

要想知道p->trapframe->a7的值,使用next到代码的行。可以看到p->trapframe->a7的值为7(问题2第一问答案)

(gdb)n

MIB 6.S081 System calls(1)using gdb_体系结构_08

根据提示,去user/initcode.S查看7代表什么意思

MIB 6.S081 System calls(1)using gdb_GDB_09

没有什么有用的信息,去syscall.h继续查看

MIB 6.S081 System calls(1)using gdb_服务器_10

可以看到7是SYS_exec的意思。

所以num=7,表示接下来要运行系统调用。

What was the previous mode that the CPU was in?(CPU运行在什么模式?)

MIB 6.S081 System calls(1)using gdb_运维_11

$sstatus 是一个寄存器名,它通常用于 RISC-V 架构的处理器,用于存储处理器状态信息。在这个例子中,p $sstatus 命令会打印 $sstatus 寄存器的当前值。

所以下面需要找sstatus标志位的含义。

查阅实验提供的riscv手册,可以看到sstatus的说明文档

 https://github.com/riscv/riscv-isa-manual/releases/download/Priv-v1.12/riscv-privileged-20211203.pdf

MIB 6.S081 System calls(1)using gdb_GDB_12

SPP=0表示trap是来自用户模式,SPP=1表示trap来自监管模式。

MIB 6.S081 System calls(1)using gdb_服务器_13

由图4.2可以看出SPP在第8位,占1位。

MIB 6.S081 System calls(1)using gdb_运维_11

0X22=0010 0010B,第8位表示0。所以,trap来自用户模式(问题3答案)

4.Write down the assembly instruction the kernel is panicing at. Which register corresponds to the variable num?

In the subsequent part of this lab , it may happen that you make a programming error that causes the xv6 kernel to panic.replace the statement num = p->trapframe->a7; with num = * (int *) 0;

使用 num = * (int *) 0;替换num = p->trapframe->a7;重新编译

make qemu

MIB 6.S081 System calls(1)using gdb_linux_15

出现报错 

MIB 6.S081 System calls(1)using gdb_GDB_16

To track down the source of a kernel page-fault panic, search for the sepc value printed for the panic you just saw in the file kernel/kernel.asm, which contains the assembly for the compiled kernel.

根据提示,去 kernel/kernel.asm查找80002052,可以看到spec对应num=*(int *)0那一行(问题4答案)

MIB 6.S081 System calls(1)using gdb_服务器_17

5.Why does the kernel crash? Hint: look at figure 3-3 in the text; is address 0 mapped in the kernel address space? Is that confirmed by the value in scause above? (See description of scause in  RISC-V privileged instructions)

暂时不会。。。

6.What is the name of the binary that was running when the kernel paniced? What is its process id (pid)?  找出在内核崩溃(kernel panic)时正在运行的二进制文件的名称及其进程ID(pid)

To inspect the state of the processor and the kernel at the faulting instruction, fire up gdb, and set a breakpoint at the faulting epc, like this:

使用gdb在faulting epc打一个断点类似上面的操作qumu启用调试模式

(gdb) b *0x0000000080002052

(gdb) layout asm

(gdb) c

在GDB中,layout asm是一个命令,用于在调试器中显示程序的汇编代码布局。

layout asm命令将GDB的视图切换到汇编代码布局。这个视图显示了程序的汇编代码,以及它们在内存中的位置。你可以使用这个视图来查看程序的指令、寄存器的状态、调用栈等信息。

MIB 6.S081 System calls(1)using gdb_服务器_18

汇编代码看不懂,使用layout asm看源代码

MIB 6.S081 System calls(1)using gdb_体系结构_19

打印出内核 panic时地用户进程名字 

MIB 6.S081 System calls(1)using gdb_linux_20

 

为什么可以打印p->name?

p是proc结构体的一个实例。

通过查看proc.h源码可以看到proc的结构体内容,proc是表示进程状态的结构体。

里面有进程pid,是否处于sleep状态,父进程,栈起始地址,打开的文件,当前目录,进程名字......

MIB 6.S081 System calls(1)using gdb_服务器_21

可以在gdb中查看p结构体中的值:

MIB 6.S081 System calls(1)using gdb_服务器_22

由p->name可以看出trap时的进程为initcode。 这是 xv6 中第一个运行的进程。pid=1(问题6答案)