1 /* $NetBSD: db_run.c,v 1.5 1994/06/29 06:31:16 cgd Exp $ */ 2 3 /* 4 * Mach Operating System 5 * Copyright (c) 1991,1990 Carnegie Mellon University 6 * All Rights Reserved. 7 * 8 * Permission to use, copy, modify and distribute this software and its 9 * documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation. 13 * 14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 17 * 18 * Carnegie Mellon requests users of this software to return to 19 * 20 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 21 * School of Computer Science 22 * Carnegie Mellon University 23 * Pittsburgh PA 15213-3890 24 * 25 * any improvements or extensions that they make and grant Carnegie the 26 * rights to redistribute these changes. 27 * 28 * Author: David B. Golub, Carnegie Mellon University 29 * Date: 7/90 30 */ 31 32 /* 33 * Commands to run process. 34 */ 35 #include <sys/param.h> 36 #include <sys/proc.h> 37 38 #include <machine/db_machdep.h> 39 40 #include <ddb/db_lex.h> 41 #include <ddb/db_break.h> 42 #include <ddb/db_access.h> 43 44 int db_run_mode; 45 #define STEP_NONE 0 46 #define STEP_ONCE 1 47 #define STEP_RETURN 2 48 #define STEP_CALLT 3 49 #define STEP_CONTINUE 4 50 #define STEP_INVISIBLE 5 51 #define STEP_COUNT 6 52 53 boolean_t db_sstep_print; 54 int db_loop_count; 55 int db_call_depth; 56 57 int db_inst_count; 58 int db_load_count; 59 int db_store_count; 60 61 #ifndef db_set_single_step 62 void db_set_single_step(/* db_regs_t *regs */); /* forward */ 63 #endif 64 #ifndef db_clear_single_step 65 void db_clear_single_step(/* db_regs_t *regs */); 66 #endif 67 68 boolean_t 69 db_stop_at_pc(is_breakpoint) 70 boolean_t *is_breakpoint; 71 { 72 register db_addr_t pc; 73 register db_breakpoint_t bkpt; 74 75 db_clear_single_step(DDB_REGS); 76 db_clear_breakpoints(); 77 db_clear_watchpoints(); 78 pc = PC_REGS(DDB_REGS); 79 80 #ifdef FIXUP_PC_AFTER_BREAK 81 if (*is_breakpoint) { 82 /* 83 * Breakpoint trap. Fix up the PC if the 84 * machine requires it. 85 */ 86 FIXUP_PC_AFTER_BREAK 87 pc = PC_REGS(DDB_REGS); 88 } 89 #endif 90 91 /* 92 * Now check for a breakpoint at this address. 93 */ 94 bkpt = db_find_breakpoint_here(pc); 95 if (bkpt) { 96 if (--bkpt->count == 0) { 97 bkpt->count = bkpt->init_count; 98 *is_breakpoint = TRUE; 99 return (TRUE); /* stop here */ 100 } 101 } else if (*is_breakpoint) { 102 PC_REGS(&ddb_regs) += BKPT_SIZE; 103 } 104 105 *is_breakpoint = FALSE; 106 107 if (db_run_mode == STEP_INVISIBLE) { 108 db_run_mode = STEP_CONTINUE; 109 return (FALSE); /* continue */ 110 } 111 if (db_run_mode == STEP_COUNT) { 112 return (FALSE); /* continue */ 113 } 114 if (db_run_mode == STEP_ONCE) { 115 if (--db_loop_count > 0) { 116 if (db_sstep_print) { 117 db_printf("\t\t"); 118 db_print_loc_and_inst(pc); 119 db_printf("\n"); 120 } 121 return (FALSE); /* continue */ 122 } 123 } 124 if (db_run_mode == STEP_RETURN) { 125 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 126 127 /* continue until matching return */ 128 129 if (!inst_trap_return(ins) && 130 (!inst_return(ins) || --db_call_depth != 0)) { 131 if (db_sstep_print) { 132 if (inst_call(ins) || inst_return(ins)) { 133 register int i; 134 135 db_printf("[after %6d] ", db_inst_count); 136 for (i = db_call_depth; --i > 0; ) 137 db_printf(" "); 138 db_print_loc_and_inst(pc); 139 db_printf("\n"); 140 } 141 } 142 if (inst_call(ins)) 143 db_call_depth++; 144 return (FALSE); /* continue */ 145 } 146 } 147 if (db_run_mode == STEP_CALLT) { 148 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 149 150 /* continue until call or return */ 151 152 if (!inst_call(ins) && 153 !inst_return(ins) && 154 !inst_trap_return(ins)) { 155 return (FALSE); /* continue */ 156 } 157 } 158 db_run_mode = STEP_NONE; 159 return (TRUE); 160 } 161 162 void 163 db_restart_at_pc(watchpt) 164 boolean_t watchpt; 165 { 166 register db_addr_t pc = PC_REGS(DDB_REGS); 167 168 if ((db_run_mode == STEP_COUNT) || 169 (db_run_mode == STEP_RETURN) || 170 (db_run_mode == STEP_CALLT)) { 171 db_expr_t ins; 172 173 /* 174 * We are about to execute this instruction, 175 * so count it now. 176 */ 177 178 ins = db_get_value(pc, sizeof(int), FALSE); 179 db_inst_count++; 180 db_load_count += inst_load(ins); 181 db_store_count += inst_store(ins); 182 #ifdef SOFTWARE_SSTEP 183 /* XXX works on mips, but... */ 184 if (inst_branch(ins) || inst_call(ins)) { 185 ins = db_get_value(next_instr_address(pc,1), 186 sizeof(int), FALSE); 187 db_inst_count++; 188 db_load_count += inst_load(ins); 189 db_store_count += inst_store(ins); 190 } 191 #endif SOFTWARE_SSTEP 192 } 193 194 if (db_run_mode == STEP_CONTINUE) { 195 if (watchpt || db_find_breakpoint_here(pc)) { 196 /* 197 * Step over breakpoint/watchpoint. 198 */ 199 db_run_mode = STEP_INVISIBLE; 200 db_set_single_step(DDB_REGS); 201 } else { 202 db_set_breakpoints(); 203 db_set_watchpoints(); 204 } 205 } else { 206 db_set_single_step(DDB_REGS); 207 } 208 } 209 210 void 211 db_single_step(regs) 212 db_regs_t *regs; 213 { 214 if (db_run_mode == STEP_CONTINUE) { 215 db_run_mode = STEP_INVISIBLE; 216 db_set_single_step(regs); 217 } 218 } 219 220 #ifdef SOFTWARE_SSTEP 221 /* 222 * Software implementation of single-stepping. 223 * If your machine does not have a trace mode 224 * similar to the vax or sun ones you can use 225 * this implementation, done for the mips. 226 * Just define the above conditional and provide 227 * the functions/macros defined below. 228 * 229 * extern boolean_t 230 * inst_branch(), returns true if the instruction might branch 231 * extern unsigned 232 * branch_taken(), return the address the instruction might 233 * branch to 234 * db_getreg_val(); return the value of a user register, 235 * as indicated in the hardware instruction 236 * encoding, e.g. 8 for r8 237 * 238 * next_instr_address(pc,bd) returns the address of the first 239 * instruction following the one at "pc", 240 * which is either in the taken path of 241 * the branch (bd==1) or not. This is 242 * for machines (mips) with branch delays. 243 * 244 * A single-step may involve at most 2 breakpoints - 245 * one for branch-not-taken and one for branch taken. 246 * If one of these addresses does not already have a breakpoint, 247 * we allocate a breakpoint and save it here. 248 * These breakpoints are deleted on return. 249 */ 250 db_breakpoint_t db_not_taken_bkpt = 0; 251 db_breakpoint_t db_taken_bkpt = 0; 252 253 void 254 db_set_single_step(regs) 255 register db_regs_t *regs; 256 { 257 db_addr_t pc = PC_REGS(regs); 258 register unsigned inst, brpc; 259 260 /* 261 * User was stopped at pc, e.g. the instruction 262 * at pc was not executed. 263 */ 264 inst = db_get_value(pc, sizeof(int), FALSE); 265 if (inst_branch(inst) || inst_call(inst)) { 266 extern unsigned getreg_val(); 267 268 brpc = branch_taken(inst, pc, getreg_val, regs); 269 if (brpc != pc) { /* self-branches are hopeless */ 270 db_taken_bkpt = db_set_temp_breakpoint(brpc); 271 } 272 pc = next_instr_address(pc,1); 273 } 274 pc = next_instr_address(pc,0); 275 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 276 } 277 278 void 279 db_clear_single_step(regs) 280 db_regs_t *regs; 281 { 282 register db_breakpoint_t bkpt; 283 284 if (db_taken_bkpt != 0) { 285 db_delete_temp_breakpoint(db_taken_bkpt); 286 db_taken_bkpt = 0; 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 } 293 294 #endif SOFTWARE_SSTEP 295 296 extern int db_cmd_loop_done; 297 298 /* single-step */ 299 /*ARGSUSED*/ 300 void 301 db_single_step_cmd(addr, have_addr, count, modif) 302 db_expr_t addr; 303 int have_addr; 304 db_expr_t count; 305 char * modif; 306 { 307 boolean_t print = FALSE; 308 309 if (count == -1) 310 count = 1; 311 312 if (modif[0] == 'p') 313 print = TRUE; 314 315 db_run_mode = STEP_ONCE; 316 db_loop_count = count; 317 db_sstep_print = print; 318 db_inst_count = 0; 319 db_load_count = 0; 320 db_store_count = 0; 321 322 db_cmd_loop_done = 1; 323 } 324 325 /* trace and print until call/return */ 326 /*ARGSUSED*/ 327 void 328 db_trace_until_call_cmd(addr, have_addr, count, modif) 329 db_expr_t addr; 330 int have_addr; 331 db_expr_t count; 332 char * modif; 333 { 334 boolean_t print = FALSE; 335 336 if (modif[0] == 'p') 337 print = TRUE; 338 339 db_run_mode = STEP_CALLT; 340 db_sstep_print = print; 341 db_inst_count = 0; 342 db_load_count = 0; 343 db_store_count = 0; 344 345 db_cmd_loop_done = 1; 346 } 347 348 /*ARGSUSED*/ 349 void 350 db_trace_until_matching_cmd(addr, have_addr, count, modif) 351 db_expr_t addr; 352 int have_addr; 353 db_expr_t count; 354 char * modif; 355 { 356 boolean_t print = FALSE; 357 358 if (modif[0] == 'p') 359 print = TRUE; 360 361 db_run_mode = STEP_RETURN; 362 db_call_depth = 1; 363 db_sstep_print = print; 364 db_inst_count = 0; 365 db_load_count = 0; 366 db_store_count = 0; 367 368 db_cmd_loop_done = 1; 369 } 370 371 /* continue */ 372 /*ARGSUSED*/ 373 void 374 db_continue_cmd(addr, have_addr, count, modif) 375 db_expr_t addr; 376 int have_addr; 377 db_expr_t count; 378 char * modif; 379 { 380 if (modif[0] == 'c') 381 db_run_mode = STEP_COUNT; 382 else 383 db_run_mode = STEP_CONTINUE; 384 db_inst_count = 0; 385 db_load_count = 0; 386 db_store_count = 0; 387 388 db_cmd_loop_done = 1; 389 } 390