回溯(中等)
添加函数定义
将backtrace()的原型添加到kernel/defs.h中,以便您可以在sys_sleep中调用backtrace。
// in defs.h
// printf.c
void backtrace(void);// in sys_proc.c
uint64
sys_sleep(void)
{
backtrace();//
}实现回溯方法
前情提要, 在 RISC-V 架构中,函数调用时的栈帧布局如下:
- 栈帧指针 (fp) :指向当前栈帧的底部(高地址)。
- 返回地址 (ra) :存储在栈帧中,表示函数返回时的地址。
- 调用者的栈帧指针 :存储在栈帧中,用于链接到上一个栈帧。
+-------------------+
| 返回地址 (ra) | <- fp - 8
+-------------------+
| 上一个 fp | <- fp - 16
+-------------------+
| 局部变量 |
+-------------------+
| ... |
调用时汇编大概是这样:
addi sp, sp, -16
sd ra, 8(sp)
sd s0, 0(sp)
addi s0, sp, 16因此,根据RISC-V的栈帧结构
fp - 8存储的是返回地址(ra)。fp - 16存储的是调用者的栈帧指针(fp)。
通过这种方式,可以通过当前栈帧指针逐步回溯到调用者的栈帧。
代码如下:
void backtrace(void)
{
printf("backtrace:\n");
struct proc *p = myproc();
if (p == 0)
return;
uint64 fp = r_fp(); // 帧指针 fp 包含当前栈帧的底部地址
// 将fp看作指针,指向栈帧的底部地址,现在需要将其转换为实际的栈帧地址
// fp = (fp - 16) & ~0xf; // 栈帧底部地址减去16字节,然后对齐到16字节边界
// 反向追踪栈帧,打印返回地址
while (PGROUNDUP(fp) != fp)
{
uint64* ret_addr = (uint64 *)(fp - 8);
printf("%p\n", *ret_addr);
uint64* caller_fp = (uint64*)(fp - 16);
fp = *caller_fp;
}
}终止条件PGROUNDUP(fp) != fp是检查fp是否位于页面顶端。栈向下增长,当fp到达页面对齐地址时,认为已遍历到栈顶,可以退出。