|  | /* Copyright (c) 2011 The Regents of the University of California | 
|  | * Barret Rhoden <brho@cs.berkeley.edu> | 
|  | * See LICENSE for details. | 
|  | * | 
|  | * Arch-independent kernel debugging */ | 
|  |  | 
|  | #include <kdebug.h> | 
|  | #include <kmalloc.h> | 
|  | #include <string.h> | 
|  | #include <assert.h> | 
|  | #include <smp.h> | 
|  |  | 
|  | struct symtab_entry gbl_symtab[1] __attribute__((weak)) = {{0, 0}}; | 
|  |  | 
|  | /* Returns a null-terminated string from the reflected symbol table with the | 
|  | * function name for a given PC / instruction pointer.  Returns NULL on | 
|  | * failure. */ | 
|  | const char *get_fn_name(uintptr_t pc) | 
|  | { | 
|  | struct symtab_entry *i, *prev = 0, *found = 0; | 
|  |  | 
|  | /* Table is in ascending order.  As soon as we get to an entry greater | 
|  | * than us, we were in the previous one.  This is only true if we were | 
|  | * given a good PC.  Random addresses will just find the previous | 
|  | * symbol. */ | 
|  | for (i = &gbl_symtab[0]; i->name; i++) { | 
|  | if (i->addr > pc) { | 
|  | found = prev; | 
|  | break; | 
|  | } | 
|  | prev = i; | 
|  | } | 
|  | if (!found) | 
|  | return NULL; | 
|  | assert(found->name); | 
|  | return found->name; | 
|  | } | 
|  |  | 
|  | uintptr_t get_symbol_addr(char *sym) | 
|  | { | 
|  | struct symtab_entry *i; | 
|  |  | 
|  | for (i = &gbl_symtab[0]; i->name; i++) { | 
|  | if (strcmp(i->name, sym) == 0) | 
|  | return i->addr; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const char *blacklist[] = { | 
|  | "addnode", | 
|  | "addqueue", | 
|  | "allocroute", | 
|  | "balancetree", | 
|  | "calcd", | 
|  | "freeroute", | 
|  | "genrandom",	/* not noisy, just never returns */ | 
|  | "limborexmit", | 
|  | "rangecompare", | 
|  | "walkadd", | 
|  | "bnx2x_alloc_rx_data", | 
|  | "bnx2x_frag_alloc", | 
|  | "__dma_map_single", | 
|  | "__dma_mapping_error", | 
|  | "__dma_zalloc_coherent", | 
|  | "__dma_alloc_coherent", | 
|  | "bnx2x_ilt_line_mem_op", | 
|  | "bnx2x_ilt_line_init_op", | 
|  | "bnx2x_ilt_line_wr", | 
|  | "bnx2x_wr_64", | 
|  | "pci_write_config_dword", | 
|  | "bnx2x_init_str_wr", | 
|  | "bnx2x_init_fill", | 
|  | "bnx2x_init_block", | 
|  | "bnx2x_write_big_buf", | 
|  | "bnx2x_init_wr_wb", | 
|  | "bnx2x_write_big_buf_wb", | 
|  | "bnx2x_cl45_read", | 
|  | "bnx2x_cl45_write", | 
|  | "bnx2x_set_mdio_clk", | 
|  | }; | 
|  |  | 
|  | static bool is_blacklisted(const char *s) | 
|  | { | 
|  | for (int i = 0; i < ARRAY_SIZE(blacklist); i++) { | 
|  | if (!strcmp(blacklist[i], s)) | 
|  | return TRUE; | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | static int tab_depth = 0; | 
|  |  | 
|  | /* Call this via kfunc */ | 
|  | void reset_print_func_depth(void) | 
|  | { | 
|  | tab_depth = 0; | 
|  | } | 
|  |  | 
|  | static void __print_hdr(void) | 
|  | { | 
|  | struct per_cpu_info *pcpui = &per_cpu_info[core_id()]; | 
|  | printd("Core %2d ", core_id());	/* may help with multicore output */ | 
|  | if (in_irq_ctx(pcpui)) { | 
|  | printk("IRQ       :"); | 
|  | } else { | 
|  | assert(pcpui->cur_kthread); | 
|  | if (is_ktask(pcpui->cur_kthread)) { | 
|  | printk("%10s:", pcpui->cur_kthread->name); | 
|  | } else { | 
|  | printk("PID %3d   :", pcpui->cur_proc ? | 
|  | pcpui->cur_proc->pid : 0); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void __print_func_entry(const char *func, const char *file) | 
|  | { | 
|  | char tentabs[] = "\t\t\t\t\t\t\t\t\t\t"; // ten tabs and a \0 | 
|  | char *ourtabs = &tentabs[10 - MIN(tab_depth, 10)]; | 
|  |  | 
|  | if (!printx_on) | 
|  | return; | 
|  | if (is_blacklisted(func)) | 
|  | return; | 
|  | print_lock(); | 
|  | __print_hdr(); | 
|  | printk("%s%s() in %s\n", ourtabs, func, file); | 
|  | print_unlock(); | 
|  | tab_depth++; | 
|  | } | 
|  |  | 
|  | void __print_func_exit(const char *func, const char *file) | 
|  | { | 
|  | char tentabs[] = "\t\t\t\t\t\t\t\t\t\t"; // ten tabs and a \0 | 
|  | char *ourtabs; | 
|  |  | 
|  | if (!printx_on) | 
|  | return; | 
|  | if (is_blacklisted(func)) | 
|  | return; | 
|  | tab_depth--; | 
|  | ourtabs = &tentabs[10 - MIN(tab_depth, 10)]; | 
|  | print_lock(); | 
|  | __print_hdr(); | 
|  | printk("%s---- %s()\n", ourtabs, func); | 
|  | print_unlock(); | 
|  | } | 
|  |  | 
|  | bool printx_on = FALSE; | 
|  |  | 
|  | void set_printx(int mode) | 
|  | { | 
|  | switch (mode) { | 
|  | case 0: | 
|  | printx_on = FALSE; | 
|  | break; | 
|  | case 1: | 
|  | printx_on = TRUE; | 
|  | break; | 
|  | case 2: | 
|  | printx_on = !printx_on; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void debug_addr_proc(struct proc *p, unsigned long addr) | 
|  | { | 
|  | struct vm_region *vmr; | 
|  |  | 
|  | spin_lock(&p->vmr_lock); | 
|  | TAILQ_FOREACH(vmr, &p->vm_regions, vm_link) { | 
|  | if ((vmr->vm_base <= addr) && (addr < vmr->vm_end)) | 
|  | break; | 
|  | } | 
|  | if (!vmr) { | 
|  | spin_unlock(&p->vmr_lock); | 
|  | printk("Addr %p has no VMR\n", addr); | 
|  | return; | 
|  | } | 
|  | if (!vmr_has_file(vmr)) { | 
|  | spin_unlock(&p->vmr_lock); | 
|  | printk("Addr %p's VMR has no file\n", addr); | 
|  | return; | 
|  | } | 
|  | printk("Addr %p is in %s at offset %p\n", addr, vmr_to_filename(vmr), | 
|  | addr - vmr->vm_base + vmr->vm_foff); | 
|  | spin_unlock(&p->vmr_lock); | 
|  | } | 
|  |  | 
|  | void debug_addr_pid(int pid, unsigned long addr) | 
|  | { | 
|  | struct proc *p; | 
|  | p = pid2proc(pid); | 
|  | if (!p) { | 
|  | printk("No such proc for pid %d\n", pid); | 
|  | return; | 
|  | } | 
|  | debug_addr_proc(p, addr); | 
|  | proc_decref(p); | 
|  | } | 
|  |  | 
|  | #define BT_FMT "#%02d [<%p>] in %s\n" | 
|  |  | 
|  | void print_backtrace_list(uintptr_t *pcs, size_t nr_pcs, void (*pfunc)(void *, | 
|  | const | 
|  | char *), | 
|  | void *opaque) | 
|  | { | 
|  | char bt_line[128]; | 
|  |  | 
|  | for (size_t i = 0; i < nr_pcs; i++) { | 
|  | snprintf(bt_line, sizeof(bt_line), BT_FMT, i + 1, pcs[i], | 
|  | get_fn_name(pcs[i])); | 
|  | pfunc(opaque, bt_line); | 
|  | } | 
|  | } | 
|  |  | 
|  | void sza_print_backtrace_list(struct sized_alloc *sza, uintptr_t *pcs, | 
|  | size_t nr_pcs) | 
|  | { | 
|  | for (size_t i = 0; i < nr_pcs; i++) | 
|  | sza_printf(sza, BT_FMT, i + 1, pcs[i], get_fn_name(pcs[i])); | 
|  | } | 
|  |  | 
|  | static void printk_func(void *opaque, const char *str) | 
|  | { | 
|  | printk("%s", str); | 
|  | } | 
|  |  | 
|  | void backtrace(void) | 
|  | { | 
|  | print_lock(); | 
|  | printk("Stack Backtrace on Core %d:\n", core_id()); | 
|  | gen_backtrace(&printk_func, NULL); | 
|  | print_unlock(); | 
|  | } | 
|  |  | 
|  | static void trace_printk_func(void *opaque, const char *str) | 
|  | { | 
|  | trace_printk("%s", str); | 
|  | } | 
|  |  | 
|  | void backtrace_trace(void) | 
|  | { | 
|  | /* Don't need this strictly, but it helps serialize to the trace buf */ | 
|  | print_lock(); | 
|  | trace_printk("Stack Backtrace on Core %d:\n", core_id()); | 
|  | gen_backtrace(&trace_printk_func, NULL); | 
|  | print_unlock(); | 
|  | } | 
|  |  | 
|  | static void trace_printx_func(void *opaque, const char *str) | 
|  | { | 
|  | trace_printx("%s", str); | 
|  | } | 
|  |  | 
|  | void backtrace_trace_printx(void) | 
|  | { | 
|  | /* Don't need this strictly, but it helps serialize to the trace buf */ | 
|  | print_lock(); | 
|  | trace_printx("Stack Backtrace on Core %d:\n", core_id()); | 
|  | gen_backtrace(&trace_printk_func, NULL); | 
|  | print_unlock(); | 
|  | } | 
|  |  | 
|  | void backtrace_frame(uintptr_t eip, uintptr_t ebp) | 
|  | { | 
|  | uintptr_t pcs[MAX_BT_DEPTH]; | 
|  | size_t nr_pcs = backtrace_list(eip, ebp, pcs, MAX_BT_DEPTH); | 
|  |  | 
|  | print_lock(); | 
|  | printk("\nBacktrace of kernel context on Core %d:\n", core_id()); | 
|  | print_backtrace_list(pcs, nr_pcs, &printk_func, NULL); | 
|  | print_unlock(); | 
|  | } | 
|  |  | 
|  | /* TODO: change debug_addr_proc() to allow print redirection like | 
|  | * print_backtrace_list(). */ | 
|  | void backtrace_user_frame(uintptr_t eip, uintptr_t ebp) | 
|  | { | 
|  | uintptr_t pcs[MAX_BT_DEPTH]; | 
|  | /* TODO: this assumes we have the user's address space loaded (current). | 
|  | */ | 
|  | size_t nr_pcs = backtrace_user_list(eip, ebp, pcs, MAX_BT_DEPTH); | 
|  |  | 
|  | print_lock(); | 
|  | printk("\nBacktrace of user context on Core %d:\n", core_id()); | 
|  | printk("\tOffsets only matter for shared libraries\n"); | 
|  | /* This formatting is consumed by scripts/bt-akaros.sh. */ | 
|  | for (int i = 0; i < nr_pcs; i++) { | 
|  | printk("#%02d ", i + 1); | 
|  | /* TODO: user backtraces all assume we're working on 'current'*/ | 
|  | debug_addr_proc(current, pcs[i]); | 
|  | } | 
|  | print_unlock(); | 
|  | } | 
|  |  | 
|  | void backtrace_hwtf(struct hw_trapframe *hw_tf) | 
|  | { | 
|  | if (in_kernel(hw_tf)) | 
|  | backtrace_frame(get_hwtf_pc(hw_tf), get_hwtf_fp(hw_tf)); | 
|  | else | 
|  | backtrace_user_frame(get_hwtf_pc(hw_tf), get_hwtf_fp(hw_tf)); | 
|  | } | 
|  |  | 
|  | void backtrace_user_ctx(struct proc *p, struct user_context *ctx) | 
|  | { | 
|  | uintptr_t st_save; | 
|  |  | 
|  | if (!ctx) { | 
|  | printk("Null user context!\n"); | 
|  | return; | 
|  | } | 
|  | st_save = switch_to(p); | 
|  | backtrace_user_frame(get_user_ctx_pc(ctx), get_user_ctx_fp(ctx)); | 
|  | switch_back(p, st_save); | 
|  | } | 
|  |  | 
|  | void backtrace_current_ctx(void) | 
|  | { | 
|  | if (current) | 
|  | backtrace_user_ctx(current, current_ctx); | 
|  | } | 
|  |  | 
|  | void backtrace_kthread(struct kthread *kth) | 
|  | { | 
|  | backtrace_frame(jmpbuf_get_pc(&kth->context), | 
|  | jmpbuf_get_fp(&kth->context)); | 
|  | } |