-#ifdef __DEPUTY__
-#pragma nodeputy
-#endif
+/* 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 <arch/stab.h>
+#include <kdebug.h>
+#include <kmalloc.h>
#include <string.h>
#include <assert.h>
-#include <kdebug.h>
-#include <pmap.h>
-#include <env.h>
-
-#include <ros/memlayout.h>
-
-extern const stab_t __STAB_BEGIN__[]; // Beginning of stabs table
-extern const stab_t __STAB_END__[]; // End of stabs table
-extern const char __STABSTR_BEGIN__[]; // Beginning of string table
-extern const char __STABSTR_END__[]; // End of string table
-
-typedef struct UserStabData {
- const stab_t *stabs;
- const stab_t *stab_end;
- const char *stabstr;
- const char *stabstr_end;
-} user_stab_data_t;
-
-
-// stab_binsearch(stabs, region_left, region_right, type, addr)
-//
-// Some stab types are arranged in increasing order by instruction
-// address. For example, N_FUN stabs (stab entries with n_type ==
-// N_FUN), which mark functions, and N_SO stabs, which mark source files.
-//
-// Given an instruction address, this function finds the single stab
-// entry of type 'type' that contains that address.
-//
-// The search takes place within the range [*region_left, *region_right].
-// Thus, to search an entire set of N stabs, you might do:
-//
-// left = 0;
-// right = N - 1; /* rightmost stab */
-// stab_binsearch(stabs, &left, &right, type, addr);
-//
-// The search modifies *region_left and *region_right to bracket the
-// 'addr'. *region_left points to the matching stab that contains
-// 'addr', and *region_right points just before the next stab. If
-// *region_left > *region_right, then 'addr' is not contained in any
-// matching stab.
-//
-// For example, given these N_SO stabs:
-// Index Type Address
-// 0 SO f0100000
-// 13 SO f0100040
-// 117 SO f0100176
-// 118 SO f0100178
-// 555 SO f0100652
-// 556 SO f0100654
-// 657 SO f0100849
-// this code:
-// left = 0, right = 657;
-// stab_binsearch(stabs, &left, &right, N_SO, 0xf0100184);
-// will exit setting left = 118, right = 554.
-//
-static void
-stab_binsearch(const stab_t *stabs, int *region_left, int *region_right,
- int type, uintptr_t addr)
-{
- int l = *region_left, r = *region_right, any_matches = 0;
-
- while (l <= r) {
- int true_m = (l + r) / 2, m = true_m;
-
- // search for earliest stab with right type
- while (m >= l && stabs[m].n_type != type)
- m--;
- if (m < l) { // no match in [l, m]
- l = true_m + 1;
- continue;
+#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;
+ strlcpy(buf, found->name, name_len);
+ return buf;
+}
- // actual binary search
- any_matches = 1;
- if (stabs[m].n_value < addr) {
- *region_left = m;
- l = true_m + 1;
- } else if (stabs[m].n_value > addr) {
- *region_right = m - 1;
- r = m - 1;
+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 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 (is_ktask(pcpui->cur_kthread)) {
+ printk("%10s:", pcpui->cur_kthread->name);
} else {
- // exact match for 'addr', but continue loop to find
- // *region_right
- *region_left = m;
- l = m;
- addr++;
+ 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++;
+}
- if (!any_matches)
- *region_right = *region_left - 1;
- else {
- // find rightmost region containing 'addr'
- for (l = *region_right;
- l > *region_left && stabs[l].n_type != type;
- l--)
- /* do nothing */;
- *region_left = l;
+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);
+}
-// debuginfo_eip(addr, info)
-//
-// Fill in the 'info' structure with information about the specified
-// instruction address, 'addr'. Returns 0 if information was found, and
-// negative if not. But even if it returns negative it has stored some
-// information into '*info'.
-//
-int
-debuginfo_eip(uintptr_t addr, eipdebuginfo_t *info)
-{
- const stab_t *stabs, *stab_end;
- const char *stabstr, *stabstr_end;
- int lfile, rfile, lfun, rfun, lline, rline;
-
- // Initialize *info
- info->eip_file = "<unknown>";
- info->eip_line = 0;
- info->eip_fn_name = "<unknown>";
- info->eip_fn_namelen = 9;
- info->eip_fn_addr = addr;
- info->eip_fn_narg = 0;
-
- // Find the relevant set of stabs
- if (addr >= ULIM) {
- stabs = __STAB_BEGIN__;
- stab_end = __STAB_END__;
- stabstr = __STABSTR_BEGIN__;
- stabstr_end = __STABSTR_END__;
- } else {
- // The user-application linker script, user/user.ld,
- // puts information about the application's stabs (equivalent
- // to __STAB_BEGIN__, __STAB_END__, __STABSTR_BEGIN__, and
- // __STABSTR_END__) in a structure located at virtual address
- // USTABDATA.
- const user_stab_data_t *usd = (const user_stab_data_t *) USTABDATA;
-
- // Make sure this memory is valid.
- // Return -1 if it is not. Hint: Call user_mem_check.
- // LAB 3: Your code here.
-
- stabs = usd->stabs;
- stab_end = usd->stab_end;
- stabstr = usd->stabstr;
- stabstr_end = usd->stabstr_end;
-
- // Make sure the STABS and string table memory is valid.
- // LAB 3: Your code here.
+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);
+}
- // String table validity checks
- if (stabstr_end <= stabstr || stabstr_end[-1] != 0)
- return -1;
-
- // Now we find the right stabs that define the function containing
- // 'eip'. First, we find the basic source file containing 'eip'.
- // Then, we look in that source file for the function. Then we look
- // for the line number.
-
- // Search the entire set of stabs for the source file (type N_SO).
- lfile = 0;
- rfile = (stab_end - stabs) - 1;
- stab_binsearch(stabs, &lfile, &rfile, N_SO, addr);
- if (lfile == 0)
- return -1;
-
- // Search within that file's stabs for the function definition
- // (N_FUN).
- lfun = lfile;
- rfun = rfile;
- stab_binsearch(stabs, &lfun, &rfun, N_FUN, addr);
-
- if (lfun <= rfun) {
- // stabs[lfun] points to the function name
- // in the string table, but check bounds just in case.
- if (stabs[lfun].n_strx < stabstr_end - stabstr)
- info->eip_fn_name = stabstr + stabs[lfun].n_strx;
- info->eip_fn_addr = stabs[lfun].n_value;
- addr -= info->eip_fn_addr;
- // Search within the function definition for the line number.
- lline = lfun;
- rline = rfun;
- } else {
- // Couldn't find function stab! Maybe we're in an assembly
- // file. Search the whole file for the line number.
- info->eip_fn_addr = addr;
- lline = lfile;
- rline = rfile;
+void print_backtrace_list(uintptr_t *pcs, size_t nr_pcs,
+ void (*pfunc)(void *, const char *), void *opaque)
+{
+ char *func_name;
+ char bt_line[128];
+
+ for (size_t i = 0; i < nr_pcs; i++) {
+ func_name = get_fn_name(pcs[i]);
+ snprintf(bt_line, sizeof(bt_line), "#%02d [<%p>] in %s\n", i + 1,
+ pcs[i], func_name);
+ pfunc(opaque, bt_line);
+ kfree(func_name);
+ }
+}
+
+static void printk_func(void *opaque, const char *str)
+{
+ printk("%s", str);
+}
+
+void backtrace(void)
+{
+ printk("Stack Backtrace on Core %d:\n", core_id());
+ gen_backtrace(&printk_func, NULL);
+}
+
+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);
+
+ printk("\nBacktrace of kernel context on Core %d:\n", core_id());
+ print_backtrace_list(pcs, nr_pcs, &printk_func, NULL);
+}
+
+/* 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];
+ size_t nr_pcs = backtrace_user_list(eip, ebp, pcs, MAX_BT_DEPTH);
+
+ 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]);
}
- // Ignore stuff after the colon.
- info->eip_fn_namelen = strfind(info->eip_fn_name, ':') - info->eip_fn_name;
-
- // Search within [lline, rline] for the line number stab.
- // If found, set info->eip_line to the right line number.
- // If not found, return -1.
- //
- // Hint:
- // There's a particular stabs type used for line numbers.
- // Look at the STABS documentation and <inc/stab.h> to find
- // which one.
- // Your code here.
-
- stab_binsearch(stabs, &lline, &rline, N_SLINE, addr);
- if (lline <= rline)
- // stabs[lline] points to the line number
- info->eip_line = stabs[lline].n_value;
+}
+
+void backtrace_hwtf(struct hw_trapframe *hw_tf)
+{
+ struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+
+ pcpui->__lock_checking_enabled--;
+ if (in_kernel(hw_tf))
+ backtrace_frame(get_hwtf_pc(hw_tf), get_hwtf_fp(hw_tf));
else
- return -1;
-
- // Search backwards from the line number for the relevant filename
- // stab.
- // We can't just use the "lfile" stab because inlined functions
- // can interpolate code from a different file!
- // Such included source files use the N_SOL stab type.
- while (lline >= lfile
- && stabs[lline].n_type != N_SOL
- && (stabs[lline].n_type != N_SO || !stabs[lline].n_value))
- lline--;
- if (lline >= lfile && stabs[lline].n_strx < stabstr_end - stabstr)
- info->eip_file = stabstr + stabs[lline].n_strx;
-
- // Set eip_fn_narg to the number of arguments taken by the function,
- // or 0 if there was no containing function.
- // Your code here.
- info->eip_fn_narg = 0;
- if (lfun <= rfun) {
- lfun++;
- while (stabs[lfun++].n_type == N_PSYM)
- info->eip_fn_narg++;
+ backtrace_user_frame(get_hwtf_pc(hw_tf), get_hwtf_fp(hw_tf));
+ pcpui->__lock_checking_enabled++;
+}
+
+void backtrace_user_ctx(struct proc *p, struct user_context *ctx)
+{
+ struct per_cpu_info *pcpui = &per_cpu_info[core_id()];
+ uintptr_t st_save;
+
+ if (!ctx) {
+ printk("Null user context!\n");
+ return;
}
-
- return 0;
+ st_save = switch_to(p);
+ pcpui->__lock_checking_enabled--;
+ backtrace_user_frame(get_user_ctx_pc(ctx), get_user_ctx_fp(ctx));
+ pcpui->__lock_checking_enabled++;
+ switch_back(p, st_save);
+}
+
+static spinlock_t __px_lock = SPINLOCK_INITIALIZER_IRQSAVE;
+void px_lock(void)
+{
+ if (printx_on)
+ spin_lock_irqsave(&__px_lock);
+}
+void px_unlock(void)
+{
+ if (printx_on)
+ spin_unlock_irqsave(&__px_lock);
}