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 #include <sys/proc.h> 41 42 #include <machine/kdb.h> 43 #include <machine/pcb.h> 44 45 #include <vm/vm.h> 46 47 #include <ddb/ddb.h> 48 #include <ddb/db_break.h> 49 #include <ddb/db_access.h> 50 51 #define STEP_ONCE 1 52 #define STEP_RETURN 2 53 #define STEP_CALLT 3 54 #define STEP_CONTINUE 4 55 #define STEP_INVISIBLE 5 56 #define STEP_COUNT 6 57 static int db_run_mode = STEP_CONTINUE; 58 59 static bool db_sstep_multiple; 60 static bool db_sstep_print; 61 static int db_loop_count; 62 static int db_call_depth; 63 64 int db_inst_count; 65 int db_load_count; 66 int db_store_count; 67 68 #ifdef SOFTWARE_SSTEP 69 db_breakpoint_t db_not_taken_bkpt = 0; 70 db_breakpoint_t db_taken_bkpt = 0; 71 #endif 72 73 #ifndef db_set_single_step 74 void db_set_single_step(void); 75 #endif 76 #ifndef db_clear_single_step 77 void db_clear_single_step(void); 78 #endif 79 #ifndef db_pc_is_singlestep 80 static bool 81 db_pc_is_singlestep(db_addr_t pc) 82 { 83 #ifdef SOFTWARE_SSTEP 84 if ((db_not_taken_bkpt != 0 && pc == db_not_taken_bkpt->address) 85 || (db_taken_bkpt != 0 && pc == db_taken_bkpt->address)) 86 return (true); 87 #endif 88 return (false); 89 } 90 #endif 91 92 bool 93 db_stop_at_pc(int type, int code, bool *is_breakpoint, bool *is_watchpoint) 94 { 95 db_addr_t pc; 96 db_breakpoint_t bkpt; 97 98 *is_breakpoint = IS_BREAKPOINT_TRAP(type, code); 99 *is_watchpoint = IS_WATCHPOINT_TRAP(type, code); 100 pc = PC_REGS(); 101 if (db_pc_is_singlestep(pc)) 102 *is_breakpoint = false; 103 104 db_clear_single_step(); 105 db_clear_breakpoints(); 106 db_clear_watchpoints(); 107 108 #ifdef FIXUP_PC_AFTER_BREAK 109 if (*is_breakpoint) { 110 /* 111 * Breakpoint trap. Fix up the PC if the 112 * machine requires it. 113 */ 114 FIXUP_PC_AFTER_BREAK 115 pc = PC_REGS(); 116 } 117 #endif 118 119 /* 120 * Now check for a breakpoint at this address. 121 */ 122 bkpt = db_find_breakpoint_here(pc); 123 if (bkpt) { 124 if (--bkpt->count == 0) { 125 bkpt->count = bkpt->init_count; 126 *is_breakpoint = true; 127 return (true); /* stop here */ 128 } 129 return (false); /* continue the countdown */ 130 } else if (*is_breakpoint) { 131 #ifdef BKPT_SKIP 132 BKPT_SKIP; 133 #endif 134 } 135 136 *is_breakpoint = false; /* might be a breakpoint, but not ours */ 137 138 /* 139 * If not stepping, then silently ignore single-step traps 140 * (except for clearing the single-step-flag above). 141 * 142 * If stepping, then abort if the trap type is unexpected. 143 * Breakpoints owned by us are expected and were handled above. 144 * Single-steps are expected and are handled below. All others 145 * are unexpected. 146 * 147 * Only do either of these if the MD layer claims to classify 148 * single-step traps unambiguously (by defining IS_SSTEP_TRAP). 149 * Otherwise, fall through to the bad historical behaviour 150 * given by turning unexpected traps into expected traps: if not 151 * stepping, then expect only breakpoints and stop, and if 152 * stepping, then expect only single-steps and step. 153 */ 154 #ifdef IS_SSTEP_TRAP 155 if (db_run_mode == STEP_CONTINUE && IS_SSTEP_TRAP(type, code)) 156 return (false); 157 if (db_run_mode != STEP_CONTINUE && !IS_SSTEP_TRAP(type, code)) { 158 printf("Stepping aborted\n"); 159 return (true); 160 } 161 #endif 162 163 if (db_run_mode == STEP_INVISIBLE) { 164 db_run_mode = STEP_CONTINUE; 165 return (false); /* continue */ 166 } 167 if (db_run_mode == STEP_COUNT) { 168 return (false); /* continue */ 169 } 170 if (db_run_mode == STEP_ONCE) { 171 if (--db_loop_count > 0) { 172 if (db_sstep_print) { 173 db_printf("\t\t"); 174 db_print_loc_and_inst(pc); 175 } 176 return (false); /* continue */ 177 } 178 } 179 if (db_run_mode == STEP_RETURN) { 180 /* continue until matching return */ 181 db_expr_t ins; 182 183 ins = db_get_value(pc, sizeof(int), false); 184 if (!inst_trap_return(ins) && 185 (!inst_return(ins) || --db_call_depth != 0)) { 186 if (db_sstep_print) { 187 if (inst_call(ins) || inst_return(ins)) { 188 int i; 189 190 db_printf("[after %6d] ", db_inst_count); 191 for (i = db_call_depth; --i > 0; ) 192 db_printf(" "); 193 db_print_loc_and_inst(pc); 194 } 195 } 196 if (inst_call(ins)) 197 db_call_depth++; 198 return (false); /* continue */ 199 } 200 } 201 if (db_run_mode == STEP_CALLT) { 202 /* continue until call or return */ 203 db_expr_t ins; 204 205 ins = db_get_value(pc, sizeof(int), false); 206 if (!inst_call(ins) && 207 !inst_return(ins) && 208 !inst_trap_return(ins)) { 209 return (false); /* continue */ 210 } 211 } 212 return (true); 213 } 214 215 void 216 db_restart_at_pc(bool watchpt) 217 { 218 db_addr_t pc = PC_REGS(); 219 220 if ((db_run_mode == STEP_COUNT) || 221 ((db_run_mode == STEP_ONCE) && db_sstep_multiple) || 222 (db_run_mode == STEP_RETURN) || 223 (db_run_mode == STEP_CALLT)) { 224 /* 225 * We are about to execute this instruction, 226 * so count it now. 227 */ 228 #ifdef SOFTWARE_SSTEP 229 db_expr_t ins = 230 #endif 231 db_get_value(pc, sizeof(int), false); 232 db_inst_count++; 233 db_load_count += inst_load(ins); 234 db_store_count += inst_store(ins); 235 #ifdef SOFTWARE_SSTEP 236 /* XXX works on mips, but... */ 237 if (inst_branch(ins) || inst_call(ins)) { 238 ins = db_get_value(next_instr_address(pc,1), 239 sizeof(int), false); 240 db_inst_count++; 241 db_load_count += inst_load(ins); 242 db_store_count += inst_store(ins); 243 } 244 #endif /* SOFTWARE_SSTEP */ 245 } 246 247 if (db_run_mode == STEP_CONTINUE) { 248 if (watchpt || db_find_breakpoint_here(pc)) { 249 /* 250 * Step over breakpoint/watchpoint. 251 */ 252 db_run_mode = STEP_INVISIBLE; 253 db_set_single_step(); 254 } else { 255 db_set_breakpoints(); 256 db_set_watchpoints(); 257 } 258 } else { 259 db_set_single_step(); 260 } 261 } 262 263 #ifdef SOFTWARE_SSTEP 264 /* 265 * Software implementation of single-stepping. 266 * If your machine does not have a trace mode 267 * similar to the vax or sun ones you can use 268 * this implementation, done for the mips. 269 * Just define the above conditional and provide 270 * the functions/macros defined below. 271 * 272 * extern bool 273 * inst_branch(), returns true if the instruction might branch 274 * extern unsigned 275 * branch_taken(), return the address the instruction might 276 * branch to 277 * db_getreg_val(); return the value of a user register, 278 * as indicated in the hardware instruction 279 * encoding, e.g. 8 for r8 280 * 281 * next_instr_address(pc,bd) returns the address of the first 282 * instruction following the one at "pc", 283 * which is either in the taken path of 284 * the branch (bd==1) or not. This is 285 * for machines (mips) with branch delays. 286 * 287 * A single-step may involve at most 2 breakpoints - 288 * one for branch-not-taken and one for branch taken. 289 * If one of these addresses does not already have a breakpoint, 290 * we allocate a breakpoint and save it here. 291 * These breakpoints are deleted on return. 292 */ 293 294 void 295 db_set_single_step(void) 296 { 297 db_addr_t pc = PC_REGS(), brpc; 298 unsigned inst; 299 300 /* 301 * User was stopped at pc, e.g. the instruction 302 * at pc was not executed. 303 */ 304 inst = db_get_value(pc, sizeof(int), false); 305 if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) { 306 brpc = branch_taken(inst, pc); 307 if (brpc != pc) { /* self-branches are hopeless */ 308 db_taken_bkpt = db_set_temp_breakpoint(brpc); 309 } 310 pc = next_instr_address(pc, 1); 311 } 312 pc = next_instr_address(pc, 0); 313 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 314 } 315 316 void 317 db_clear_single_step(void) 318 { 319 320 if (db_not_taken_bkpt != 0) { 321 db_delete_temp_breakpoint(db_not_taken_bkpt); 322 db_not_taken_bkpt = 0; 323 } 324 if (db_taken_bkpt != 0) { 325 db_delete_temp_breakpoint(db_taken_bkpt); 326 db_taken_bkpt = 0; 327 } 328 } 329 330 #endif /* SOFTWARE_SSTEP */ 331 332 extern int db_cmd_loop_done; 333 334 /* single-step */ 335 /*ARGSUSED*/ 336 void 337 db_single_step_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 338 { 339 bool print = false; 340 341 if (count == -1) 342 count = 1; 343 344 if (modif[0] == 'p') 345 print = true; 346 347 db_run_mode = STEP_ONCE; 348 db_loop_count = count; 349 db_sstep_multiple = (count != 1); 350 db_sstep_print = print; 351 db_inst_count = 0; 352 db_load_count = 0; 353 db_store_count = 0; 354 355 db_cmd_loop_done = 1; 356 } 357 358 /* trace and print until call/return */ 359 /*ARGSUSED*/ 360 void 361 db_trace_until_call_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 362 char *modif) 363 { 364 bool print = false; 365 366 if (modif[0] == 'p') 367 print = true; 368 369 db_run_mode = STEP_CALLT; 370 db_sstep_print = print; 371 db_inst_count = 0; 372 db_load_count = 0; 373 db_store_count = 0; 374 375 db_cmd_loop_done = 1; 376 } 377 378 /*ARGSUSED*/ 379 void 380 db_trace_until_matching_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 381 char *modif) 382 { 383 bool print = false; 384 385 if (modif[0] == 'p') 386 print = true; 387 388 db_run_mode = STEP_RETURN; 389 db_call_depth = 1; 390 db_sstep_print = print; 391 db_inst_count = 0; 392 db_load_count = 0; 393 db_store_count = 0; 394 395 db_cmd_loop_done = 1; 396 } 397 398 /* continue */ 399 /*ARGSUSED*/ 400 void 401 db_continue_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 402 { 403 if (modif[0] == 'c') 404 db_run_mode = STEP_COUNT; 405 else 406 db_run_mode = STEP_CONTINUE; 407 db_inst_count = 0; 408 db_load_count = 0; 409 db_store_count = 0; 410 411 db_cmd_loop_done = 1; 412 } 413