调试StarrOS

February 1, 2024 · View on GitHub

此部分主要介绍调试kernel的一些方法与技巧。

产生调试目标ELF文件

调试kernel需要产生对应的可执行文件,目前LoongArch支持两类调试。

  1. 带有调试信息的kernel ELF文件
make MODE=debug ARCH=loongarch64 TARGET=loongarch64-qemu-virt A=apps/oscomp
# 此时产生的可执行文件带有调试信息
file apps/oscomp/oscomp_loongarch64-qemu-virt.elf

## 显示如下:
# apps/oscomp/oscomp_loongarch64-qemu-virt.elf: ELF 64-bit LSB executable, LoongArch, version 1 (SYSV), statically linked, with debug_info, not stripped

# 带有符合信息 with debug_info

此种生成的ELF文件,可以对照这rust源码,单步调试,也可以设置断点,查看执行状态

注意: 此步骤对于前期刚开始接触Starry内核时,比较友好。但是缺点也比较明显,就是执行 比较缓慢。

  1. 优化后的不带调试信息的kernel ELF文件
make ARCH=loongarch64 TARGET=loongarch64-qemu-virt A=apps/oscomp
# 此时产生的可执行文件不带调试信息
file apps/oscomp/oscomp_loongarch64-qemu-virt.elf

## 显示如下:
# apps/oscomp/oscomp_loongarch64-qemu-virt.elf: ELF 64-bit LSB executable, LoongArch, version 1 (SYSV), statically linked, not stripped

# 没有符合信息,但是可以调试。

此方式下kernel执行效率较高,推荐此方式。

QEMU模拟器

Qemu是一个开源的托管虚拟机,通过纯软件来实现虚拟化模拟器,几乎可以模拟任何硬件设备。

QEMU有两种主要运作模式:

  • User mode模拟模式,亦即是用户模式。QEMU能启动那些为不同中央处理器编译的Linux程序。
  • System mode模拟模式,亦即是系统模式。QEMU能模拟整个电脑系统,包括中央处理器及其他周边设备。它使得为跨平台编写的程序进行测试及除错工作变得容易。其亦能用来在一部主机上虚拟数部不同虚拟电脑。

我们使用QEMU的System mode模拟模式。

下面介绍一些QEMU的常用参数:

1.设备类型(-machine/-M)

在qemu中,不同的指令集的模拟器会编译成不同的可执行文件,诸如:qemu-system-x86_64/qemu-system-aarch64/qemu-system-loongarch64/qemu-system-riscv64等。使用参数-machine help运行模拟器可以查看当前模拟器支持的设备信息,例如:

$ qemu-system-loongarch64 -M ?

Supported machines are:
none                 empty machine
virt                 Loongson-3A5000 LS7A1000 machine (default)

2.内存大小(-m)

参数-m 1G就是指定虚拟机内部的内存大小为1GB,帮助说明如下:

$ qemu-system-loongarch64 -m ?

qemu-system-loongarch64: -m ?: Parameter 'size' expects a non-negative number below $2^{64}$
Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta-
and exabytes, respectively.

3.核心数(-smp)

现代cpu往往是对称多核心的,因此通过指定-smp 8可以指定虚拟机核心数。

$ qemu-system-loongarch64 -smp ?

smp-opts options:
  books=<num>
  clusters=<num>
  cores=<num>
  cpus=<num>
  dies=<num>
  drawers=<num>
  maxcpus=<num>
  sockets=<num>
  threads=<num>

4.-nographic

disable graphical output and redirect serial I/Os to console.

5.调试参数(-s -S)

此参数选项用于启动gdb服务,启动后qemu不立即运行guest,而是等待主机gdb发起连接。

6.指定kernel镜像

此参数指定传入qemu的内核的镜像文件,一般是ELF文化,也可以是uImage等。

其他的可参考张老师的仓库qemu-loongarch-runenv

使用QEMU调试

按照上面步骤,产生相应的kernel ELF文件。

make debug ARCH=loongarch64 TARGET=loongarch64-qemu-virt A=apps/oscomp

此方式会调用qemu,相当于如下:

qemu-system-loongarch64 -m 16G -smp 1 -machine virt -kernel apps/oscomp/ \
oscomp_loongarch64-qemu-virt.elf -nographic -vga none -s -S

新打开一个终端,输入:

loongarch64-linux-gnu-gdb apps/oscomp/oscomp_loongarch64-qemu-virt.elf \
  -ex 'target remote localhost:1234' \
  -ex 'b rust_entry' \
  -ex 'continue' \
  -ex 'disp /16i $pc'

其中target remote localhost:1234通过端口1234链接到qemu,b rust_entry是在rust_entry处设置断点。 disp /16i $pc为显示当前pc位置的16条汇编指令。

如果配置正常的话,会显示如下:

Breakpoint 1, axhal::platform::loongarch64_qemu_virt::rust_entry (cpu_id=0, _dtb=0) at modules/axhal/src/platform/loongarch64_qemu_virt/mod.rs:25
25	    crate::mem::clear_bss();
1: x/16i $pc
=> 0x90000000921c6cf4 <axhal::platform::loongarch64_qemu_virt::rust_entry+20>:	
    bl          	3852(0xf0c)	# 0x90000000921c7c00 <_ZN5axhal3mem9clear_bss17h90acbf5a70f3d7b1E>
   0x90000000921c6cf8 <axhal::platform::loongarch64_qemu_virt::rust_entry+24>:	ld.d        	$a0, $sp, 0
   0x90000000921c6cfc <axhal::platform::loongarch64_qemu_virt::rust_entry+28>:	
