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