「palab03」基础设施-简易调试器

本文最后更新于:2023年4月15日 晚上

基础设施

大概是半个月前实现了表达式求值, 之后就一直搁置了PA实验, 现在慢慢捡起来

之前实现表达式求值的时候发现无法调试, 然后才发现自己直接跳过了「基础设施」这一步直接开始写表达式求值了。。。

现在把之前的基础设施部分补一下

大概就是要在monitor/debug下实现一些简单的命令解析功能

照着文档来写就ok了

命令 格式 使用举例 说明
帮助(1) help help 打印命令的帮助信息
继续运行(1) c c 继续运行被暂停的程序
退出(1) q q 退出NEMU
单步执行 si [N] si 10 让程序单步执行N条指令后暂停执行, 当N没有给出时, 缺省为1
打印程序状态 info SUBCMD info r info w 打印寄存器状态 打印监视点信息
扫描内存(2) x N EXPR x 10 $esp 求出表达式EXPR的值, 将结果作为起始内存 地址, 以十六进制形式输出连续的N个4字节
表达式求值 p EXPR p $eax + 1 求出表达式EXPR的值, EXPR支持的 运算请见调试中的表达式求值小节
设置监视点 w EXPR w *0x2000 当表达式EXPR的值发生变化时, 暂停程序执行
删除监视点 d N d 2 删除序号为N的监视点

解析命令可能用到的api:

strtok

readline

sscanf

单步执行

单步执行的功能十分简单, 而且框架代码中已经给出了模拟CPU执行方式的函数, 你只要使用相应的参数去调用它就可以了. 如果你仍然不知道要怎么做, RTFSC.

「框架代码中已经给出了模拟CPU执行方式的函数」:

nemu/src/isa/$ISA/exec/exec.c中定义的isa_exec_once():

参考给出的cmd_c的实现:

1
2
3
4
static int cmd_c(char *args) {
cpu_exec(-1);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  for (; n > 0; n --) {
vaddr_t this_pc = cpu.pc;

/* Execute one instruction, including instruction fetch,
* instruction decode, and the actual execution. */
__attribute__((unused)) vaddr_t seq_pc = isa_exec_once(); // 让cpu执行当前pc指向的一条指令

difftest_step(this_pc, cpu.pc);

g_nr_guest_instr ++;

#ifdef DEBUG
asm_print(this_pc, seq_pc - this_pc, n < MAX_INSTR_TO_PRINT);

/* TODO: check watchpoints here. */
#endif

#ifdef HAS_IOE
extern void device_update();
device_update();
#endif

if (nemu_state.state != NEMU_RUNNING) break;
}

注意到这里的cpu_exec(-1)之前「palab01」中已经提到过

传入-1相当于无限执行, 那么我们传入N就相当于执行N次了

这样一来, 单步执行便实现了

我这里的思路历程:

框架代码? 找到RTFSC一章->找到isa_exec_once->想到不会要自己调用这个吧->想到给出了继续执行的封装->原来可以直接调用更上层的封装

所以还是要好好理解代码啊

如何将*args转换为数字并保证一定的鲁棒性? 用sscanf

我的实现:

1
2
3
if (sscanf(args, "%d", &n)) {
cpu_exec(n);
}

打印程序状态

打印寄存器就更简单了. 不过既然寄存器的结构是ISA相关的, 我们希望能为简易调试器屏蔽ISA的差异. 框架代码已经为大家准备了如下的API:

1
2
// nemu/src/isa/$ISA/reg.c
void isa_reg_display(void);

执行info r之后, 就调用isa_reg_display(), 在里面直接通过printf()输出所有寄存器的值即可. 如果你从来没有使用过printf(), 请RTFM或者STFW. 如果你不知道要输出什么, 你可以参考GDB中的输出.

首先阅读一下:reg.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <isa.h>
#include "local-include/reg.h"

const char *regs[] = {
"$0", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
"s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
"a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7",
"s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"
};

void isa_reg_display() {
}

word_t isa_reg_str2val(const char *s, bool *success) {
return 0;
}

emm..我确实不知道要输出什么..

在gdb中使用info registers打印出类似如下格式的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(gdb) info registers
rax 0x0 0
rbx 0x555555555390 93824992236432
rcx 0x100 256
rdx 0x7fffffffe338 140737488347960
rsi 0x7fffffffe328 140737488347944
rdi 0x1 1
rbp 0x7fffffffe230 0x7fffffffe230
rsp 0x7fffffffe210 0x7fffffffe210
r8 0x0 0
r9 0x7ffff7faeec0 140737353805504
r10 0x7ffff7e311d5 140737352241621
r11 0x7ffff7f255a0 140737353242016
r12 0x555555555120 93824992235808
r13 0x7fffffffe320 140737488347936
r14 0x0 0
r15 0x0 0
rip 0x555555555240 0x555555555240 <main()+55>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0

「palab03」基础设施-简易调试器
https://blog.roccoshi.top/posts/30418/
作者
RoccoShi
发布于
2021年8月31日
许可协议