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