1 /* 2 * Mach Operating System 3 * Copyright (c) 1991,1990 Carnegie Mellon University 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify and distribute this software and its 7 * documentation is hereby granted, provided that both the copyright 8 * notice and this permission notice appear in all copies of the 9 * software, derivative works or modified versions, and any portions 10 * thereof, and that both notices appear in supporting documentation. 11 * 12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 15 * 16 * Carnegie Mellon requests users of this software to return to 17 * 18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 19 * School of Computer Science 20 * Carnegie Mellon University 21 * Pittsburgh PA 15213-3890 22 * 23 * any improvements or extensions that they make and grant Carnegie the 24 * rights to redistribute these changes. 25 */ 26 /* 27 * Author: David B. Golub, Carnegie Mellon University 28 * Date: 7/90 29 */ 30 31 /* 32 * Commands to run process. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include <sys/param.h> 39 #include <sys/kdb.h> 40 41 #include <machine/kdb.h> 42 #include <machine/pcb.h> 43 44 #include <vm/vm.h> 45 46 #include <ddb/ddb.h> 47 #include <ddb/db_break.h> 48 #include <ddb/db_access.h> 49 50 static int db_run_mode; 51 #define STEP_NONE 0 52 #define STEP_ONCE 1 53 #define STEP_RETURN 2 54 #define STEP_CALLT 3 55 #define STEP_CONTINUE 4 56 #define STEP_INVISIBLE 5 57 #define STEP_COUNT 6 58 59 static boolean_t db_sstep_print; 60 static int db_loop_count; 61 static int db_call_depth; 62 63 int db_inst_count; 64 int db_load_count; 65 int db_store_count; 66 67 #ifndef db_set_single_step 68 void db_set_single_step(void); 69 #endif 70 #ifndef db_clear_single_step 71 void db_clear_single_step(void); 72 #endif 73 74 boolean_t 75 db_stop_at_pc(is_breakpoint) 76 boolean_t *is_breakpoint; 77 { 78 register db_addr_t pc; 79 register db_breakpoint_t bkpt; 80 81 db_clear_single_step(); 82 db_clear_breakpoints(); 83 db_clear_watchpoints(); 84 pc = PC_REGS(); 85 86 #ifdef FIXUP_PC_AFTER_BREAK 87 if (*is_breakpoint) { 88 /* 89 * Breakpoint trap. Fix up the PC if the 90 * machine requires it. 91 */ 92 FIXUP_PC_AFTER_BREAK 93 pc = PC_REGS(); 94 } 95 #endif 96 97 /* 98 * Now check for a breakpoint at this address. 99 */ 100 bkpt = db_find_breakpoint_here(pc); 101 if (bkpt) { 102 if (--bkpt->count == 0) { 103 bkpt->count = bkpt->init_count; 104 *is_breakpoint = TRUE; 105 return (TRUE); /* stop here */ 106 } 107 } else if (*is_breakpoint) { 108 #ifdef BKPT_SKIP 109 BKPT_SKIP; 110 #endif 111 } 112 113 *is_breakpoint = FALSE; 114 115 if (db_run_mode == STEP_INVISIBLE) { 116 db_run_mode = STEP_CONTINUE; 117 return (FALSE); /* continue */ 118 } 119 if (db_run_mode == STEP_COUNT) { 120 return (FALSE); /* continue */ 121 } 122 if (db_run_mode == STEP_ONCE) { 123 if (--db_loop_count > 0) { 124 if (db_sstep_print) { 125 db_printf("\t\t"); 126 db_print_loc_and_inst(pc); 127 db_printf("\n"); 128 } 129 return (FALSE); /* continue */ 130 } 131 } 132 if (db_run_mode == STEP_RETURN) { 133 /* continue until matching return */ 134 db_expr_t ins; 135 136 ins = db_get_value(pc, sizeof(int), FALSE); 137 if (!inst_trap_return(ins) && 138 (!inst_return(ins) || --db_call_depth != 0)) { 139 if (db_sstep_print) { 140 if (inst_call(ins) || inst_return(ins)) { 141 register int i; 142 143 db_printf("[after %6d] ", db_inst_count); 144 for (i = db_call_depth; --i > 0; ) 145 db_printf(" "); 146 db_print_loc_and_inst(pc); 147 db_printf("\n"); 148 } 149 } 150 if (inst_call(ins)) 151 db_call_depth++; 152 return (FALSE); /* continue */ 153 } 154 } 155 if (db_run_mode == STEP_CALLT) { 156 /* continue until call or return */ 157 db_expr_t ins; 158 159 ins = db_get_value(pc, sizeof(int), FALSE); 160 if (!inst_call(ins) && 161 !inst_return(ins) && 162 !inst_trap_return(ins)) { 163 return (FALSE); /* continue */ 164 } 165 } 166 db_run_mode = STEP_NONE; 167 return (TRUE); 168 } 169 170 void 171 db_restart_at_pc(watchpt) 172 boolean_t watchpt; 173 { 174 register db_addr_t pc = PC_REGS(); 175 176 if ((db_run_mode == STEP_COUNT) || 177 (db_run_mode == STEP_RETURN) || 178 (db_run_mode == STEP_CALLT)) { 179 db_expr_t ins; 180 181 /* 182 * We are about to execute this instruction, 183 * so count it now. 184 */ 185 186 ins = db_get_value(pc, sizeof(int), FALSE); 187 db_inst_count++; 188 db_load_count += inst_load(ins); 189 db_store_count += inst_store(ins); 190 #ifdef SOFTWARE_SSTEP 191 /* XXX works on mips, but... */ 192 if (inst_branch(ins) || inst_call(ins)) { 193 ins = db_get_value(next_instr_address(pc,1), 194 sizeof(int), FALSE); 195 db_inst_count++; 196 db_load_count += inst_load(ins); 197 db_store_count += inst_store(ins); 198 } 199 #endif /* SOFTWARE_SSTEP */ 200 } 201 202 if (db_run_mode == STEP_CONTINUE) { 203 if (watchpt || db_find_breakpoint_here(pc)) { 204 /* 205 * Step over breakpoint/watchpoint. 206 */ 207 db_run_mode = STEP_INVISIBLE; 208 db_set_single_step(); 209 } else { 210 db_set_breakpoints(); 211 db_set_watchpoints(); 212 } 213 } else { 214 db_set_single_step(); 215 } 216 } 217 218 #ifdef SOFTWARE_SSTEP 219 /* 220 * Software implementation of single-stepping. 221 * If your machine does not have a trace mode 222 * similar to the vax or sun ones you can use 223 * this implementation, done for the mips. 224 * Just define the above conditional and provide 225 * the functions/macros defined below. 226 * 227 * extern boolean_t 228 * inst_branch(), returns true if the instruction might branch 229 * extern unsigned 230 * branch_taken(), return the address the instruction might 231 * branch to 232 * db_getreg_val(); return the value of a user register, 233 * as indicated in the hardware instruction 234 * encoding, e.g. 8 for r8 235 * 236 * next_instr_address(pc,bd) returns the address of the first 237 * instruction following the one at "pc", 238 * which is either in the taken path of 239 * the branch (bd==1) or not. This is 240 * for machines (mips) with branch delays. 241 * 242 * A single-step may involve at most 2 breakpoints - 243 * one for branch-not-taken and one for branch taken. 244 * If one of these addresses does not already have a breakpoint, 245 * we allocate a breakpoint and save it here. 246 * These breakpoints are deleted on return. 247 */ 248 db_breakpoint_t db_not_taken_bkpt = 0; 249 db_breakpoint_t db_taken_bkpt = 0; 250 251 void 252 db_set_single_step(void) 253 { 254 db_addr_t pc = PC_REGS(), brpc; 255 unsigned inst; 256 257 /* 258 * User was stopped at pc, e.g. the instruction 259 * at pc was not executed. 260 */ 261 inst = db_get_value(pc, sizeof(int), FALSE); 262 if (inst_branch(inst) || inst_call(inst)) { 263 brpc = branch_taken(inst, pc); 264 if (brpc != pc) { /* self-branches are hopeless */ 265 db_taken_bkpt = db_set_temp_breakpoint(brpc); 266 } 267 pc = next_instr_address(pc, 1); 268 } 269 pc = next_instr_address(pc, 0); 270 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 271 } 272 273 void 274 db_clear_single_step(void) 275 { 276 277 if (db_not_taken_bkpt != 0) { 278 db_delete_temp_breakpoint(db_not_taken_bkpt); 279 db_not_taken_bkpt = 0; 280 } 281 if (db_taken_bkpt != 0) { 282 db_delete_temp_breakpoint(db_taken_bkpt); 283 db_taken_bkpt = 0; 284 } 285 } 286 287 #endif /* SOFTWARE_SSTEP */ 288 289 extern int db_cmd_loop_done; 290 291 /* single-step */ 292 /*ARGSUSED*/ 293 void 294 db_single_step_cmd(addr, have_addr, count, modif) 295 db_expr_t addr; 296 boolean_t have_addr; 297 db_expr_t count; 298 char * modif; 299 { 300 boolean_t print = FALSE; 301 302 if (count == -1) 303 count = 1; 304 305 if (modif[0] == 'p') 306 print = TRUE; 307 308 db_run_mode = STEP_ONCE; 309 db_loop_count = count; 310 db_sstep_print = print; 311 db_inst_count = 0; 312 db_load_count = 0; 313 db_store_count = 0; 314 315 db_cmd_loop_done = 1; 316 } 317 318 /* trace and print until call/return */ 319 /*ARGSUSED*/ 320 void 321 db_trace_until_call_cmd(addr, have_addr, count, modif) 322 db_expr_t addr; 323 boolean_t have_addr; 324 db_expr_t count; 325 char * modif; 326 { 327 boolean_t print = FALSE; 328 329 if (modif[0] == 'p') 330 print = TRUE; 331 332 db_run_mode = STEP_CALLT; 333 db_sstep_print = print; 334 db_inst_count = 0; 335 db_load_count = 0; 336 db_store_count = 0; 337 338 db_cmd_loop_done = 1; 339 } 340 341 /*ARGSUSED*/ 342 void 343 db_trace_until_matching_cmd(addr, have_addr, count, modif) 344 db_expr_t addr; 345 boolean_t have_addr; 346 db_expr_t count; 347 char * modif; 348 { 349 boolean_t print = FALSE; 350 351 if (modif[0] == 'p') 352 print = TRUE; 353 354 db_run_mode = STEP_RETURN; 355 db_call_depth = 1; 356 db_sstep_print = print; 357 db_inst_count = 0; 358 db_load_count = 0; 359 db_store_count = 0; 360 361 db_cmd_loop_done = 1; 362 } 363 364 /* continue */ 365 /*ARGSUSED*/ 366 void 367 db_continue_cmd(addr, have_addr, count, modif) 368 db_expr_t addr; 369 boolean_t have_addr; 370 db_expr_t count; 371 char * modif; 372 { 373 if (modif[0] == 'c') 374 db_run_mode = STEP_COUNT; 375 else 376 db_run_mode = STEP_CONTINUE; 377 db_inst_count = 0; 378 db_load_count = 0; 379 db_store_count = 0; 380 381 db_cmd_loop_done = 1; 382 } 383