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