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