diff --git a/kernel/elf.c b/kernel/elf.c
index beb3dffc78e37419e145574f1394303100289cb4..2f374b64ea72deb0a5ee7ee45a13f63a2abdb1bf 100644
--- a/kernel/elf.c
+++ b/kernel/elf.c
@@ -8,15 +8,23 @@
 #include "riscv.h"
 #include "spike_interface/spike_utils.h"
 
-typedef struct elf_info_t {
+typedef struct elf_info_t
+{
   spike_file_t *f;
   process *p;
 } elf_info;
 
+elf_symbol symbols[64];
+char sym_names[64][32];
+int sym_count;
+
+void load_func_name(elf_ctx *ctx);
+
 //
 // the implementation of allocater. allocates memory space for later segment loading
 //
-static void *elf_alloc_mb(elf_ctx *ctx, uint64 elf_pa, uint64 elf_va, uint64 size) {
+static void *elf_alloc_mb(elf_ctx *ctx, uint64 elf_pa, uint64 elf_va, uint64 size)
+{
   // directly returns the virtual address as we are in the Bare mode in lab1_x
   return (void *)elf_va;
 }
@@ -24,7 +32,8 @@ static void *elf_alloc_mb(elf_ctx *ctx, uint64 elf_pa, uint64 elf_va, uint64 siz
 //
 // actual file reading, using the spike file interface.
 //
-static uint64 elf_fpread(elf_ctx *ctx, void *dest, uint64 nb, uint64 offset) {
+static uint64 elf_fpread(elf_ctx *ctx, void *dest, uint64 nb, uint64 offset)
+{
   elf_info *msg = (elf_info *)ctx->info;
   // call spike file utility to load the content of elf file into memory.
   // spike_file_pread will read the elf file (msg->f) from offset to memory (indicated by
@@ -35,14 +44,17 @@ static uint64 elf_fpread(elf_ctx *ctx, void *dest, uint64 nb, uint64 offset) {
 //
 // init elf_ctx, a data structure that loads the elf.
 //
-elf_status elf_init(elf_ctx *ctx, void *info) {
+elf_status elf_init(elf_ctx *ctx, void *info)
+{
   ctx->info = info;
 
   // load the elf header
-  if (elf_fpread(ctx, &ctx->ehdr, sizeof(ctx->ehdr), 0) != sizeof(ctx->ehdr)) return EL_EIO;
+  if (elf_fpread(ctx, &ctx->ehdr, sizeof(ctx->ehdr), 0) != sizeof(ctx->ehdr))
+    return EL_EIO;
 
   // check the signature (magic value) of the elf
-  if (ctx->ehdr.magic != ELF_MAGIC) return EL_NOTELF;
+  if (ctx->ehdr.magic != ELF_MAGIC)
+    return EL_NOTELF;
 
   return EL_OK;
 }
@@ -50,19 +62,25 @@ elf_status elf_init(elf_ctx *ctx, void *info) {
 //
 // load the elf segments to memory regions as we are in Bare mode in lab1
 //
-elf_status elf_load(elf_ctx *ctx) {
+elf_status elf_load(elf_ctx *ctx)
+{
   // elf_prog_header structure is defined in kernel/elf.h
   elf_prog_header ph_addr;
   int i, off;
 
   // traverse the elf program segment headers
-  for (i = 0, off = ctx->ehdr.phoff; i < ctx->ehdr.phnum; i++, off += sizeof(ph_addr)) {
+  for (i = 0, off = ctx->ehdr.phoff; i < ctx->ehdr.phnum; i++, off += sizeof(ph_addr))
+  {
     // read segment headers
-    if (elf_fpread(ctx, (void *)&ph_addr, sizeof(ph_addr), off) != sizeof(ph_addr)) return EL_EIO;
+    if (elf_fpread(ctx, (void *)&ph_addr, sizeof(ph_addr), off) != sizeof(ph_addr))
+      return EL_EIO;
 
-    if (ph_addr.type != ELF_PROG_LOAD) continue;
-    if (ph_addr.memsz < ph_addr.filesz) return EL_ERR;
-    if (ph_addr.vaddr + ph_addr.memsz < ph_addr.vaddr) return EL_ERR;
+    if (ph_addr.type != ELF_PROG_LOAD)
+      continue;
+    if (ph_addr.memsz < ph_addr.filesz)
+      return EL_ERR;
+    if (ph_addr.vaddr + ph_addr.memsz < ph_addr.vaddr)
+      return EL_ERR;
 
     // allocate memory block before elf loading
     void *dest = elf_alloc_mb(ctx, ph_addr.vaddr, ph_addr.vaddr, ph_addr.memsz);
@@ -75,7 +93,8 @@ elf_status elf_load(elf_ctx *ctx) {
   return EL_OK;
 }
 
-typedef union {
+typedef union
+{
   uint64 buf[MAX_CMDLINE_ARGS];
   char *argv[MAX_CMDLINE_ARGS];
 } arg_buf;
@@ -84,36 +103,39 @@ typedef union {
 // returns the number (should be 1) of string(s) after PKE kernel in command line.
 // and store the string(s) in arg_bug_msg.
 //
-static size_t parse_args(arg_buf *arg_bug_msg) {
+static size_t parse_args(arg_buf *arg_bug_msg)
+{
   // HTIFSYS_getmainvars frontend call reads command arguments to (input) *arg_bug_msg
   long r = frontend_syscall(HTIFSYS_getmainvars, (uint64)arg_bug_msg,
-      sizeof(*arg_bug_msg), 0, 0, 0, 0, 0);
+                            sizeof(*arg_bug_msg), 0, 0, 0, 0, 0);
   kassert(r == 0);
 
   size_t pk_argc = arg_bug_msg->buf[0];
   uint64 *pk_argv = &arg_bug_msg->buf[1];
 
-  int arg = 1;  // skip the PKE OS kernel string, leave behind only the application name
+  int arg = 1; // skip the PKE OS kernel string, leave behind only the application name
   for (size_t i = 0; arg + i < pk_argc; i++)
     arg_bug_msg->argv[i] = (char *)(uintptr_t)pk_argv[arg + i];
 
-  //returns the number of strings after PKE kernel in command line
+  // returns the number of strings after PKE kernel in command line
   return pk_argc - arg;
 }
 
 //
 // load the elf of user application, by using the spike file interface.
 //
-void load_bincode_from_host_elf(process *p) {
+void load_bincode_from_host_elf(process *p)
+{
   arg_buf arg_bug_msg;
 
   // retrieve command line arguements
   size_t argc = parse_args(&arg_bug_msg);
-  if (!argc) panic("You need to specify the application program!\n");
+  if (!argc)
+    panic("You need to specify the application program!\n");
 
   sprint("Application: %s\n", arg_bug_msg.argv[0]);
 
-  //elf loading. elf_ctx is defined in kernel/elf.h, used to track the loading process.
+  // elf loading. elf_ctx is defined in kernel/elf.h, used to track the loading process.
   elf_ctx elfloader;
   // elf_info is defined above, used to tie the elf file and its corresponding process.
   elf_info info;
@@ -121,20 +143,73 @@ void load_bincode_from_host_elf(process *p) {
   info.f = spike_file_open(arg_bug_msg.argv[0], O_RDONLY, 0);
   info.p = p;
   // IS_ERR_VALUE is a macro defined in spike_interface/spike_htif.h
-  if (IS_ERR_VALUE(info.f)) panic("Fail on openning the input application program.\n");
+  if (IS_ERR_VALUE(info.f))
+    panic("Fail on openning the input application program.\n");
 
   // init elfloader context. elf_init() is defined above.
   if (elf_init(&elfloader, &info) != EL_OK)
     panic("fail to init elfloader.\n");
 
   // load elf. elf_load() is defined above.
-  if (elf_load(&elfloader) != EL_OK) panic("Fail on loading elf.\n");
+  if (elf_load(&elfloader) != EL_OK)
+    panic("Fail on loading elf.\n");
 
   // entry (virtual, also physical in lab1_x) address
   p->trapframe->epc = elfloader.ehdr.entry;
 
+  load_func_name(&elfloader);
+
   // close the host spike file
-  spike_file_close( info.f );
+  spike_file_close(info.f);
 
   sprint("Application program entry point (virtual address): 0x%lx\n", p->trapframe->epc);
 }
+
+void load_func_name(elf_ctx *ctx)
+{
+  elf_sect_header sym_sh;
+  elf_sect_header str_sh;
+  elf_sect_header shstr_sh;
+  elf_sect_header temp_sh;
+
+  // 查找shstrtab
+  uint16 sect_num = ctx->ehdr.shnum;
+  uint64 shstr_offset = ctx->ehdr.shoff + ctx->ehdr.shstrndx * sizeof(elf_sect_header);
+  elf_fpread(ctx, (void *)&shstr_sh, sizeof(shstr_sh), shstr_offset);
+
+  char temp_str[shstr_sh.sh_size];
+  uint64 shstr_sect_off = shstr_sh.sh_offset;
+  elf_fpread(ctx, &temp_str, shstr_sh.sh_size, shstr_sect_off);
+
+  // 查找strtab和symtab
+  for (int i = 0; i < sect_num; i++)
+  {
+    elf_fpread(ctx, (void *)&temp_sh, sizeof(temp_sh), ctx->ehdr.shoff + i * ctx->ehdr.shentsize);
+    uint32 type = temp_sh.sh_type;
+    if (type == SHT_SYMTAB)
+    {
+      sym_sh = temp_sh;
+    }
+    else if (type == SHT_STRTAB && strcmp(temp_str + temp_sh.sh_name, ".strtab") == 0)
+    {
+      str_sh = temp_sh;
+    }
+  }
+
+  uint64 str_sect_off = str_sh.sh_offset;
+  uint64 sym_num = sym_sh.sh_size / sizeof(elf_symbol);
+  int count = 0;
+  for (int i = 0; i < sym_num; i++)
+  {
+    elf_symbol symbol;
+    elf_fpread(ctx, (void *)&symbol, sizeof(symbol), sym_sh.sh_offset + i * sizeof(elf_symbol));
+    if (symbol.st_name != 0 && symbol.st_info == 18)
+    {
+      char sym_name[32];
+      elf_fpread(ctx, (void *)&sym_name, sizeof(sym_name), str_sect_off + symbol.st_name);
+      symbols[count++] = symbol;
+      strcpy(sym_names[count - 1], sym_name);
+    }
+  }
+  sym_count = count;
+}
diff --git a/kernel/elf.h b/kernel/elf.h
index 673c7d72a146a2b00495e3740e13e692db211cd8..bef1ce04222c49be8a5c1d00bf70d72e4de29f42 100644
--- a/kernel/elf.h
+++ b/kernel/elf.h
@@ -6,8 +6,37 @@
 
 #define MAX_CMDLINE_ARGS 64
 
+#define SHT_SYMTAB 2
+#define SHT_STRTAB 3 
+#define STT_FUNC 2
+
+typedef struct elf_sect_header_t
+{
+  uint32 sh_name;      /* Section name, index in string tbl */
+  uint32 sh_type;      /* Type of section */
+  uint64 sh_flags;     /* Miscellaneous section attributes */
+  uint64 sh_addr;      /* Section virtual addr at execution */
+  uint64 sh_offset;    /* Section file offset */
+  uint64 sh_size;      /* Size of section in bytes */
+  uint32 sh_link;      /* Index of another section */
+  uint32 sh_info;      /* Additional section information */
+  uint64 sh_addralign; /* Section alignment */
+  uint64 sh_entsize;   /* Entry size if section holds table */
+} elf_sect_header;
+
+typedef struct elf64_sym
+{
+  uint32 st_name;         /* Symbol name, index in string tbl */
+  unsigned char st_info;  /* Type and binding attributes */
+  unsigned char st_other; /* No defined meaning, 0 */
+  uint16 st_shndx;        /* Associated section index */
+  uint64 st_value;        /* Value of the symbol */
+  uint64 st_size;         /* Associated symbol size */
+} elf_symbol;
+
 // elf header structure
-typedef struct elf_header_t {
+typedef struct elf_header_t
+{
   uint32 magic;
   uint8 elf[12];
   uint16 type;      /* Object file type */
@@ -26,7 +55,8 @@ typedef struct elf_header_t {
 } elf_header;
 
 // Program segment header.
-typedef struct elf_prog_header_t {
+typedef struct elf_prog_header_t
+{
   uint32 type;   /* Segment type */
   uint32 flags;  /* Segment flags */
   uint64 off;    /* Segment file offset */
@@ -37,10 +67,11 @@ typedef struct elf_prog_header_t {
   uint64 align;  /* Segment alignment */
 } elf_prog_header;
 
-#define ELF_MAGIC 0x464C457FU  // "\x7FELF" in little endian
+#define ELF_MAGIC 0x464C457FU // "\x7FELF" in little endian
 #define ELF_PROG_LOAD 1
 
-typedef enum elf_status_t {
+typedef enum elf_status_t
+{
   EL_OK = 0,
 
   EL_EIO,
@@ -50,7 +81,8 @@ typedef enum elf_status_t {
 
 } elf_status;
 
-typedef struct elf_ctx_t {
+typedef struct elf_ctx_t
+{
   void *info;
   elf_header ehdr;
 } elf_ctx;
@@ -60,4 +92,6 @@ elf_status elf_load(elf_ctx *ctx);
 
 void load_bincode_from_host_elf(process *p);
 
+void elf_user_print_backtrace(uint64 depth);
+
 #endif
diff --git a/kernel/syscall.c b/kernel/syscall.c
index 562245a09df3ca42c347320ff12c4316e11e219d..df58983f0393d461ad91ee408d258385c2525dc5 100644
--- a/kernel/syscall.c
+++ b/kernel/syscall.c
@@ -13,10 +13,17 @@
 
 #include "spike_interface/spike_utils.h"
 
+#include "elf.h"
+
+extern elf_symbol symbols[64];
+extern char sym_names[64][32];
+extern int sym_count;
+
 //
 // implement the SYS_user_print syscall
 //
-ssize_t sys_user_print(const char* buf, size_t n) {
+ssize_t sys_user_print(const char *buf, size_t n)
+{
   sprint(buf);
   return 0;
 }
@@ -24,24 +31,64 @@ ssize_t sys_user_print(const char* buf, size_t n) {
 //
 // implement the SYS_user_exit syscall
 //
-ssize_t sys_user_exit(uint64 code) {
+ssize_t sys_user_exit(uint64 code)
+{
   sprint("User exit with code:%d.\n", code);
-  // in lab1, PKE considers only one app (one process). 
+  // in lab1, PKE considers only one app (one process).
   // therefore, shutdown the system when the app calls exit()
   shutdown(code);
 }
 
+// ssize_t sys_user_print_backtrace(uint64 depth) {
+//   elf_user_print_backtrace(depth);
+//   return 0;
+// }
+
+int func_name_printer(uint64 ret_addr)
+{
+  for (int i = 0; i < sym_count; i++)
+  {
+    if (ret_addr >= symbols[i].st_value && ret_addr < symbols[i].st_value + symbols[i].st_size)
+    {
+      sprint("%s\n", sym_names[i]);
+      if (strcmp(sym_names[i], "main") == 0)
+        return 0;
+      return 1;
+    }
+  }
+  return 1;
+}
+
+ssize_t sys_user_print_backtrace(uint64 max_depth)
+{
+  uint64 sp = current->trapframe->regs.sp + 32;
+  uint64 ra = sp + 8;
+  uint64 depth = 0;
+  while (depth < max_depth)
+  {
+    if (func_name_printer(*(uint64 *)ra) == 0)
+      return depth;
+    ra += 16;
+    depth++;
+  }
+  return depth;
+}
+
 //
 // [a0]: the syscall number; [a1] ... [a7]: arguments to the syscalls.
 // returns the code of success, (e.g., 0 means success, fail for otherwise)
 //
-long do_syscall(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7) {
-  switch (a0) {
-    case SYS_user_print:
-      return sys_user_print((const char*)a1, a2);
-    case SYS_user_exit:
-      return sys_user_exit(a1);
-    default:
-      panic("Unknown syscall %ld \n", a0);
+long do_syscall(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7)
+{
+  switch (a0)
+  {
+  case SYS_user_print:
+    return sys_user_print((const char *)a1, a2);
+  case SYS_user_exit:
+    return sys_user_exit(a1);
+  case SYS_user_print_backtrace:
+    return sys_user_print_backtrace(a1);
+  default:
+    panic("Unknown syscall %ld \n", a0);
   }
 }
diff --git a/kernel/syscall.h b/kernel/syscall.h
index 9dd228c7b60e67f6f7f3df7b74ee68d03d3cec5e..b4492c0190e237121480d7176a1030e1d653ae8c 100644
--- a/kernel/syscall.h
+++ b/kernel/syscall.h
@@ -8,6 +8,7 @@
 #define SYS_user_base 64
 #define SYS_user_print (SYS_user_base + 0)
 #define SYS_user_exit (SYS_user_base + 1)
+#define SYS_user_print_backtrace (SYS_user_base + 2)
 
 long do_syscall(long a0, long a1, long a2, long a3, long a4, long a5, long a6, long a7);
 
diff --git a/user/user_lib.c b/user/user_lib.c
index fff4546d3753fc2d9d8133b08a4b5240cc9c13ec..f5daf91b495adc0a689061104be56260c4e26513 100644
--- a/user/user_lib.c
+++ b/user/user_lib.c
@@ -49,3 +49,7 @@ int printu(const char* s, ...) {
 int exit(int code) {
   return do_user_call(SYS_user_exit, code, 0, 0, 0, 0, 0, 0); 
 }
+
+int print_backtrace(int max_depth) {
+  return do_user_call(SYS_user_print_backtrace, max_depth, 0, 0, 0, 0, 0, 0);
+}
diff --git a/user/user_lib.h b/user/user_lib.h
index 7c538057a29ce38fdba9d34eb5c0504e85d1f05c..eed3f8c91f57e47b48515dcb430efc81e6420633 100644
--- a/user/user_lib.h
+++ b/user/user_lib.h
@@ -4,3 +4,5 @@
 
 int printu(const char *s, ...);
 int exit(int code);
+
+int print_backtrace(int max_depth);