1 /* $OpenBSD: db_run.c,v 1.22 2010/11/27 19:59:11 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 54 #ifndef KGDB 55 56 #include <ddb/db_lex.h> 57 #include <ddb/db_watch.h> 58 #include <ddb/db_output.h> 59 #include <ddb/db_sym.h> 60 #include <ddb/db_extern.h> 61 62 int db_run_mode; 63 #define STEP_NONE 0 64 #define STEP_ONCE 1 65 #define STEP_RETURN 2 66 #define STEP_CALLT 3 67 #define STEP_CONTINUE 4 68 #define STEP_INVISIBLE 5 69 #define STEP_COUNT 6 70 71 boolean_t db_sstep_print; 72 int db_loop_count; 73 int db_call_depth; 74 75 boolean_t 76 db_stop_at_pc(db_regs_t *regs, boolean_t *is_breakpoint) 77 { 78 db_addr_t pc, old_pc; 79 db_breakpoint_t bkpt; 80 81 db_clear_breakpoints(); 82 db_clear_watchpoints(); 83 old_pc = pc = PC_REGS(regs); 84 85 #ifdef FIXUP_PC_AFTER_BREAK 86 if (*is_breakpoint) { 87 /* 88 * Breakpoint trap. Fix up the PC if the 89 * machine requires it. 90 */ 91 FIXUP_PC_AFTER_BREAK(regs); 92 pc = PC_REGS(regs); 93 } 94 #endif 95 96 /* 97 * Now check for a breakpoint at this address. 98 */ 99 bkpt = db_find_breakpoint(pc); 100 if (bkpt) { 101 if (--bkpt->count == 0) { 102 db_clear_single_step(regs); 103 bkpt->count = bkpt->init_count; 104 *is_breakpoint = TRUE; 105 return (TRUE); /* stop here */ 106 } 107 } else if (*is_breakpoint 108 #ifdef SOFTWARE_SSTEP 109 && !((db_taken_bkpt && db_taken_bkpt->address == pc) || 110 (db_not_taken_bkpt && db_not_taken_bkpt->address == pc)) 111 #endif 112 ) { 113 #ifdef PC_ADVANCE 114 PC_ADVANCE(regs); 115 #else 116 # ifdef SET_PC_REGS 117 SET_PC_REGS(regs, old_pc); 118 # else 119 PC_REGS(regs) = old_pc; 120 # endif 121 #endif 122 } 123 db_clear_single_step(regs); 124 125 *is_breakpoint = FALSE; 126 127 if (db_run_mode == STEP_INVISIBLE) { 128 db_run_mode = STEP_CONTINUE; 129 return (FALSE); /* continue */ 130 } 131 if (db_run_mode == STEP_COUNT) { 132 return (FALSE); /* continue */ 133 } 134 if (db_run_mode == STEP_ONCE) { 135 if (--db_loop_count > 0) { 136 if (db_sstep_print) { 137 db_printf("\t\t"); 138 db_print_loc_and_inst(pc); 139 db_printf("\n"); 140 } 141 return (FALSE); /* continue */ 142 } 143 } 144 if (db_run_mode == STEP_RETURN) { 145 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 146 147 /* continue until matching return */ 148 149 if (!inst_trap_return(ins) && 150 (!inst_return(ins) || --db_call_depth != 0)) { 151 if (db_sstep_print) { 152 if (inst_call(ins) || inst_return(ins)) { 153 int i; 154 155 db_printf("[after %6d] ", db_inst_count); 156 for (i = db_call_depth; --i > 0; ) 157 db_printf(" "); 158 db_print_loc_and_inst(pc); 159 db_printf("\n"); 160 } 161 } 162 if (inst_call(ins)) 163 db_call_depth++; 164 return (FALSE); /* continue */ 165 } 166 } 167 if (db_run_mode == STEP_CALLT) { 168 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 169 170 /* continue until call or return */ 171 172 if (!inst_call(ins) && !inst_return(ins) && 173 !inst_trap_return(ins)) { 174 return (FALSE); /* continue */ 175 } 176 } 177 db_run_mode = STEP_NONE; 178 return (TRUE); 179 } 180 181 void 182 db_restart_at_pc(db_regs_t *regs, boolean_t watchpt) 183 { 184 db_addr_t pc = PC_REGS(regs); 185 186 if ((db_run_mode == STEP_COUNT) || (db_run_mode == STEP_RETURN) || 187 (db_run_mode == STEP_CALLT)) { 188 db_expr_t ins; 189 190 /* 191 * We are about to execute this instruction, 192 * so count it now. 193 */ 194 ins = db_get_value(pc, sizeof(int), FALSE); 195 db_inst_count++; 196 #ifdef SOFTWARE_SSTEP 197 /* XXX works on mips, but... */ 198 if (inst_branch(ins) || inst_call(ins)) { 199 ins = db_get_value(next_instr_address(pc, 1), 200 sizeof(int), FALSE); 201 db_inst_count++; 202 } 203 #endif /* SOFTWARE_SSTEP */ 204 } 205 206 if (db_run_mode == STEP_CONTINUE) { 207 if (watchpt || db_find_breakpoint(pc)) { 208 /* 209 * Step over breakpoint/watchpoint. 210 */ 211 db_run_mode = STEP_INVISIBLE; 212 db_set_single_step(regs); 213 } else { 214 db_set_breakpoints(); 215 db_set_watchpoints(); 216 } 217 } else { 218 db_set_single_step(regs); 219 } 220 } 221 222 void 223 db_single_step(db_regs_t *regs) 224 { 225 if (db_run_mode == STEP_CONTINUE) { 226 db_run_mode = STEP_INVISIBLE; 227 db_set_single_step(regs); 228 } 229 } 230 231 /* single-step */ 232 /*ARGSUSED*/ 233 void 234 db_single_step_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 235 { 236 boolean_t print = FALSE; 237 238 if (count == -1) 239 count = 1; 240 241 if (modif[0] == 'p') 242 print = TRUE; 243 244 db_run_mode = STEP_ONCE; 245 db_loop_count = count; 246 db_sstep_print = print; 247 db_inst_count = 0; 248 249 db_cmd_loop_done = 1; 250 } 251 252 /* trace and print until call/return */ 253 /*ARGSUSED*/ 254 void 255 db_trace_until_call_cmd(db_expr_t addr, int have_addr, db_expr_t count, 256 char *modif) 257 { 258 boolean_t print = FALSE; 259 260 if (modif[0] == 'p') 261 print = TRUE; 262 263 db_run_mode = STEP_CALLT; 264 db_sstep_print = print; 265 db_inst_count = 0; 266 267 db_cmd_loop_done = 1; 268 } 269 270 /*ARGSUSED*/ 271 void 272 db_trace_until_matching_cmd(db_expr_t addr, int have_addr, db_expr_t count, 273 char *modif) 274 { 275 boolean_t print = FALSE; 276 277 if (modif[0] == 'p') 278 print = TRUE; 279 280 db_run_mode = STEP_RETURN; 281 db_call_depth = 1; 282 db_sstep_print = print; 283 db_inst_count = 0; 284 285 db_cmd_loop_done = 1; 286 } 287 288 /* continue */ 289 /*ARGSUSED*/ 290 void 291 db_continue_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 292 { 293 if (modif[0] == 'c') 294 db_run_mode = STEP_COUNT; 295 else 296 db_run_mode = STEP_CONTINUE; 297 db_inst_count = 0; 298 299 db_cmd_loop_done = 1; 300 } 301 #endif /* NO KGDB */ 302 303 #ifdef SOFTWARE_SSTEP 304 /* 305 * Software implementation of single-stepping. 306 * If your machine does not have a trace mode 307 * similar to the vax or sun ones you can use 308 * this implementation, done for the mips. 309 * Just define the above conditional and provide 310 * the functions/macros defined below. 311 * 312 * extern boolean_t 313 * inst_branch(ins), returns true if the instruction might branch 314 * extern unsigned 315 * branch_taken(ins, pc, getreg_val, regs), 316 * return the address the instruction might 317 * branch to 318 * getreg_val(regs, reg), return the value of a user register, 319 * as indicated in the hardware instruction 320 * encoding, e.g. 8 for r8 321 * 322 * next_instr_address(pc, bd) returns the address of the first 323 * instruction following the one at "pc", 324 * which is either in the taken path of 325 * the branch (bd==1) or not. This is 326 * for machines (mips) with branch delays. 327 * 328 * A single-step may involve at most 2 breakpoints - 329 * one for branch-not-taken and one for branch taken. 330 * If one of these addresses does not already have a breakpoint, 331 * we allocate a breakpoint and save it here. 332 * These breakpoints are deleted on return. 333 */ 334 335 void 336 db_set_single_step(db_regs_t *regs) 337 { 338 db_addr_t pc = PC_REGS(regs); 339 #ifndef SOFTWARE_SSTEP_EMUL 340 db_addr_t brpc; 341 u_int inst; 342 343 /* 344 * User was stopped at pc, e.g. the instruction 345 * at pc was not executed. 346 */ 347 inst = db_get_value(pc, sizeof(int), FALSE); 348 if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) { 349 brpc = branch_taken(inst, pc, getreg_val, regs); 350 if (brpc != pc) { /* self-branches are hopeless */ 351 db_taken_bkpt = db_set_temp_breakpoint(brpc); 352 } 353 #if 0 354 /* XXX this seems like a true bug, no? */ 355 pc = next_instr_address(pc, 1); 356 #endif 357 } 358 #endif /*SOFTWARE_SSTEP_EMUL*/ 359 pc = next_instr_address(pc, 0); 360 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 361 } 362 363 void 364 db_clear_single_step(db_regs_t *regs) 365 { 366 if (db_taken_bkpt != 0) { 367 db_delete_temp_breakpoint(db_taken_bkpt); 368 db_taken_bkpt = 0; 369 } 370 if (db_not_taken_bkpt != 0) { 371 db_delete_temp_breakpoint(db_not_taken_bkpt); 372 db_not_taken_bkpt = 0; 373 } 374 } 375 376 #endif /* SOFTWARE_SSTEP */ 377