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