traceback.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. #include "traceback.h"
  2. #include <common/printk.h>
  3. #include <process/process.h>
  4. static int lookup_kallsyms(uint64_t addr, int level)
  5. {
  6. const char *str = (const char *)&kallsyms_names;
  7. // 暴力查找符合要求的symbol
  8. // todo: 改用二分搜索。
  9. // 由于符号表使用nm -n生成,因此是按照地址升序排列的,因此可以二分
  10. uint64_t index = 0;
  11. for (index = 0; index < kallsyms_num - 1; ++index)
  12. {
  13. if (addr > kallsyms_address[index] && addr <= kallsyms_address[index + 1])
  14. break;
  15. }
  16. if (index < kallsyms_num) // 找到对应的函数
  17. {
  18. // 依次输出函数名称、rip离函数起始处的偏移量、函数执行的rip
  19. printk("function:%s() \t(+) %04d address:%#018lx\n", &str[kallsyms_names_index[index]], addr - kallsyms_address[index], addr);
  20. return 0;
  21. }
  22. else
  23. return -1;
  24. }
  25. /**
  26. * @brief 追溯内核栈调用情况
  27. *
  28. * @param regs 内核栈结构体
  29. */
  30. void traceback(struct pt_regs *regs)
  31. {
  32. // 先检验是否为用户态出错,若为用户态出错,则直接返回
  33. if (verify_area(regs->rbp, 0))
  34. {
  35. printk_color(YELLOW, BLACK, "Kernel traceback: Fault in userland. pid=%ld, rbp=%#018lx\n", current_pcb->pid, regs->rbp);
  36. return;
  37. }
  38. uint64_t *rbp = (uint64_t *)regs->rbp;
  39. printk_color(YELLOW, BLACK, "======== Kernel traceback =======\n");
  40. // printk("&kallsyms_address:%#018lx,kallsyms_address:%#018lx\n", &kallsyms_address, kallsyms_address);
  41. // printk("&kallsyms_syms_num:%#018lx,kallsyms_syms_num:%d\n", &kallsyms_num, kallsyms_num);
  42. // printk("&kallsyms_index:%#018lx\n", &kallsyms_names_index);
  43. // printk("&kallsyms_names:%#018lx,kallsyms_names:%s\n", &kallsyms_names, &kallsyms_names);
  44. uint64_t ret_addr = regs->rip;
  45. // 最大追踪10层调用栈
  46. for (int i = 0; i < 10; ++i)
  47. {
  48. printk_color(ORANGE, BLACK, "rbp:%#018lx,*rbp:%#018lx\n", rbp, *rbp);
  49. if (lookup_kallsyms(ret_addr, i) != 0)
  50. break;
  51. // 由于内核栈大小32K,因此当前rbp的值为按照32K对齐时,表明调用栈已经到头了,追踪结束。
  52. if (((*rbp) & (~STACK_SIZE)) == 0)
  53. break;
  54. // 由于x86处理器在执行call指令时,先将调用返回地址压入栈中,然后再把函数的rbp入栈,最后将rsp设为新的rbp。
  55. // 因此,此处的rbp就是上一层的rsp,那么,*(rbp+1)得到的就是上一层函数的返回地址
  56. ret_addr = *(rbp + 1);
  57. rbp = (uint64_t *)(*rbp);
  58. printk("\n");
  59. }
  60. printk_color(YELLOW, BLACK, "======== Kernel traceback end =======\n");
  61. }