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.6 1995/11/24 14:13:40 bde 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 extern void db_set_single_step __P((db_regs_t *regs); 65 #endif 66 #ifndef db_clear_single_step 67 extern void db_clear_single_step __P((db_regs_t *regs)); 68 #endif 69 70 #ifdef notused 71 static void db_single_step __P((db_regs_t *regs)); 72 #endif 73 74 boolean_t 75 db_stop_at_pc(is_breakpoint) 76 boolean_t *is_breakpoint; 77 { 78 register db_addr_t pc; 79 register db_breakpoint_t bkpt; 80 81 db_clear_single_step(DDB_REGS); 82 db_clear_breakpoints(); 83 db_clear_watchpoints(); 84 pc = PC_REGS(DDB_REGS); 85 86 #ifdef FIXUP_PC_AFTER_BREAK 87 if (*is_breakpoint) { 88 /* 89 * Breakpoint trap. Fix up the PC if the 90 * machine requires it. 91 */ 92 FIXUP_PC_AFTER_BREAK 93 pc = PC_REGS(DDB_REGS); 94 } 95 #endif 96 97 /* 98 * Now check for a breakpoint at this address. 99 */ 100 bkpt = db_find_breakpoint_here(pc); 101 if (bkpt) { 102 if (--bkpt->count == 0) { 103 bkpt->count = bkpt->init_count; 104 *is_breakpoint = TRUE; 105 return (TRUE); /* stop here */ 106 } 107 } else if (*is_breakpoint) { 108 ddb_regs.tf_eip += 1; 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); 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 register 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); 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(watchpt) 170 boolean_t watchpt; 171 { 172 register 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(regs) 219 db_regs_t *regs; 220 { 221 if (db_run_mode == STEP_CONTINUE) { 222 db_run_mode = STEP_INVISIBLE; 223 db_set_single_step(regs); 224 } 225 } 226 #endif 227 228 #ifdef SOFTWARE_SSTEP 229 /* 230 * Software implementation of single-stepping. 231 * If your machine does not have a trace mode 232 * similar to the vax or sun ones you can use 233 * this implementation, done for the mips. 234 * Just define the above conditional and provide 235 * the functions/macros defined below. 236 * 237 * extern boolean_t 238 * inst_branch(), returns true if the instruction might branch 239 * extern unsigned 240 * branch_taken(), return the address the instruction might 241 * branch to 242 * db_getreg_val(); return the value of a user register, 243 * as indicated in the hardware instruction 244 * encoding, e.g. 8 for r8 245 * 246 * next_instr_address(pc,bd) returns the address of the first 247 * instruction following the one at "pc", 248 * which is either in the taken path of 249 * the branch (bd==1) or not. This is 250 * for machines (mips) with branch delays. 251 * 252 * A single-step may involve at most 2 breakpoints - 253 * one for branch-not-taken and one for branch taken. 254 * If one of these addresses does not already have a breakpoint, 255 * we allocate a breakpoint and save it here. 256 * These breakpoints are deleted on return. 257 */ 258 db_breakpoint_t db_not_taken_bkpt = 0; 259 db_breakpoint_t db_taken_bkpt = 0; 260 261 void 262 db_set_single_step(regs) 263 register db_regs_t *regs; 264 { 265 db_addr_t pc = PC_REGS(regs); 266 register unsigned inst, brpc; 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 extern unsigned getreg_val(); 275 276 brpc = branch_taken(inst, pc, getreg_val, regs); 277 if (brpc != pc) { /* self-branches are hopeless */ 278 db_taken_bkpt = db_set_temp_breakpoint(brpc); 279 } 280 pc = next_instr_address(pc,1); 281 } 282 pc = next_instr_address(pc,0); 283 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 284 } 285 286 void 287 db_clear_single_step(regs) 288 db_regs_t *regs; 289 { 290 register db_breakpoint_t bkpt; 291 292 if (db_taken_bkpt != 0) { 293 db_delete_temp_breakpoint(db_taken_bkpt); 294 db_taken_bkpt = 0; 295 } 296 if (db_not_taken_bkpt != 0) { 297 db_delete_temp_breakpoint(db_not_taken_bkpt); 298 db_not_taken_bkpt = 0; 299 } 300 } 301 302 #endif SOFTWARE_SSTEP 303 304 extern int db_cmd_loop_done; 305 306 /* single-step */ 307 /*ARGSUSED*/ 308 void 309 db_single_step_cmd(addr, have_addr, count, modif) 310 db_expr_t addr; 311 boolean_t have_addr; 312 db_expr_t count; 313 char * modif; 314 { 315 boolean_t print = FALSE; 316 317 if (count == -1) 318 count = 1; 319 320 if (modif[0] == 'p') 321 print = TRUE; 322 323 db_run_mode = STEP_ONCE; 324 db_loop_count = count; 325 db_sstep_print = print; 326 db_inst_count = 0; 327 db_load_count = 0; 328 db_store_count = 0; 329 330 db_cmd_loop_done = 1; 331 } 332 333 /* trace and print until call/return */ 334 /*ARGSUSED*/ 335 void 336 db_trace_until_call_cmd(addr, have_addr, count, modif) 337 db_expr_t addr; 338 boolean_t have_addr; 339 db_expr_t count; 340 char * modif; 341 { 342 boolean_t print = FALSE; 343 344 if (modif[0] == 'p') 345 print = TRUE; 346 347 db_run_mode = STEP_CALLT; 348 db_sstep_print = print; 349 db_inst_count = 0; 350 db_load_count = 0; 351 db_store_count = 0; 352 353 db_cmd_loop_done = 1; 354 } 355 356 /*ARGSUSED*/ 357 void 358 db_trace_until_matching_cmd(addr, have_addr, count, modif) 359 db_expr_t addr; 360 boolean_t have_addr; 361 db_expr_t count; 362 char * modif; 363 { 364 boolean_t print = FALSE; 365 366 if (modif[0] == 'p') 367 print = TRUE; 368 369 db_run_mode = STEP_RETURN; 370 db_call_depth = 1; 371 db_sstep_print = print; 372 db_inst_count = 0; 373 db_load_count = 0; 374 db_store_count = 0; 375 376 db_cmd_loop_done = 1; 377 } 378 379 /* continue */ 380 /*ARGSUSED*/ 381 void 382 db_continue_cmd(addr, have_addr, count, modif) 383 db_expr_t addr; 384 boolean_t have_addr; 385 db_expr_t count; 386 char * modif; 387 { 388 if (modif[0] == 'c') 389 db_run_mode = STEP_COUNT; 390 else 391 db_run_mode = STEP_CONTINUE; 392 db_inst_count = 0; 393 db_load_count = 0; 394 db_store_count = 0; 395 396 db_cmd_loop_done = 1; 397 } 398