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