/* $NetBSD: db_trace.c,v 1.8 2003/01/17 22:28:48 thorpej Exp $ */ /* * Copyright (c) 2000, 2001 Ben Harris * Copyright (c) 1996 Scott K. Stevens * * Mach Operating System * Copyright (c) 1991,1990 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include __FBSDID("$FreeBSD: head/sys/arm/arm/db_trace.c 135529 2004-09-20 19:05:32Z jhb $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define INKERNEL(va) (((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS) int db_md_set_watchpoint(db_expr_t addr, db_expr_t size); int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size); void db_md_list_watchpoints(void); /* * APCS stack frames are awkward beasts, so I don't think even trying to use * a structure to represent them is a good idea. * * Here's the diagram from the APCS. Increasing address is _up_ the page. * * save code pointer [fp] <- fp points to here * return link value [fp, #-4] * return sp value [fp, #-8] * return fp value [fp, #-12] * [saved v7 value] * [saved v6 value] * [saved v5 value] * [saved v4 value] * [saved v3 value] * [saved v2 value] * [saved v1 value] * [saved a4 value] * [saved a3 value] * [saved a2 value] * [saved a1 value] * * The save code pointer points twelve bytes beyond the start of the * code sequence (usually a single STM) that created the stack frame. * We have to disassemble it if we want to know which of the optional * fields are actually present. */ #define FR_SCP (0) #define FR_RLV (-1) #define FR_RSP (-2) #define FR_RFP (-3) static void db_stack_trace_cmd(addr, have_addr, count, modif) db_expr_t addr; int have_addr; db_expr_t count; char *modif; { u_int32_t *frame, *lastframe; c_db_sym_t sym; db_expr_t pc; char c, *cp = modif; const char *name; db_expr_t value; db_expr_t offset; boolean_t kernel_only = TRUE; boolean_t trace_thread = FALSE; int scp_offset, quit; if (kdb_frame == NULL && !have_addr) return; while ((c = *cp++) != 0) { if (c == 'u') kernel_only = FALSE; if (c == 't') trace_thread = TRUE; } if (!have_addr) frame = (u_int32_t *)(kdb_frame->tf_r11); else { if (trace_thread) { struct proc *p; struct thread *td; pid_t pid = (pid_t)addr; LIST_FOREACH(p, &allproc, p_list) { if (p->p_pid == pid) break; } if (p == NULL) { db_printf("not found\n"); return; } if (!(p->p_sflag & PS_INMEM)) { db_printf("swapped out\n"); return; } td = FIRST_THREAD_IN_PROC(p); frame = (u_int32_t *)(td->td_pcb->un_32.pcb32_r11); db_printf("at %p\n", frame); } else frame = (u_int32_t *)(addr); } lastframe = NULL; scp_offset = -(get_pc_str_offset() >> 2); quit = 0; db_setup_paging(db_simple_pager, &quit, DB_LINES_PER_PAGE); while (count-- && frame != NULL && !quit) { db_addr_t scp; u_int32_t savecode; int r; u_int32_t *rp; const char *sep; /* * In theory, the SCP isn't guaranteed to be in the function * that generated the stack frame. We hope for the best. */ scp = frame[FR_SCP]; db_printsym(scp, DB_STGY_PROC); db_printf("\n\t"); pc = kdb_frame->tf_pc; sym = db_search_symbol(pc, DB_STGY_ANY, &offset); if (sym == C_DB_SYM_NULL) { value = 0; name = "(null)"; } else db_symbol_values(sym, &name, &value); db_printf("%s() at ", name); db_printsym(pc, DB_STGY_PROC); db_printf("\n"); #ifdef __PROG26 db_printf("scp=0x%08x rlv=0x%08x (", scp, frame[FR_RLV] & R15_PC); db_printsym(frame[FR_RLV] & R15_PC, DB_STGY_PROC); db_printf(")\n"); #else db_printf("scp=0x%08x rlv=0x%08x (", scp, frame[FR_RLV]); db_printsym(frame[FR_RLV], DB_STGY_PROC); db_printf(")\n"); #endif db_printf("\trsp=0x%08x rfp=0x%08x", frame[FR_RSP], frame[FR_RFP]); savecode = ((u_int32_t *)scp)[scp_offset]; if ((savecode & 0x0e100000) == 0x08000000) { /* Looks like an STM */ rp = frame - 4; sep = "\n\t"; for (r = 10; r >= 0; r--) { if (savecode & (1 << r)) { db_printf("%sr%d=0x%08x", sep, r, *rp--); sep = (frame - rp) % 4 == 2 ? "\n\t" : " "; } } } db_printf("\n"); /* * Switch to next frame up */ if (frame[FR_RFP] == 0) break; /* Top of stack */ lastframe = frame; frame = (u_int32_t *)(frame[FR_RFP]); if (INKERNEL((int)frame)) { /* staying in kernel */ if (frame <= lastframe) { db_printf("Bad frame pointer: %p\n", frame); break; } } else if (INKERNEL((int)lastframe)) { /* switch from user to kernel */ if (kernel_only) break; /* kernel stack only */ } else { /* in user */ if (frame <= lastframe) { db_printf("Bad user frame pointer: %p\n", frame); break; } } } } /* XXX stubs */ void db_md_list_watchpoints() { } int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) { return (0); } int db_md_set_watchpoint(db_expr_t addr, db_expr_t size) { return (0); } int db_trace_thread(struct thread *thr, int count) { uint32_t addr; if (thr == curthread) addr = (uint32_t)__builtin_frame_address(0); else addr = thr->td_pcb->un_32.pcb32_r11; db_stack_trace_cmd(addr, 1, -1, NULL); return (0); } void db_trace_self(void) { db_trace_thread(curthread, -1); }