1 /* $NetBSD: db_trace.c,v 1.8 2003/01/17 22:28:48 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2000, 2001 Ben Harris 5 * Copyright (c) 1996 Scott K. Stevens 6 * 7 * Mach Operating System 8 * Copyright (c) 1991,1990 Carnegie Mellon University 9 * All Rights Reserved. 10 * 11 * Permission to use, copy, modify and distribute this software and its 12 * documentation is hereby granted, provided that both the copyright 13 * notice and this permission notice appear in all copies of the 14 * software, derivative works or modified versions, and any portions 15 * thereof, and that both notices appear in supporting documentation. 16 * 17 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 18 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 19 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 20 * 21 * Carnegie Mellon requests users of this software to return to 22 * 23 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 24 * School of Computer Science 25 * Carnegie Mellon University 26 * Pittsburgh PA 15213-3890 27 * 28 * any improvements or extensions that they make and grant Carnegie the 29 * rights to redistribute these changes. 30 */ 31 32 #include <sys/cdefs.h> 33 __FBSDID("$FreeBSD$"); 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 37 38 #include <sys/proc.h> 39 #include <sys/kdb.h> 40 #include <sys/stack.h> 41 #include <machine/armreg.h> 42 #include <machine/asm.h> 43 #include <machine/cpufunc.h> 44 #include <machine/db_machdep.h> 45 #include <machine/pcb.h> 46 #include <machine/vmparam.h> 47 #include <ddb/ddb.h> 48 #include <ddb/db_access.h> 49 #include <ddb/db_sym.h> 50 #include <ddb/db_output.h> 51 52 #define INKERNEL(va) (((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS) 53 54 /* 55 * APCS stack frames are awkward beasts, so I don't think even trying to use 56 * a structure to represent them is a good idea. 57 * 58 * Here's the diagram from the APCS. Increasing address is _up_ the page. 59 * 60 * save code pointer [fp] <- fp points to here 61 * return link value [fp, #-4] 62 * return sp value [fp, #-8] 63 * return fp value [fp, #-12] 64 * [saved v7 value] 65 * [saved v6 value] 66 * [saved v5 value] 67 * [saved v4 value] 68 * [saved v3 value] 69 * [saved v2 value] 70 * [saved v1 value] 71 * [saved a4 value] 72 * [saved a3 value] 73 * [saved a2 value] 74 * [saved a1 value] 75 * 76 * The save code pointer points twelve bytes beyond the start of the 77 * code sequence (usually a single STM) that created the stack frame. 78 * We have to disassemble it if we want to know which of the optional 79 * fields are actually present. 80 */ 81 82 #define FR_SCP (0) 83 #define FR_RLV (-1) 84 #define FR_RSP (-2) 85 #define FR_RFP (-3) 86 87 static void 88 db_stack_trace_cmd(db_expr_t addr, db_expr_t count) 89 { 90 u_int32_t *frame, *lastframe; 91 c_db_sym_t sym; 92 const char *name; 93 db_expr_t value; 94 db_expr_t offset; 95 boolean_t kernel_only = TRUE; 96 int scp_offset, quit; 97 98 frame = (u_int32_t *)addr; 99 lastframe = NULL; 100 scp_offset = -(get_pc_str_offset() >> 2); 101 102 quit = 0; 103 db_setup_paging(db_simple_pager, &quit, db_lines_per_page); 104 while (count-- && frame != NULL && !quit) { 105 db_addr_t scp; 106 u_int32_t savecode; 107 int r; 108 u_int32_t *rp; 109 const char *sep; 110 111 /* 112 * In theory, the SCP isn't guaranteed to be in the function 113 * that generated the stack frame. We hope for the best. 114 */ 115 scp = frame[FR_SCP]; 116 117 sym = db_search_symbol(scp, DB_STGY_ANY, &offset); 118 if (sym == C_DB_SYM_NULL) { 119 value = 0; 120 name = "(null)"; 121 } else 122 db_symbol_values(sym, &name, &value); 123 db_printf("%s() at ", name); 124 db_printsym(scp, DB_STGY_PROC); 125 db_printf("\n"); 126 #ifdef __PROG26 127 db_printf("scp=0x%08x rlv=0x%08x (", scp, frame[FR_RLV] & R15_PC); 128 db_printsym(frame[FR_RLV] & R15_PC, DB_STGY_PROC); 129 db_printf(")\n"); 130 #else 131 db_printf("scp=0x%08x rlv=0x%08x (", scp, frame[FR_RLV]); 132 db_printsym(frame[FR_RLV], DB_STGY_PROC); 133 db_printf(")\n"); 134 #endif 135 db_printf("\trsp=0x%08x rfp=0x%08x", frame[FR_RSP], frame[FR_RFP]); 136 137 savecode = ((u_int32_t *)scp)[scp_offset]; 138 if ((savecode & 0x0e100000) == 0x08000000) { 139 /* Looks like an STM */ 140 rp = frame - 4; 141 sep = "\n\t"; 142 for (r = 10; r >= 0; r--) { 143 if (savecode & (1 << r)) { 144 db_printf("%sr%d=0x%08x", 145 sep, r, *rp--); 146 sep = (frame - rp) % 4 == 2 ? 147 "\n\t" : " "; 148 } 149 } 150 } 151 152 db_printf("\n"); 153 154 /* 155 * Switch to next frame up 156 */ 157 if (frame[FR_RFP] == 0) 158 break; /* Top of stack */ 159 160 lastframe = frame; 161 frame = (u_int32_t *)(frame[FR_RFP]); 162 163 if (INKERNEL((int)frame)) { 164 /* staying in kernel */ 165 if (frame <= lastframe) { 166 db_printf("Bad frame pointer: %p\n", frame); 167 break; 168 } 169 } else if (INKERNEL((int)lastframe)) { 170 /* switch from user to kernel */ 171 if (kernel_only) 172 break; /* kernel stack only */ 173 } else { 174 /* in user */ 175 if (frame <= lastframe) { 176 db_printf("Bad user frame pointer: %p\n", 177 frame); 178 break; 179 } 180 } 181 } 182 } 183 184 /* XXX stubs */ 185 void 186 db_md_list_watchpoints() 187 { 188 } 189 190 int 191 db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) 192 { 193 return (0); 194 } 195 196 int 197 db_md_set_watchpoint(db_expr_t addr, db_expr_t size) 198 { 199 return (0); 200 } 201 202 int 203 db_trace_thread(struct thread *thr, int count) 204 { 205 uint32_t addr; 206 207 if (thr == curthread) 208 addr = (uint32_t)__builtin_frame_address(0); 209 else 210 addr = thr->td_pcb->un_32.pcb32_r11; 211 db_stack_trace_cmd(addr, -1); 212 return (0); 213 } 214 215 void 216 db_trace_self(void) 217 { 218 db_trace_thread(curthread, -1); 219 } 220 221 void 222 stack_save(struct stack *st) 223 { 224 vm_offset_t callpc; 225 u_int32_t *frame; 226 227 stack_zero(st); 228 frame = (u_int32_t *)__builtin_frame_address(0); 229 while (1) { 230 if (!INKERNEL(frame)) 231 break; 232 callpc = frame[FR_SCP]; 233 if (stack_put(st, callpc) == -1) 234 break; 235 frame = (u_int32_t *)(frame[FR_RFP]); 236 } 237 } 238