|  | /* 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 with the function name for a given PC / | 
|  | * instruction pointer.  kfree() the result. */ | 
|  | char *get_fn_name(uintptr_t pc) | 
|  | { | 
|  | struct symtab_entry *i, *prev = 0, *found = 0; | 
|  | char *buf; | 
|  | size_t name_len; | 
|  | /* 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 0; | 
|  | assert(found->name); | 
|  | name_len = strlen(found->name) + 1; | 
|  | buf = kmalloc(name_len, 0); | 
|  | if (!buf) | 
|  | return 0; | 
|  | strncpy(buf, found->name, name_len); | 
|  | buf[name_len] = 0; | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | 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 */ | 
|  | "rangecompare", | 
|  | "walkadd", | 
|  | "limborexmit", | 
|  | }; | 
|  |  | 
|  | 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; | 
|  | static bool print = TRUE; | 
|  |  | 
|  | /* Call these via kfunc */ | 
|  | void reset_print_func_depth(void) | 
|  | { | 
|  | tab_depth = 0; | 
|  | } | 
|  |  | 
|  | void toggle_print_func(void) | 
|  | { | 
|  | print = !print; | 
|  | printk("Func entry/exit printing is now %sabled\n", print ? "en" : "dis"); | 
|  | } | 
|  |  | 
|  | static spinlock_t lock = SPINLOCK_INITIALIZER_IRQSAVE; | 
|  |  | 
|  | void __print_func_entry(const char *func, const char *file) | 
|  | { | 
|  | if (!print) | 
|  | return; | 
|  | if (is_blacklisted(func)) | 
|  | return; | 
|  | spin_lock_irqsave(&lock); | 
|  | printd("Core %2d", core_id());	/* helps with multicore output */ | 
|  | for (int i = 0; i < tab_depth; i++) | 
|  | printk("\t"); | 
|  | printk("%s() in %s\n", func, file); | 
|  | spin_unlock_irqsave(&lock); | 
|  | tab_depth++; | 
|  | } | 
|  |  | 
|  | void __print_func_exit(const char *func, const char *file) | 
|  | { | 
|  | if (!print) | 
|  | return; | 
|  | if (is_blacklisted(func)) | 
|  | return; | 
|  | tab_depth--; | 
|  | spin_lock_irqsave(&lock); | 
|  | printd("Core %2d", core_id()); | 
|  | for (int i = 0; i < tab_depth; i++) | 
|  | printk("\t"); | 
|  | printk("---- %s()\n", func); | 
|  | spin_unlock_irqsave(&lock); | 
|  | } |