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 * $Id: db_run.c,v 1.12 1997/02/22 09:28:29 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 __P((db_regs_t *regs); 64 #endif 65 #ifndef db_clear_single_step 66 extern void db_clear_single_step __P((db_regs_t *regs)); 67 #endif 68 69 #ifdef notused 70 static void db_single_step __P((db_regs_t *regs)); 71 #endif 72 73 boolean_t 74 db_stop_at_pc(is_breakpoint) 75 boolean_t *is_breakpoint; 76 { 77 register db_addr_t pc; 78 register 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 ddb_regs.tf_eip += 1; 108 } 109 110 *is_breakpoint = FALSE; 111 112 if (db_run_mode == STEP_INVISIBLE) { 113 db_run_mode = STEP_CONTINUE; 114 return (FALSE); /* continue */ 115 } 116 if (db_run_mode == STEP_COUNT) { 117 return (FALSE); /* continue */ 118 } 119 if (db_run_mode == STEP_ONCE) { 120 if (--db_loop_count > 0) { 121 if (db_sstep_print) { 122 db_printf("\t\t"); 123 db_print_loc_and_inst(pc); 124 db_printf("\n"); 125 } 126 return (FALSE); /* continue */ 127 } 128 } 129 if (db_run_mode == STEP_RETURN) { 130 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 131 132 /* continue until matching return */ 133 134 if (!inst_trap_return(ins) && 135 (!inst_return(ins) || --db_call_depth != 0)) { 136 if (db_sstep_print) { 137 if (inst_call(ins) || inst_return(ins)) { 138 register int i; 139 140 db_printf("[after %6d] ", db_inst_count); 141 for (i = db_call_depth; --i > 0; ) 142 db_printf(" "); 143 db_print_loc_and_inst(pc); 144 db_printf("\n"); 145 } 146 } 147 if (inst_call(ins)) 148 db_call_depth++; 149 return (FALSE); /* continue */ 150 } 151 } 152 if (db_run_mode == STEP_CALLT) { 153 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 154 155 /* continue until call or return */ 156 157 if (!inst_call(ins) && 158 !inst_return(ins) && 159 !inst_trap_return(ins)) { 160 return (FALSE); /* continue */ 161 } 162 } 163 db_run_mode = STEP_NONE; 164 return (TRUE); 165 } 166 167 void 168 db_restart_at_pc(watchpt) 169 boolean_t watchpt; 170 { 171 register 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; 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(regs) 218 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(regs) 262 register db_regs_t *regs; 263 { 264 db_addr_t pc = PC_REGS(regs); 265 register unsigned inst, brpc; 266 267 /* 268 * User was stopped at pc, e.g. the instruction 269 * at pc was not executed. 270 */ 271 inst = db_get_value(pc, sizeof(int), FALSE); 272 if (inst_branch(inst) || inst_call(inst)) { 273 extern unsigned getreg_val(); 274 275 brpc = branch_taken(inst, pc, getreg_val, regs); 276 if (brpc != pc) { /* self-branches are hopeless */ 277 db_taken_bkpt = db_set_temp_breakpoint(brpc); 278 } 279 pc = next_instr_address(pc,1); 280 } 281 pc = next_instr_address(pc,0); 282 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 283 } 284 285 void 286 db_clear_single_step(regs) 287 db_regs_t *regs; 288 { 289 register db_breakpoint_t bkpt; 290 291 if (db_taken_bkpt != 0) { 292 db_delete_temp_breakpoint(db_taken_bkpt); 293 db_taken_bkpt = 0; 294 } 295 if (db_not_taken_bkpt != 0) { 296 db_delete_temp_breakpoint(db_not_taken_bkpt); 297 db_not_taken_bkpt = 0; 298 } 299 } 300 301 #endif SOFTWARE_SSTEP 302 303 extern int db_cmd_loop_done; 304 305 /* single-step */ 306 /*ARGSUSED*/ 307 void 308 db_single_step_cmd(addr, have_addr, count, modif) 309 db_expr_t addr; 310 boolean_t have_addr; 311 db_expr_t count; 312 char * modif; 313 { 314 boolean_t print = FALSE; 315 316 if (count == -1) 317 count = 1; 318 319 if (modif[0] == 'p') 320 print = TRUE; 321 322 db_run_mode = STEP_ONCE; 323 db_loop_count = count; 324 db_sstep_print = print; 325 db_inst_count = 0; 326 db_load_count = 0; 327 db_store_count = 0; 328 329 db_cmd_loop_done = 1; 330 } 331 332 /* trace and print until call/return */ 333 /*ARGSUSED*/ 334 void 335 db_trace_until_call_cmd(addr, have_addr, count, modif) 336 db_expr_t addr; 337 boolean_t have_addr; 338 db_expr_t count; 339 char * modif; 340 { 341 boolean_t print = FALSE; 342 343 if (modif[0] == 'p') 344 print = TRUE; 345 346 db_run_mode = STEP_CALLT; 347 db_sstep_print = print; 348 db_inst_count = 0; 349 db_load_count = 0; 350 db_store_count = 0; 351 352 db_cmd_loop_done = 1; 353 } 354 355 /*ARGSUSED*/ 356 void 357 db_trace_until_matching_cmd(addr, have_addr, count, modif) 358 db_expr_t addr; 359 boolean_t have_addr; 360 db_expr_t count; 361 char * modif; 362 { 363 boolean_t print = FALSE; 364 365 if (modif[0] == 'p') 366 print = TRUE; 367 368 db_run_mode = STEP_RETURN; 369 db_call_depth = 1; 370 db_sstep_print = print; 371 db_inst_count = 0; 372 db_load_count = 0; 373 db_store_count = 0; 374 375 db_cmd_loop_done = 1; 376 } 377 378 /* continue */ 379 /*ARGSUSED*/ 380 void 381 db_continue_cmd(addr, have_addr, count, modif) 382 db_expr_t addr; 383 boolean_t have_addr; 384 db_expr_t count; 385 char * modif; 386 { 387 if (modif[0] == 'c') 388 db_run_mode = STEP_COUNT; 389 else 390 db_run_mode = STEP_CONTINUE; 391 db_inst_count = 0; 392 db_load_count = 0; 393 db_store_count = 0; 394 395 db_cmd_loop_done = 1; 396 } 397