【MIT-OS6.S081作业-4-2】Lab4-traps-Backtrace

本文记录MIT-OS6.S081 Lab4 traps的Backtrace函数的实现过程

文章目录

  • [1. 作业要求](#1. 作业要求)
  • [2. 实现过程](#2. 实现过程)
    • [2.1 代码实现](#2.1 代码实现)

1. 作业要求

Backtrace (moderate)

For debugging it is often useful to have a backtrace: a list of the function calls on the stack above the point at which the error occurred.

Implement a backtrace() function in kernel/printf.c. Insert a call to this function in sys_sleep, and then run bttest, which calls sys_sleep. Your output should be as follows:

c 复制代码
backtrace:
0x0000000080002cda
0x0000000080002bb6
0x0000000080002898

After bttest exit qemu. In your terminal: the addresses may be slightly different but if you run addr2line -e kernel/kernel (or riscv64-unknown-elf-addr2line -e kernel/kernel) and cut-and-paste the above addresses as follows:

c 复制代码
$ addr2line -e kernel/kernel
0x0000000080002de2
0x0000000080002f4a
0x0000000080002bfc
Ctrl-D

You should see something like this:

c 复制代码
kernel/sysproc.c:74
kernel/syscall.c:224
kernel/trap.c:85

The compiler puts in each stack frame a frame pointer that holds the address of the caller's frame pointer. Your backtrace should use these frame pointers to walk up the stack and print the saved return address in each stack frame.
Some hints:

  • Add the prototype for backtrace to kernel/defs.h so that you can invoke backtrace in sys_sleep.
  • The GCC compiler stores the frame pointer of the currently executing function in the register s0. Add the following function to kernel/riscv.h:
c 复制代码
static inline uint64
r_fp()
{
  uint64 x;
  asm volatile("mv %0, s0" : "=r" (x) );
  return x;
}

and call this function in backtrace to read the current frame pointer. This function uses in-line assembly to read s0.

  • These lecture notes have a picture of the layout of stack frames. Note that the return address lives at a fixed offset (-8) from the frame pointer of a stackframe, and that the saved frame pointer lives at fixed offset (-16) from the frame pointer.
  • Xv6 allocates one page for each stack in the xv6 kernel at PAGE-aligned address. You can compute the top and bottom address of the stack page by using PGROUNDDOWN(fp) and PGROUNDUP(fp) (see kernel/riscv.h. These number are helpful for backtrace to terminate its loop.
    Once your backtrace is working, call it from panic in kernel/printf.c so that you see the kernel's backtrace when it panics.

2. 实现过程

2.1 代码实现

我们先看一下bttest

c 复制代码
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int
main(int argc, char *argv[])
{
  sleep(1);
  exit(0);
}

这里调用了sleep,题目说backtrace要被插入到sleep里。

Add the prototype for backtrace to kernel/defs.h so that you can invoke backtrace in sys_sleep.

c 复制代码
// printf.c
void            printf(char*, ...);
void            panic(char*) __attribute__((noreturn));
void            printfinit(void);
void            backtrace(void); // 添加在这里

The GCC compiler stores the frame pointer of the currently executing function in the register s0. Add the following function to kernel/riscv.h

s0寄存器存放当前执行函数的frame指针。我们需要在kernel/riscv.h添加函数:

c 复制代码
static inline uint64
r_fp()
{
  uint64 x;
  asm volatile("mv %0, s0" : "=r" (x) );
  return x;
}

call this function in backtrace to read the current frame pointer. This function uses in-line assembly to read s0.

backtrace会使用上面的r_fp函数读取s0寄存器。

These lecture notes have a picture of the layout of stack frames. Note that the return address lives at a fixed offset (-8) from the frame pointer of a stackframe, and that the saved frame pointer lives at fixed offset (-16) from the frame pointer.

从这句话可以看出栈的布局

Xv6 allocates one page for each stack in the xv6 kernel at PAGE-aligned address. You can compute the top and bottom address of the stack page by using PGROUNDDOWN(fp) and PGROUNDUP(fp) (see kernel/riscv.h. These number are helpful for backtrace to terminate its loop.

xv6会为栈分配一页,backtrace沿着上面的内存布局不断上溯,其实是一个从低地址往高地址回溯的过程,停止的条件就是当fp超过[PGROUNDDOWN(fp), PGROUNDUP(fp))范围。

于是在printf我们很自然地可以写出backtrace

c 复制代码
void            
backtrace()
{
  uint64 fp = r_fp();
  uint64 stackUp = PGROUNDUP(fp);
  uint64 stackBottom = PGROUNDDOWN(fp);
  while (fp >= stackBottom && fp < stackUp)
  {
    uint64 ra = *(uint64*)(fp - 8);
    printf("%p\n", ra);
    fp = *(uint64*)(fp - 16);
  }
}

我们测试一下:

c 复制代码
make qemu
bttest

然后我们再使用作业所说的测试命令:

cpp 复制代码
./grade-lab-traps backtrace

完活!理解了其实不难的。

相关推荐
sulikey8 小时前
个人Linux操作系统学习笔记2 - gcc与库的理解
linux·笔记·学习·操作系统·gcc·
手可摘星辰的少年17 小时前
Ext2文件系统核心结构详解:超级块、位图、Inode与多级间接块
操作系统
星马梦缘18 小时前
操作系统实验5 —— 进程互斥
linux·操作系统·进程互斥
iCxhust20 小时前
从裸机到微内核:8088单板机微型操作系统规划设计
操作系统·课程设计·微机原理·8086最小系统·8088单板机
磊 子2 天前
1.2内存的存储金字塔
java·开发语言·spring·操作系统
sulikey2 天前
Linux ext2文件系统结构
linux·操作系统·文件系统·linux文件系统·ext2·ext2文件系统
-To be number.wan2 天前
进程与线程的区别
学习·操作系统
sulikey3 天前
ext2 GDT 块组描述符表 详细技术拆解
linux·操作系统·文件系统·ext2·gdt·ext·块组描述符
山木嵌入式3 天前
FreeRTOS从入门到进阶:核心概念与调度原理全解析
stm32·操作系统·嵌入式·freertos·rtos
暴力求解3 天前
Linux--网络-->UDP_socket
linux·网络·网络协议·udp·操作系统