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