1 /* $NetBSD: db_run.c,v 1.13 1997/12/10 23:09:31 pk 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 #ifdef SOFTWARE_SSTEP 63 db_breakpoint_t db_not_taken_bkpt = 0; 64 db_breakpoint_t db_taken_bkpt = 0; 65 #endif 66 67 boolean_t 68 db_stop_at_pc(regs, is_breakpoint) 69 db_regs_t *regs; 70 boolean_t *is_breakpoint; 71 { 72 register db_addr_t pc; 73 register db_breakpoint_t bkpt; 74 75 pc = PC_REGS(regs); 76 77 #ifdef SOFTWARE_SSTEP 78 /* 79 * If we stopped at one of the single-step breakpoints, 80 * say it's not really a breakpoint so that 81 * we don't skip over the real instruction. 82 */ 83 if ((db_taken_bkpt != NULL && db_taken_bkpt->address == pc) || 84 (db_not_taken_bkpt != NULL && db_not_taken_bkpt->address == pc)) 85 *is_breakpoint = FALSE; 86 #endif 87 88 db_clear_single_step(regs); 89 db_clear_breakpoints(); 90 db_clear_watchpoints(); 91 92 #ifdef FIXUP_PC_AFTER_BREAK 93 if (*is_breakpoint) { 94 /* 95 * Breakpoint trap. Fix up the PC if the 96 * machine requires it. 97 */ 98 FIXUP_PC_AFTER_BREAK(regs); 99 pc = PC_REGS(regs); 100 } 101 #endif 102 103 /* 104 * Now check for a breakpoint at this address. 105 */ 106 bkpt = db_find_breakpoint_here(pc); 107 if (bkpt) { 108 if (--bkpt->count == 0) { 109 bkpt->count = bkpt->init_count; 110 *is_breakpoint = TRUE; 111 return (TRUE); /* stop here */ 112 } 113 } else if (*is_breakpoint) { 114 #ifdef PC_ADVANCE 115 PC_ADVANCE(regs); 116 #else 117 PC_REGS(regs) += BKPT_SIZE; 118 #endif 119 } 120 121 *is_breakpoint = FALSE; 122 123 if (db_run_mode == STEP_INVISIBLE) { 124 db_run_mode = STEP_CONTINUE; 125 return (FALSE); /* continue */ 126 } 127 if (db_run_mode == STEP_COUNT) { 128 return (FALSE); /* continue */ 129 } 130 if (db_run_mode == STEP_ONCE) { 131 if (--db_loop_count > 0) { 132 if (db_sstep_print) { 133 db_printf("\t\t"); 134 db_print_loc_and_inst(pc); 135 db_printf("\n"); 136 } 137 return (FALSE); /* continue */ 138 } 139 } 140 if (db_run_mode == STEP_RETURN) { 141 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 142 143 /* continue until matching return */ 144 145 if (!inst_trap_return(ins) && 146 (!inst_return(ins) || --db_call_depth != 0)) { 147 if (db_sstep_print) { 148 if (inst_call(ins) || inst_return(ins)) { 149 register int i; 150 151 db_printf("[after %6d] ", db_inst_count); 152 for (i = db_call_depth; --i > 0; ) 153 db_printf(" "); 154 db_print_loc_and_inst(pc); 155 db_printf("\n"); 156 } 157 } 158 if (inst_call(ins)) 159 db_call_depth++; 160 return (FALSE); /* continue */ 161 } 162 } 163 if (db_run_mode == STEP_CALLT) { 164 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 165 166 /* continue until call or return */ 167 168 if (!inst_call(ins) && 169 !inst_return(ins) && 170 !inst_trap_return(ins)) { 171 return (FALSE); /* continue */ 172 } 173 } 174 db_run_mode = STEP_NONE; 175 return (TRUE); 176 } 177 178 void 179 db_restart_at_pc(regs, watchpt) 180 db_regs_t *regs; 181 boolean_t watchpt; 182 { 183 register db_addr_t pc = PC_REGS(regs); 184 185 if ((db_run_mode == STEP_COUNT) || 186 (db_run_mode == STEP_RETURN) || 187 (db_run_mode == STEP_CALLT)) { 188 db_expr_t ins; 189 190 /* 191 * We are about to execute this instruction, 192 * so count it now. 193 */ 194 ins = db_get_value(pc, sizeof(int), FALSE); 195 db_inst_count++; 196 db_load_count += inst_load(ins); 197 db_store_count += inst_store(ins); 198 199 #ifdef SOFTWARE_SSTEP 200 /* 201 * Account for instructions in delay slots. 202 */ 203 { 204 db_addr_t brpc; 205 206 brpc = next_instr_address(pc, TRUE); 207 if ((brpc != pc) && (inst_branch(ins) || inst_call(ins))) { 208 ins = db_get_value(brpc, sizeof(int), FALSE); 209 db_inst_count++; 210 db_load_count += inst_load(ins); 211 db_store_count += inst_store(ins); 212 } 213 } 214 #endif 215 } 216 217 if (db_run_mode == STEP_CONTINUE) { 218 if (watchpt || db_find_breakpoint_here(pc)) { 219 /* 220 * Step over breakpoint/watchpoint. 221 */ 222 db_run_mode = STEP_INVISIBLE; 223 db_set_single_step(regs); 224 } else { 225 db_set_breakpoints(); 226 db_set_watchpoints(); 227 } 228 } else { 229 db_set_single_step(regs); 230 } 231 } 232 233 void 234 db_single_step(regs) 235 db_regs_t *regs; 236 { 237 if (db_run_mode == STEP_CONTINUE) { 238 db_run_mode = STEP_INVISIBLE; 239 db_set_single_step(regs); 240 } 241 } 242 243 #ifdef SOFTWARE_SSTEP 244 /* 245 * Software implementation of single-stepping. 246 * If your machine does not have a trace mode 247 * similar to the vax or sun ones you can use 248 * this implementation, done for the mips. 249 * Just define the above conditional and provide 250 * the functions/macros defined below. 251 * 252 * boolean_t inst_branch(int inst) 253 * boolean_t inst_call(int inst) 254 * returns TRUE if the instruction might branch 255 * 256 * boolean_t inst_unconditional_flow_transfer(int inst) 257 * returns TRUE if the instruction is an unconditional 258 * transter of flow (i.e. unconditional branch) 259 * 260 * db_addr_t branch_taken(int inst, db_addr_t pc, db_regs_t *regs) 261 * returns the target address of the branch 262 * 263 * db_addr_t next_instr_address(db_addr_t pc, boolean_t bd) 264 * returns the address of the first instruction following the 265 * one at "pc", which is either in the taken path of the branch 266 * (bd == TRUE) or not. This is for machines (e.g. mips) with 267 * branch delays. 268 * 269 * A single-step may involve at most 2 breakpoints - 270 * one for branch-not-taken and one for branch taken. 271 * If one of these addresses does not already have a breakpoint, 272 * we allocate a breakpoint and save it here. 273 * These breakpoints are deleted on return. 274 */ 275 276 void 277 db_set_single_step(regs) 278 register db_regs_t *regs; 279 { 280 db_addr_t pc = PC_REGS(regs), brpc = pc; 281 boolean_t unconditional; 282 unsigned int inst; 283 284 /* 285 * User was stopped at pc, e.g. the instruction 286 * at pc was not executed. 287 */ 288 inst = db_get_value(pc, sizeof(int), FALSE); 289 if (inst_branch(inst) || inst_call(inst)) { 290 brpc = branch_taken(inst, pc, regs); 291 if (brpc != pc) { /* self-branches are hopeless */ 292 db_taken_bkpt = db_set_temp_breakpoint(brpc); 293 } else 294 db_taken_bkpt = 0; 295 pc = next_instr_address(pc, TRUE); 296 } 297 298 /* 299 * Check if this control flow instruction is an 300 * unconditional transfer. 301 */ 302 unconditional = inst_unconditional_flow_transfer(inst); 303 304 pc = next_instr_address(pc, FALSE); 305 306 /* 307 * We only set the sequential breakpoint if previous 308 * instruction was not an unconditional change of flow 309 * control. If the previous instruction is an 310 * unconditional change of flow control, setting a 311 * breakpoint in the next sequential location may set 312 * a breakpoint in data or in another routine, which 313 * could screw up in either the program or the debugger. 314 * (Consider, for instance, that the next sequential 315 * instruction is the start of a routine needed by the 316 * debugger.) 317 * 318 * Also, don't set both the taken and not-taken breakpoints 319 * in the same place even if the MD code would otherwise 320 * have us do so. 321 */ 322 if (unconditional == FALSE && 323 db_find_breakpoint_here(pc) == 0 && 324 pc != brpc) 325 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 326 else 327 db_not_taken_bkpt = 0; 328 } 329 330 void 331 db_clear_single_step(regs) 332 db_regs_t *regs; 333 { 334 335 if (db_taken_bkpt != 0) { 336 db_delete_temp_breakpoint(db_taken_bkpt); 337 db_taken_bkpt = 0; 338 } 339 if (db_not_taken_bkpt != 0) { 340 db_delete_temp_breakpoint(db_not_taken_bkpt); 341 db_not_taken_bkpt = 0; 342 } 343 } 344 345 #endif /* SOFTWARE_SSTEP */ 346 347 extern int db_cmd_loop_done; 348 349 /* single-step */ 350 /*ARGSUSED*/ 351 void 352 db_single_step_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 (count == -1) 361 count = 1; 362 363 if (modif[0] == 'p') 364 print = TRUE; 365 366 db_run_mode = STEP_ONCE; 367 db_loop_count = count; 368 db_sstep_print = print; 369 db_inst_count = 0; 370 db_load_count = 0; 371 db_store_count = 0; 372 373 db_cmd_loop_done = 1; 374 } 375 376 /* trace and print until call/return */ 377 /*ARGSUSED*/ 378 void 379 db_trace_until_call_cmd(addr, have_addr, count, modif) 380 db_expr_t addr; 381 int have_addr; 382 db_expr_t count; 383 char * modif; 384 { 385 boolean_t print = FALSE; 386 387 if (modif[0] == 'p') 388 print = TRUE; 389 390 db_run_mode = STEP_CALLT; 391 db_sstep_print = print; 392 db_inst_count = 0; 393 db_load_count = 0; 394 db_store_count = 0; 395 396 db_cmd_loop_done = 1; 397 } 398 399 /*ARGSUSED*/ 400 void 401 db_trace_until_matching_cmd(addr, have_addr, count, modif) 402 db_expr_t addr; 403 int have_addr; 404 db_expr_t count; 405 char * modif; 406 { 407 boolean_t print = FALSE; 408 409 if (modif[0] == 'p') 410 print = TRUE; 411 412 db_run_mode = STEP_RETURN; 413 db_call_depth = 1; 414 db_sstep_print = print; 415 db_inst_count = 0; 416 db_load_count = 0; 417 db_store_count = 0; 418 419 db_cmd_loop_done = 1; 420 } 421 422 /* continue */ 423 /*ARGSUSED*/ 424 void 425 db_continue_cmd(addr, have_addr, count, modif) 426 db_expr_t addr; 427 int have_addr; 428 db_expr_t count; 429 char * modif; 430 { 431 if (modif[0] == 'c') 432 db_run_mode = STEP_COUNT; 433 else 434 db_run_mode = STEP_CONTINUE; 435 db_inst_count = 0; 436 db_load_count = 0; 437 db_store_count = 0; 438 439 db_cmd_loop_done = 1; 440 } 441