MIT 6.s081 lab4.2–backtrace
No.1 写在前面的话
过了页表,难度降了下来,忽然有些不习惯。通过做lab,我已经养成了在每次实验前都会Google搜索相关补充功能的原型的习惯,进行大致的了解。虽然代码看不懂,但至少知道这东西怎么用,backtrace便是如此。
No.2 实验思路
提示中的添加函数和原型不再赘述,主要看后面需要编写代码的部分,只不过,那些提示实在是不知道如何下手,所以再看看实验指导对于backtrace的说明,咬文嚼字一下:
一个存放于栈上用于指示错误发生位置的函数调用列表
编译器向每一个栈帧中放置一个帧指针(frame pointer)保存调用者帧指针的地址。你的backtrace应当使用这些帧指针来遍历栈,并在每个栈帧中打印保存的返回地址.
仔细阅读,便知道了我们现在的目标,有三点:使用指针,遍历栈,打印地址。那么现在先从第一个目标开始。
说起使用指针,首先想到的就是定义一个变量用来存储帧指针,但是帧指针在哪呢?仔细阅读第二条提示,它将帧指针保存在s0寄存器,并且上面继续写道——在backtrace中调用此函数读取指针,所以可以这样:
uint64 fp = r_fp();
第一个目标便完成了,接下来则是遍历栈。第一想法是for循环,但是我也不清楚栈的大小,肯定不行,可以用while吗?提示写道——可以通过PGROUNDDOWN(fp)
和PGROUNDUP(fp)
来计算栈页面的顶部和底部地址。我们知道栈是向下增长的,顶是Prev.Frame(fp),底是栈指针(sp),具体如下:
那么,要想遍历栈只需让其在底部停止就好:
while (fp < PGROUNDUP(fp)){}
接下来,只剩打印地址了。话虽如此,通过前面的vmprint已经知道打印地址是%p,但是我们要打印的是哪一部分的地址呢?
仔细阅读任务目标:【你的backtrace
应当使用这些帧指针来遍历栈,并在每个栈帧中打印保存的返回地址。】以及提示:【注意返回地址位于栈帧帧指针的固定偏移(-8)位置,并且保存的帧指针位于帧指针的固定偏移(-16)位置】,很清楚,这下就明白怎么写了,完整代码如下:
void
backtrace(void){
printf("backtrace:\n");
uint64 fp = r_fp();//存储帧指针
while (fp < PGROUNDUP(fp))
{
uint64 ra = *(uint64*)(fp-8);//转化为uint64的指针,随后指向fp-8的位置
printf("%p\n", ra);
fp = *(uint64*)(fp-16);//保存前一个frame指针,为了能跳转回去
//具体原因在lecture5有说明
}
}
最后记得panic,以及在defs.h中添加:
printf("\n");
backtrace();
panicked = 1; // freeze uart output from other CPUs
//defs.h
void backtrace(void);
这就完成了,很是平缓。【用时半天】