xref: /DragonOS/kernel/src/debug/traceback/traceback.c (revision 1496ba7b24a5e6954291ca9643b9f3cec567479a)
12813126eSlogin #include "traceback.h"
22813126eSlogin #include <common/printk.h>
32813126eSlogin #include <process/process.h>
42813126eSlogin 
52813126eSlogin static int lookup_kallsyms(uint64_t addr, int level)
62813126eSlogin {
72813126eSlogin     const char *str = (const char *)&kallsyms_names;
82813126eSlogin 
92813126eSlogin     // 暴力查找符合要求的symbol
102813126eSlogin     // todo: 改用二分搜索。
112813126eSlogin     // 由于符号表使用nm -n生成,因此是按照地址升序排列的,因此可以二分
122813126eSlogin     uint64_t index = 0;
132813126eSlogin     for (index = 0; index < kallsyms_num - 1; ++index)
142813126eSlogin     {
152813126eSlogin         if (addr > kallsyms_address[index] && addr <= kallsyms_address[index + 1])
162813126eSlogin             break;
172813126eSlogin     }
182813126eSlogin 
192813126eSlogin     if (index < kallsyms_num) // 找到对应的函数
202813126eSlogin     {
212813126eSlogin         // 依次输出函数名称、rip离函数起始处的偏移量、函数执行的rip
222813126eSlogin         printk("function:%s() \t(+) %04d address:%#018lx\n", &str[kallsyms_names_index[index]], addr - kallsyms_address[index], addr);
232813126eSlogin         return 0;
242813126eSlogin     }
252813126eSlogin     else
262813126eSlogin         return -1;
272813126eSlogin }
282813126eSlogin 
292813126eSlogin /**
302813126eSlogin  * @brief 追溯内核栈调用情况
312813126eSlogin  *
322813126eSlogin  * @param regs 内核栈结构体
332813126eSlogin  */
342813126eSlogin void traceback(struct pt_regs *regs)
352813126eSlogin {
362813126eSlogin     // 先检验是否为用户态出错,若为用户态出错,则直接返回
372813126eSlogin     if (verify_area(regs->rbp, 0))
382813126eSlogin     {
39*1496ba7bSLoGin         printk_color(YELLOW, BLACK, "Kernel traceback: Fault in userland. pid=%ld, rbp=%#018lx\n", rs_current_pcb_pid(), regs->rbp);
402813126eSlogin         return;
412813126eSlogin     }
422813126eSlogin 
432813126eSlogin     uint64_t *rbp = (uint64_t *)regs->rbp;
442813126eSlogin     printk_color(YELLOW, BLACK, "======== Kernel traceback =======\n");
452813126eSlogin     // printk("&kallsyms_address:%#018lx,kallsyms_address:%#018lx\n", &kallsyms_address, kallsyms_address);
462813126eSlogin     // printk("&kallsyms_syms_num:%#018lx,kallsyms_syms_num:%d\n", &kallsyms_num, kallsyms_num);
472813126eSlogin     // printk("&kallsyms_index:%#018lx\n", &kallsyms_names_index);
482813126eSlogin     // printk("&kallsyms_names:%#018lx,kallsyms_names:%s\n", &kallsyms_names, &kallsyms_names);
492813126eSlogin 
502813126eSlogin     uint64_t ret_addr = regs->rip;
512813126eSlogin     // 最大追踪10层调用栈
522813126eSlogin     for (int i = 0; i < 10; ++i)
532813126eSlogin     {
542813126eSlogin         if (lookup_kallsyms(ret_addr, i) != 0)
552813126eSlogin             break;
562813126eSlogin 
572813126eSlogin         // 当前栈帧的rbp的地址大于等于内核栈的rbp的时候,表明调用栈已经到头了,追踪结束。
582813126eSlogin         // 当前rbp的地址为用户空间时,直接退出
59*1496ba7bSLoGin         if ((uint64_t)(rbp) >= rs_current_pcb_thread_rbp() || ((uint64_t)rbp < regs->rsp))
602813126eSlogin             break;
612813126eSlogin 
622813126eSlogin         printk_color(ORANGE, BLACK, "rbp:%#018lx,*rbp:%#018lx\n", rbp, *rbp);
632813126eSlogin 
642813126eSlogin         // 由于x86处理器在执行call指令时,先将调用返回地址压入栈中,然后再把函数的rbp入栈,最后将rsp设为新的rbp。
652813126eSlogin         // 因此,此处的rbp就是上一层的rsp,那么,*(rbp+1)得到的就是上一层函数的返回地址
662813126eSlogin         ret_addr = *(rbp + 1);
672813126eSlogin         rbp = (uint64_t *)(*rbp);
682813126eSlogin         printk("\n");
692813126eSlogin     }
702813126eSlogin     printk_color(YELLOW, BLACK, "======== Kernel traceback end =======\n");
712813126eSlogin }