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