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