| /* 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 */ | 
 | 	"limborexmit", | 
 | 	"rangecompare", | 
 | 	"walkadd", | 
 | 	"bxe_read_dmae", | 
 | 	"bxe_write_dmae", | 
 | 	"bxe_prep_dmae_with_comp", | 
 | 	"bxe_dmae_opcode", | 
 | 	"bxe_dmae_opcode_add_comp", | 
 | 	"bxe_issue_dmae_with_comp", | 
 | 	"bxe_post_dmae", | 
 | 	"ecore_write_dmae_phys_len", | 
 | 	"bxe_write_dmae_phys_len", | 
 | 	"bxe_dma_alloc", | 
 | }; | 
 |  | 
 | 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 spinlock_t lock = SPINLOCK_INITIALIZER_IRQSAVE; | 
 |  | 
 | 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 (pcpui->cur_kthread->is_ktask) { | 
 | 			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; | 
 | 	spin_lock_irqsave(&lock); | 
 | 	__print_hdr(); | 
 | 	printk("%s%s() in %s\n", ourtabs, func, file); | 
 | 	spin_unlock_irqsave(&lock); | 
 | 	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)]; | 
 | 	spin_lock_irqsave(&lock); | 
 | 	__print_hdr(); | 
 | 	printk("%s---- %s()\n", ourtabs, func); | 
 | 	spin_unlock_irqsave(&lock); | 
 | } | 
 |  | 
 | 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->vm_file) { | 
 | 		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, file_name(vmr->vm_file), | 
 | 	       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); | 
 | } |