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