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