--Type <RET> for more, q to quit, c to continue without paging--
    bl          	-12156(0xfffd084)	# 0x90000000921c3d80 <_ZN5axhal3cpu12init_primary17hed83e7a768a9c739E>
   0x90000000921c6d00 <axhal::platform::loongarch64_qemu_virt::rust_entry+32>:	pcalau12i   	$a0, -7(0xffff9)
   0x90000000921c6d04 <axhal::platform::loongarch64_qemu_virt::rust_entry+36>:	addi.d      	$a0, $a0, 0
   0x90000000921c6d08 <axhal::platform::loongarch64_qemu_virt::rust_entry+40>:	
    bl          	-584(0xffffdb8)	# 0x90000000921c6ac0 <_ZN5axhal4arch11loongarch6420set_trap_vector_base17hdb527d2259c8c6cdE>
   0x90000000921c6d0c <axhal::platform::loongarch64_qemu_virt::rust_entry+44>:	ori         	$a0, $zero, 0x200
   0x90000000921c6d10 <axhal::platform::loongarch64_qemu_virt::rust_entry+48>:	st.d        	$a0, $sp, 32(0x20)
   0x90000000921c6d14 <axhal::platform::loongarch64_qemu_virt::rust_entry+52>:	pcalau12i   	$a0, 154(0x9a)
   0x90000000921c6d18 <axhal::platform::loongarch64_qemu_virt::rust_entry+56>:	addi.d      	$a0, $a0, 0
   0x90000000921c6d1c <axhal::platform::loongarch64_qemu_virt::rust_entry+60>:	st.d        	$a0, $sp, 24(0x18)
   0x90000000921c6d20 <axhal::platform::loongarch64_qemu_virt::rust_entry+64>:	pcalau12i   	$a1, -6(0xffffa)
   0x90000000921c6d24 <axhal::platform::loongarch64_qemu_virt::rust_entry+68>:	addi.d      	$a1, $a1, 0
   0x90000000921c6d28 <axhal::platform::loongarch64_qemu_virt::rust_entry+72>:	
    bl          	-520(0xffffdf8)	# 0x90000000921c6b20 <_ZN5axhal4arch11loongarch648tlb_init17h97116086c310791dE>
   0x90000000921c6d2c <axhal::platform::loongarch64_qemu_virt::rust_entry+76>:	ld.d        	$a0, $sp, 0
   0x90000000921c6d30 <axhal::platform::loongarch64_qemu_virt::rust_entry+80>:	move        	$a1, $zero
(gdb) 

1.设置断点

设置断点的命令为b(break),分带有符合调试信息与不带符合调试信息。如下:

带符合调试信息的ELF文件

b platform/loongarch64_qemu_virt/boot.rs:30 # 在文件platform/loongarch64_qemu_virt/boot.rs的30行,设置断点。

# 上面等同于:
break platform/loongarch64_qemu_virt/boot.rs:30

对于没有调试信息的ELF文件,上面方式无法打断点。我们使用另外的方式打断点。

# 1. 获得需要设置断点的位置
loongarch64-linux-gnu-objdump -ald apps/oscomp/oscomp_loongarch64-qemu-virt.elf > elf.S

# 2. 比如想查看一下 trap_vector_base

loongarch64-linux-gnu-nm oscomp_loongarch64-qemu-virt.elf | grep trap_vector_base
# 显示 90000000921bf000 T trap_vector_base

# 3. 设置断点
b *0x90000000921bf000

# 4. 继续执行
c # 或者 continue

2.单步调试

si  # 或者 step into

3.查看寄存器的值

i r #或者 info register

r0             0x0                 0
r1             0x0                 0x0 <axruntime::init_interrupt::__PERCPU_NEXT_DEADLINE>
r2             0x0                 0x0 <axruntime::init_interrupt::__PERCPU_NEXT_DEADLINE>
r3             0x90000000949c1fd0  0x90000000949c1fd0 <axhal::platform::loongarch64_qemu_virt::boot::BOOT_STACK+262096>
r4             0x0                 0
r5             0x0                 0
r6             0x0                 0
r7             0x0                 0
r8             0x0                 0
r9             0x0                 0
r10            0x0                 0
r11            0x0                 0
r12            0x90000000921c6ce0  10376293543912959200

查看某一个寄存器的值:

i r r1    #查看ra的值
i r r2    #查看tp的值
i r r3    #查看sp的值
i r $pc   #查看当前pc的值

4.查看内存的值

查看内存0x120000000的值,以16进制显示。

p /x *0x120000000

5.查看堆栈信息

  • backtrace,也可简写为 bt,用于查看当前调用堆栈。
  • frame 堆栈编号,也可简写为 f 堆栈编号,用于切换到其他堆栈处。
bt :
#0  axhal::platform::loongarch64_qemu_virt::rust_entry (cpu_id=0, _dtb=0)
    at modules/axhal/src/platform/loongarch64_qemu_virt/mod.rs:25
#1  0x0000000000000000 in ?? ()
Backtrace stopped: frame did not save the PC

f 0 :
#0  axhal::platform::loongarch64_qemu_virt::rust_entry (cpu_id=0, _dtb=0)
    at modules/axhal/src/platform/loongarch64_qemu_virt/mod.rs:25
25	    crate::mem::clear_bss();

其他的调试参数与gdb查看相关的文档。

Qemu 官方文档 GDB 官方文档