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 bool 74 db_stop_at_pc(int type, int code, bool *is_breakpoint, bool *is_watchpoint) 75 { 76 db_addr_t pc; 77 db_breakpoint_t bkpt; 78 79 *is_breakpoint = IS_BREAKPOINT_TRAP(type, code); 80 *is_watchpoint = IS_WATCHPOINT_TRAP(type, code); 81 pc = PC_REGS(); 82 83 db_clear_single_step(); 84 db_clear_breakpoints(); 85 db_clear_watchpoints(); 86 87 #ifdef FIXUP_PC_AFTER_BREAK 88 if (*is_breakpoint) { 89 /* 90 * Breakpoint trap. Fix up the PC if the 91 * machine requires it. 92 */ 93 FIXUP_PC_AFTER_BREAK 94 pc = PC_REGS(); 95 } 96 #endif 97 98 /* 99 * Now check for a breakpoint at this address. 100 */ 101 bkpt = db_find_breakpoint_here(pc); 102 if (bkpt) { 103 if (--bkpt->count == 0) { 104 bkpt->count = bkpt->init_count; 105 *is_breakpoint = true; 106 return (true); /* stop here */ 107 } 108 return (false); /* continue the countdown */ 109 } else if (*is_breakpoint) { 110 #ifdef BKPT_SKIP 111 BKPT_SKIP; 112 #endif 113 } 114 115 *is_breakpoint = false; /* might be a breakpoint, but not ours */ 116 117 /* 118 * If not stepping, then silently ignore single-step traps 119 * (except for clearing the single-step-flag above). 120 * 121 * If stepping, then abort if the trap type is unexpected. 122 * Breakpoints owned by us are expected and were handled above. 123 * Single-steps are expected and are handled below. All others 124 * are unexpected. 125 * 126 * Only do either of these if the MD layer claims to classify 127 * single-step traps unambiguously (by defining IS_SSTEP_TRAP). 128 * Otherwise, fall through to the bad historical behaviour 129 * given by turning unexpected traps into expected traps: if not 130 * stepping, then expect only breakpoints and stop, and if 131 * stepping, then expect only single-steps and step. 132 */ 133 #ifdef IS_SSTEP_TRAP 134 if (db_run_mode == STEP_CONTINUE && IS_SSTEP_TRAP(type, code)) 135 return (false); 136 if (db_run_mode != STEP_CONTINUE && !IS_SSTEP_TRAP(type, code)) { 137 printf("Stepping aborted\n"); 138 return (true); 139 } 140 #endif 141 142 if (db_run_mode == STEP_INVISIBLE) { 143 db_run_mode = STEP_CONTINUE; 144 return (false); /* continue */ 145 } 146 if (db_run_mode == STEP_COUNT) { 147 return (false); /* continue */ 148 } 149 if (db_run_mode == STEP_ONCE) { 150 if (--db_loop_count > 0) { 151 if (db_sstep_print) { 152 db_printf("\t\t"); 153 db_print_loc_and_inst(pc); 154 } 155 return (false); /* continue */ 156 } 157 } 158 if (db_run_mode == STEP_RETURN) { 159 /* continue until matching return */ 160 db_expr_t ins; 161 162 ins = db_get_value(pc, sizeof(int), false); 163 if (!inst_trap_return(ins) && 164 (!inst_return(ins) || --db_call_depth != 0)) { 165 if (db_sstep_print) { 166 if (inst_call(ins) || inst_return(ins)) { 167 int i; 168 169 db_printf("[after %6d] ", db_inst_count); 170 for (i = db_call_depth; --i > 0; ) 171 db_printf(" "); 172 db_print_loc_and_inst(pc); 173 } 174 } 175 if (inst_call(ins)) 176 db_call_depth++; 177 return (false); /* continue */ 178 } 179 } 180 if (db_run_mode == STEP_CALLT) { 181 /* continue until call or return */ 182 db_expr_t ins; 183 184 ins = db_get_value(pc, sizeof(int), false); 185 if (!inst_call(ins) && 186 !inst_return(ins) && 187 !inst_trap_return(ins)) { 188 return (false); /* continue */ 189 } 190 } 191 return (true); 192 } 193 194 void 195 db_restart_at_pc(bool watchpt) 196 { 197 db_addr_t pc = PC_REGS(); 198 199 if ((db_run_mode == STEP_COUNT) || 200 ((db_run_mode == STEP_ONCE) && db_sstep_multiple) || 201 (db_run_mode == STEP_RETURN) || 202 (db_run_mode == STEP_CALLT)) { 203 /* 204 * We are about to execute this instruction, 205 * so count it now. 206 */ 207 db_get_value(pc, sizeof(int), false); 208 db_inst_count++; 209 db_load_count += inst_load(ins); 210 db_store_count += inst_store(ins); 211 } 212 213 if (db_run_mode == STEP_CONTINUE) { 214 if (watchpt || db_find_breakpoint_here(pc)) { 215 /* 216 * Step over breakpoint/watchpoint. 217 */ 218 db_run_mode = STEP_INVISIBLE; 219 db_set_single_step(); 220 } else { 221 db_set_breakpoints(); 222 db_set_watchpoints(); 223 } 224 } else { 225 db_set_single_step(); 226 } 227 } 228 229 /* single-step */ 230 /*ARGSUSED*/ 231 void 232 db_single_step_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 233 { 234 bool print = false; 235 236 if (count == -1) 237 count = 1; 238 239 if (modif[0] == 'p') 240 print = true; 241 242 db_run_mode = STEP_ONCE; 243 db_loop_count = count; 244 db_sstep_multiple = (count != 1); 245 db_sstep_print = print; 246 db_inst_count = 0; 247 db_load_count = 0; 248 db_store_count = 0; 249 250 db_cmd_loop_done = 1; 251 } 252 253 /* trace and print until call/return */ 254 /*ARGSUSED*/ 255 void 256 db_trace_until_call_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 257 char *modif) 258 { 259 bool print = false; 260 261 if (modif[0] == 'p') 262 print = true; 263 264 db_run_mode = STEP_CALLT; 265 db_sstep_print = print; 266 db_inst_count = 0; 267 db_load_count = 0; 268 db_store_count = 0; 269 270 db_cmd_loop_done = 1; 271 } 272 273 /*ARGSUSED*/ 274 void 275 db_trace_until_matching_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 276 char *modif) 277 { 278 bool print = false; 279 280 if (modif[0] == 'p') 281 print = true; 282 283 db_run_mode = STEP_RETURN; 284 db_call_depth = 1; 285 db_sstep_print = print; 286 db_inst_count = 0; 287 db_load_count = 0; 288 db_store_count = 0; 289 290 db_cmd_loop_done = 1; 291 } 292 293 /* continue */ 294 /*ARGSUSED*/ 295 void 296 db_continue_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 297 { 298 if (modif[0] == 'c') 299 db_run_mode = STEP_COUNT; 300 else 301 db_run_mode = STEP_CONTINUE; 302 db_inst_count = 0; 303 db_load_count = 0; 304 db_store_count = 0; 305 306 db_cmd_loop_done = 1; 307 } 308