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 * $DragonFly: src/sys/ddb/db_run.c,v 1.8 2008/08/25 17:01:37 dillon Exp $ 28 */ 29 30 /* 31 * Author: David B. Golub, Carnegie Mellon University 32 * Date: 7/90 33 */ 34 35 /* 36 * Commands to run process. 37 */ 38 #include <sys/param.h> 39 40 #include <vm/vm.h> 41 42 #include <ddb/ddb.h> 43 #include <ddb/db_break.h> 44 #include <ddb/db_access.h> 45 46 static int db_run_mode; 47 #define STEP_NONE 0 48 #define STEP_ONCE 1 49 #define STEP_RETURN 2 50 #define STEP_CALLT 3 51 #define STEP_CONTINUE 4 52 #define STEP_INVISIBLE 5 53 #define STEP_COUNT 6 54 55 static boolean_t db_sstep_print; 56 static int db_loop_count; 57 static int db_call_depth; 58 59 int db_inst_count; 60 int db_load_count; 61 int db_store_count; 62 63 #ifndef db_set_single_step 64 extern void db_set_single_step (db_regs_t *regs); 65 #endif 66 #ifndef db_clear_single_step 67 extern void db_clear_single_step (db_regs_t *regs); 68 #endif 69 70 #ifdef notused 71 static void db_single_step (db_regs_t *regs); 72 #endif 73 74 boolean_t 75 db_stop_at_pc(boolean_t *is_breakpoint) 76 { 77 db_addr_t pc; 78 db_breakpoint_t bkpt; 79 80 db_clear_single_step(DDB_REGS); 81 db_clear_breakpoints(); 82 db_clear_watchpoints(); 83 pc = PC_REGS(DDB_REGS); 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(DDB_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 } else if (*is_breakpoint) { 107 #ifdef __i386__ /* XXx */ 108 ddb_regs.tf_eip += 1; 109 #elif defined(__x86_64__) 110 ddb_regs.tf_rip += 1; 111 #endif 112 } 113 114 *is_breakpoint = FALSE; 115 116 if (db_run_mode == STEP_INVISIBLE) { 117 db_run_mode = STEP_CONTINUE; 118 return (FALSE); /* continue */ 119 } 120 if (db_run_mode == STEP_COUNT) { 121 return (FALSE); /* continue */ 122 } 123 if (db_run_mode == STEP_ONCE) { 124 if (--db_loop_count > 0) { 125 if (db_sstep_print) { 126 db_printf("\t\t"); 127 db_print_loc_and_inst(pc, DDB_REGS); 128 db_printf("\n"); 129 } 130 return (FALSE); /* continue */ 131 } 132 } 133 if (db_run_mode == STEP_RETURN) { 134 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 135 136 /* continue until matching return */ 137 138 if (!inst_trap_return(ins) && 139 (!inst_return(ins) || --db_call_depth != 0)) { 140 if (db_sstep_print) { 141 if (inst_call(ins) || inst_return(ins)) { 142 int i; 143 144 db_printf("[after %6d] ", db_inst_count); 145 for (i = db_call_depth; --i > 0; ) 146 db_printf(" "); 147 db_print_loc_and_inst(pc, DDB_REGS); 148 db_printf("\n"); 149 } 150 } 151 if (inst_call(ins)) 152 db_call_depth++; 153 return (FALSE); /* continue */ 154 } 155 } 156 if (db_run_mode == STEP_CALLT) { 157 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 158 159 /* continue until call or return */ 160 161 if (!inst_call(ins) && 162 !inst_return(ins) && 163 !inst_trap_return(ins)) { 164 return (FALSE); /* continue */ 165 } 166 } 167 db_run_mode = STEP_NONE; 168 return (TRUE); 169 } 170 171 void 172 db_restart_at_pc(boolean_t watchpt) 173 { 174 db_addr_t pc = PC_REGS(DDB_REGS); 175 176 if ((db_run_mode == STEP_COUNT) || 177 (db_run_mode == STEP_RETURN) || 178 (db_run_mode == STEP_CALLT)) { 179 db_expr_t ins __unused; /* seems used but gcc thinks not */ 180 181 /* 182 * We are about to execute this instruction, 183 * so count it now. 184 */ 185 186 ins = db_get_value(pc, sizeof(int), FALSE); 187 db_inst_count++; 188 db_load_count += inst_load(ins); 189 db_store_count += inst_store(ins); 190 #ifdef SOFTWARE_SSTEP 191 /* XXX works on mips, but... */ 192 if (inst_branch(ins) || inst_call(ins)) { 193 ins = db_get_value(next_instr_address(pc,1), 194 sizeof(int), FALSE); 195 db_inst_count++; 196 db_load_count += inst_load(ins); 197 db_store_count += inst_store(ins); 198 } 199 #endif /* SOFTWARE_SSTEP */ 200 } 201 202 if (db_run_mode == STEP_CONTINUE) { 203 if (watchpt || db_find_breakpoint_here(pc)) { 204 /* 205 * Step over breakpoint/watchpoint. 206 */ 207 db_run_mode = STEP_INVISIBLE; 208 db_set_single_step(DDB_REGS); 209 } else { 210 db_set_breakpoints(); 211 db_set_watchpoints(); 212 } 213 } else { 214 db_set_single_step(DDB_REGS); 215 } 216 } 217 218 #ifdef notused 219 static void 220 db_single_step(db_regs_t *regs) 221 { 222 if (db_run_mode == STEP_CONTINUE) { 223 db_run_mode = STEP_INVISIBLE; 224 db_set_single_step(regs); 225 } 226 } 227 #endif 228 229 #ifdef SOFTWARE_SSTEP 230 /* 231 * Software implementation of single-stepping. 232 * If your machine does not have a trace mode 233 * similar to the vax or sun ones you can use 234 * this implementation, done for the mips. 235 * Just define the above conditional and provide 236 * the functions/macros defined below. 237 * 238 * extern boolean_t 239 * inst_branch(), returns true if the instruction might branch 240 * extern unsigned 241 * branch_taken(), return the address the instruction might 242 * branch to 243 * db_getreg_val(); return the value of a user register, 244 * as indicated in the hardware instruction 245 * encoding, e.g. 8 for r8 246 * 247 * next_instr_address(pc,bd) returns the address of the first 248 * instruction following the one at "pc", 249 * which is either in the taken path of 250 * the branch (bd==1) or not. This is 251 * for machines (mips) with branch delays. 252 * 253 * A single-step may involve at most 2 breakpoints - 254 * one for branch-not-taken and one for branch taken. 255 * If one of these addresses does not already have a breakpoint, 256 * we allocate a breakpoint and save it here. 257 * These breakpoints are deleted on return. 258 */ 259 db_breakpoint_t db_not_taken_bkpt = 0; 260 db_breakpoint_t db_taken_bkpt = 0; 261 262 void 263 db_set_single_step(db_regs_t *regs) 264 { 265 db_addr_t pc = PC_REGS(regs), brpc; 266 unsigned inst; 267 268 /* 269 * User was stopped at pc, e.g. the instruction 270 * at pc was not executed. 271 */ 272 inst = db_get_value(pc, sizeof(int), FALSE); 273 if (inst_branch(inst) || inst_call(inst)) { 274 brpc = branch_taken(inst, pc, regs); 275 if (brpc != pc) { /* self-branches are hopeless */ 276 db_taken_bkpt = db_set_temp_breakpoint(brpc); 277 } 278 pc = next_instr_address(pc,1); 279 } 280 pc = next_instr_address(pc,0); 281 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 282 } 283 284 void 285 db_clear_single_step(db_regs_t *regs) 286 { 287 288 if (db_not_taken_bkpt != 0) { 289 db_delete_temp_breakpoint(db_not_taken_bkpt); 290 db_not_taken_bkpt = 0; 291 } 292 if (db_taken_bkpt != 0) { 293 db_delete_temp_breakpoint(db_taken_bkpt); 294 db_taken_bkpt = 0; 295 } 296 } 297 298 #endif /* SOFTWARE_SSTEP */ 299 300 extern int db_cmd_loop_done; 301 302 /* single-step */ 303 /*ARGSUSED*/ 304 void 305 db_single_step_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 306 char *modif) 307 { 308 boolean_t print = FALSE; 309 310 if (count == -1) 311 count = 1; 312 313 if (modif[0] == 'p') 314 print = TRUE; 315 316 db_run_mode = STEP_ONCE; 317 db_loop_count = count; 318 db_sstep_print = print; 319 db_inst_count = 0; 320 db_load_count = 0; 321 db_store_count = 0; 322 323 db_cmd_loop_done = 1; 324 } 325 326 /* trace and print until call/return */ 327 /*ARGSUSED*/ 328 void 329 db_trace_until_call_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 330 char *modif) 331 { 332 boolean_t print = FALSE; 333 334 if (modif[0] == 'p') 335 print = TRUE; 336 337 db_run_mode = STEP_CALLT; 338 db_sstep_print = print; 339 db_inst_count = 0; 340 db_load_count = 0; 341 db_store_count = 0; 342 343 db_cmd_loop_done = 1; 344 } 345 346 /*ARGSUSED*/ 347 void 348 db_trace_until_matching_cmd(db_expr_t addr, boolean_t have_addr, 349 db_expr_t count, char *modif) 350 { 351 boolean_t print = FALSE; 352 353 if (modif[0] == 'p') 354 print = TRUE; 355 356 db_run_mode = STEP_RETURN; 357 db_call_depth = 1; 358 db_sstep_print = print; 359 db_inst_count = 0; 360 db_load_count = 0; 361 db_store_count = 0; 362 363 db_cmd_loop_done = 1; 364 } 365 366 /* continue */ 367 /*ARGSUSED*/ 368 void 369 db_continue_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 370 char *modif) 371 { 372 if (modif[0] == 'c') 373 db_run_mode = STEP_COUNT; 374 else 375 db_run_mode = STEP_CONTINUE; 376 db_inst_count = 0; 377 db_load_count = 0; 378 db_store_count = 0; 379 380 db_cmd_loop_done = 1; 381 } 382