1 /* $NetBSD: db_run.c,v 1.11 1997/06/26 01:18:11 thorpej Exp $ */ 2 3 /* 4 * Mach Operating System 5 * Copyright (c) 1993-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_run.h> 41 #include <ddb/db_lex.h> 42 #include <ddb/db_break.h> 43 #include <ddb/db_access.h> 44 #include <ddb/db_watch.h> 45 #include <ddb/db_output.h> 46 #include <ddb/db_sym.h> 47 #include <ddb/db_extern.h> 48 49 int db_run_mode; 50 #define STEP_NONE 0 51 #define STEP_ONCE 1 52 #define STEP_RETURN 2 53 #define STEP_CALLT 3 54 #define STEP_CONTINUE 4 55 #define STEP_INVISIBLE 5 56 #define STEP_COUNT 6 57 58 boolean_t db_sstep_print; 59 int db_loop_count; 60 int db_call_depth; 61 62 boolean_t 63 db_stop_at_pc(regs, is_breakpoint) 64 db_regs_t *regs; 65 boolean_t *is_breakpoint; 66 { 67 register db_addr_t pc; 68 register db_breakpoint_t bkpt; 69 70 db_clear_single_step(regs); 71 db_clear_breakpoints(); 72 db_clear_watchpoints(); 73 pc = PC_REGS(regs); 74 75 #ifdef FIXUP_PC_AFTER_BREAK 76 if (*is_breakpoint) { 77 /* 78 * Breakpoint trap. Fix up the PC if the 79 * machine requires it. 80 */ 81 FIXUP_PC_AFTER_BREAK(regs); 82 pc = PC_REGS(regs); 83 } 84 #endif 85 86 /* 87 * Now check for a breakpoint at this address. 88 */ 89 bkpt = db_find_breakpoint_here(pc); 90 if (bkpt) { 91 if (--bkpt->count == 0) { 92 bkpt->count = bkpt->init_count; 93 *is_breakpoint = TRUE; 94 return (TRUE); /* stop here */ 95 } 96 } else if (*is_breakpoint) { 97 PC_REGS(regs) += BKPT_SIZE; 98 } 99 100 *is_breakpoint = FALSE; 101 102 if (db_run_mode == STEP_INVISIBLE) { 103 db_run_mode = STEP_CONTINUE; 104 return (FALSE); /* continue */ 105 } 106 if (db_run_mode == STEP_COUNT) { 107 return (FALSE); /* continue */ 108 } 109 if (db_run_mode == STEP_ONCE) { 110 if (--db_loop_count > 0) { 111 if (db_sstep_print) { 112 db_printf("\t\t"); 113 db_print_loc_and_inst(pc); 114 db_printf("\n"); 115 } 116 return (FALSE); /* continue */ 117 } 118 } 119 if (db_run_mode == STEP_RETURN) { 120 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 121 122 /* continue until matching return */ 123 124 if (!inst_trap_return(ins) && 125 (!inst_return(ins) || --db_call_depth != 0)) { 126 if (db_sstep_print) { 127 if (inst_call(ins) || inst_return(ins)) { 128 register int i; 129 130 db_printf("[after %6d] ", db_inst_count); 131 for (i = db_call_depth; --i > 0; ) 132 db_printf(" "); 133 db_print_loc_and_inst(pc); 134 db_printf("\n"); 135 } 136 } 137 if (inst_call(ins)) 138 db_call_depth++; 139 return (FALSE); /* continue */ 140 } 141 } 142 if (db_run_mode == STEP_CALLT) { 143 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 144 145 /* continue until call or return */ 146 147 if (!inst_call(ins) && 148 !inst_return(ins) && 149 !inst_trap_return(ins)) { 150 return (FALSE); /* continue */ 151 } 152 } 153 db_run_mode = STEP_NONE; 154 return (TRUE); 155 } 156 157 void 158 db_restart_at_pc(regs, watchpt) 159 db_regs_t *regs; 160 boolean_t watchpt; 161 { 162 register db_addr_t pc = PC_REGS(regs); 163 164 if ((db_run_mode == STEP_COUNT) || 165 (db_run_mode == STEP_RETURN) || 166 (db_run_mode == STEP_CALLT)) { 167 db_expr_t ins; 168 169 /* 170 * We are about to execute this instruction, 171 * so count it now. 172 */ 173 ins = db_get_value(pc, sizeof(int), FALSE); 174 db_inst_count++; 175 db_load_count += inst_load(ins); 176 db_store_count += inst_store(ins); 177 178 #ifdef SOFTWARE_SSTEP 179 /* 180 * Account for instructions in delay slots. 181 */ 182 { 183 db_addr_t brpc; 184 185 brpc = next_instr_address(pc, TRUE); 186 if ((brpc != pc) && (inst_branch(ins) || inst_call(ins))) { 187 ins = db_get_value(brpc, sizeof(int), FALSE); 188 db_inst_count++; 189 db_load_count += inst_load(ins); 190 db_store_count += inst_store(ins); 191 } 192 } 193 #endif 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(regs); 203 } else { 204 db_set_breakpoints(); 205 db_set_watchpoints(); 206 } 207 } else { 208 db_set_single_step(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 * boolean_t inst_branch(int inst) 232 * boolean_t inst_call(int inst) 233 * returns TRUE if the instruction might branch 234 * 235 * boolean_t inst_unconditional_flow_transfer(int inst) 236 * returns TRUE if the instruction is an unconditional 237 * transter of flow (i.e. unconditional branch) 238 * 239 * db_addr_t branch_taken(int inst, db_addr_t pc, db_regs_t *regs) 240 * returns the target address of the branch 241 * 242 * db_addr_t next_instr_address(db_addr_t pc, boolean_t bd) 243 * returns the address of the first instruction following the 244 * one at "pc", which is either in the taken path of the branch 245 * (bd == TRUE) or not. This is for machines (e.g. mips) with 246 * branch delays. 247 * 248 * A single-step may involve at most 2 breakpoints - 249 * one for branch-not-taken and one for branch taken. 250 * If one of these addresses does not already have a breakpoint, 251 * we allocate a breakpoint and save it here. 252 * These breakpoints are deleted on return. 253 */ 254 db_breakpoint_t db_not_taken_bkpt = 0; 255 db_breakpoint_t db_taken_bkpt = 0; 256 257 void 258 db_set_single_step(regs) 259 register db_regs_t *regs; 260 { 261 db_addr_t pc = PC_REGS(regs), brpc; 262 boolean_t unconditional; 263 unsigned int inst; 264 265 /* 266 * User was stopped at pc, e.g. the instruction 267 * at pc was not executed. 268 */ 269 inst = db_get_value(pc, sizeof(int), FALSE); 270 if (inst_branch(inst) || inst_call(inst)) { 271 brpc = branch_taken(inst, pc, regs); 272 if (brpc != pc) { /* self-branches are hopeless */ 273 db_taken_bkpt = db_set_temp_breakpoint(brpc); 274 } else 275 db_taken_bkpt = 0; 276 pc = next_instr_address(pc, TRUE); 277 } 278 279 /* 280 * Check if this control flow instruction is an 281 * unconditional transfer. 282 */ 283 unconditional = inst_unconditional_flow_transfer(inst); 284 285 pc = next_instr_address(pc, FALSE); 286 287 /* 288 * We only set the sequential breakpoint if previous 289 * instruction was not an unconditional change of flow 290 * control. If the previous instruction is an 291 * unconditional change of flow control, setting a 292 * breakpoint in the next sequential location may set 293 * a breakpoint in data or in another routine, which 294 * could screw up in either the program or the debugger. 295 * (Consider, for instance, that the next sequential 296 * instruction is the start of a routine needed by the 297 * debugger.) 298 */ 299 if (unconditional == FALSE && db_find_breakpoint_here(pc) == 0) 300 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 301 else 302 db_not_taken_bkpt = 0; 303 } 304 305 void 306 db_clear_single_step(regs) 307 db_regs_t *regs; 308 { 309 310 if (db_taken_bkpt != 0) { 311 db_delete_temp_breakpoint(db_taken_bkpt); 312 db_taken_bkpt = 0; 313 } 314 if (db_not_taken_bkpt != 0) { 315 db_delete_temp_breakpoint(db_not_taken_bkpt); 316 db_not_taken_bkpt = 0; 317 } 318 } 319 320 #endif /* SOFTWARE_SSTEP */ 321 322 extern int db_cmd_loop_done; 323 324 /* single-step */ 325 /*ARGSUSED*/ 326 void 327 db_single_step_cmd(addr, have_addr, count, modif) 328 db_expr_t addr; 329 int have_addr; 330 db_expr_t count; 331 char * modif; 332 { 333 boolean_t print = FALSE; 334 335 if (count == -1) 336 count = 1; 337 338 if (modif[0] == 'p') 339 print = TRUE; 340 341 db_run_mode = STEP_ONCE; 342 db_loop_count = count; 343 db_sstep_print = print; 344 db_inst_count = 0; 345 db_load_count = 0; 346 db_store_count = 0; 347 348 db_cmd_loop_done = 1; 349 } 350 351 /* trace and print until call/return */ 352 /*ARGSUSED*/ 353 void 354 db_trace_until_call_cmd(addr, have_addr, count, modif) 355 db_expr_t addr; 356 int have_addr; 357 db_expr_t count; 358 char * modif; 359 { 360 boolean_t print = FALSE; 361 362 if (modif[0] == 'p') 363 print = TRUE; 364 365 db_run_mode = STEP_CALLT; 366 db_sstep_print = print; 367 db_inst_count = 0; 368 db_load_count = 0; 369 db_store_count = 0; 370 371 db_cmd_loop_done = 1; 372 } 373 374 /*ARGSUSED*/ 375 void 376 db_trace_until_matching_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 boolean_t print = FALSE; 383 384 if (modif[0] == 'p') 385 print = TRUE; 386 387 db_run_mode = STEP_RETURN; 388 db_call_depth = 1; 389 db_sstep_print = print; 390 db_inst_count = 0; 391 db_load_count = 0; 392 db_store_count = 0; 393 394 db_cmd_loop_done = 1; 395 } 396 397 /* continue */ 398 /*ARGSUSED*/ 399 void 400 db_continue_cmd(addr, have_addr, count, modif) 401 db_expr_t addr; 402 int have_addr; 403 db_expr_t count; 404 char * modif; 405 { 406 if (modif[0] == 'c') 407 db_run_mode = STEP_COUNT; 408 else 409 db_run_mode = STEP_CONTINUE; 410 db_inst_count = 0; 411 db_load_count = 0; 412 db_store_count = 0; 413 414 db_cmd_loop_done = 1; 415 } 416