db_run.c revision 1.3
1/* $OpenBSD: db_run.c,v 1.3 1996/03/11 11:16:20 mickey Exp $ */ 2 3/* 4 * Mach Operating System 5 * Copyright (c) 1993,1992,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 "AS IS" 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 Mellon 26 * the 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 49int 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 58boolean_t db_sstep_print; 59int db_loop_count; 60int db_call_depth; 61 62boolean_t 63db_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 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 157void 158db_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 174 ins = db_get_value(pc, sizeof(int), FALSE); 175 db_inst_count++; 176 db_load_count += inst_load(ins); 177 db_store_count += inst_store(ins); 178#ifdef SOFTWARE_SSTEP 179 /* XXX works on mips, but... */ 180 if (inst_branch(ins) || inst_call(ins)) { 181 ins = db_get_value(next_instr_address(pc,1), 182 sizeof(int), FALSE); 183 db_inst_count++; 184 db_load_count += inst_load(ins); 185 db_store_count += inst_store(ins); 186 } 187#endif SOFTWARE_SSTEP 188 } 189 190 if (db_run_mode == STEP_CONTINUE) { 191 if (watchpt || db_find_breakpoint_here(pc)) { 192 /* 193 * Step over breakpoint/watchpoint. 194 */ 195 db_run_mode = STEP_INVISIBLE; 196 db_set_single_step(regs); 197 } else { 198 db_set_breakpoints(); 199 db_set_watchpoints(); 200 } 201 } else { 202 db_set_single_step(regs); 203 } 204} 205 206void 207db_single_step(regs) 208 db_regs_t *regs; 209{ 210 if (db_run_mode == STEP_CONTINUE) { 211 db_run_mode = STEP_INVISIBLE; 212 db_set_single_step(regs); 213 } 214} 215 216#ifdef SOFTWARE_SSTEP 217/* 218 * Software implementation of single-stepping. 219 * If your machine does not have a trace mode 220 * similar to the vax or sun ones you can use 221 * this implementation, done for the mips. 222 * Just define the above conditional and provide 223 * the functions/macros defined below. 224 * 225 * extern boolean_t 226 * inst_branch(), returns true if the instruction might branch 227 * extern unsigned 228 * branch_taken(), return the address the instruction might 229 * branch to 230 * db_getreg_val(); return the value of a user register, 231 * as indicated in the hardware instruction 232 * encoding, e.g. 8 for r8 233 * 234 * next_instr_address(pc,bd) returns the address of the first 235 * instruction following the one at "pc", 236 * which is either in the taken path of 237 * the branch (bd==1) or not. This is 238 * for machines (mips) with branch delays. 239 * 240 * A single-step may involve at most 2 breakpoints - 241 * one for branch-not-taken and one for branch taken. 242 * If one of these addresses does not already have a breakpoint, 243 * we allocate a breakpoint and save it here. 244 * These breakpoints are deleted on return. 245 */ 246db_breakpoint_t db_not_taken_bkpt = 0; 247db_breakpoint_t db_taken_bkpt = 0; 248 249void 250db_set_single_step(regs) 251 register db_regs_t *regs; 252{ 253 db_addr_t pc = PC_REGS(regs); 254 register unsigned inst, brpc; 255 256 /* 257 * User was stopped at pc, e.g. the instruction 258 * at pc was not executed. 259 */ 260 inst = db_get_value(pc, sizeof(int), FALSE); 261 if (inst_branch(inst) || inst_call(inst)) { 262 263 brpc = branch_taken(inst, pc, getreg_val, regs); 264 if (brpc != pc) { /* self-branches are hopeless */ 265 db_taken_bkpt = db_set_temp_breakpoint(brpc); 266 } 267 pc = next_instr_address(pc,1); 268 } 269 pc = next_instr_address(pc,0); 270 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 271} 272 273void 274db_clear_single_step(regs) 275 db_regs_t *regs; 276{ 277 register db_breakpoint_t bkpt; 278 279 if (db_taken_bkpt != 0) { 280 db_delete_temp_breakpoint(db_taken_bkpt); 281 db_taken_bkpt = 0; 282 } 283 if (db_not_taken_bkpt != 0) { 284 db_delete_temp_breakpoint(db_not_taken_bkpt); 285 db_not_taken_bkpt = 0; 286 } 287} 288 289#endif SOFTWARE_SSTEP 290 291extern int db_cmd_loop_done; 292 293/* single-step */ 294/*ARGSUSED*/ 295void 296db_single_step_cmd(addr, have_addr, count, modif) 297 db_expr_t addr; 298 int have_addr; 299 db_expr_t count; 300 char * modif; 301{ 302 boolean_t print = FALSE; 303 304 if (count == -1) 305 count = 1; 306 307 if (modif[0] == 'p') 308 print = TRUE; 309 310 db_run_mode = STEP_ONCE; 311 db_loop_count = count; 312 db_sstep_print = print; 313 db_inst_count = 0; 314 db_load_count = 0; 315 db_store_count = 0; 316 317 db_cmd_loop_done = 1; 318} 319 320/* trace and print until call/return */ 321/*ARGSUSED*/ 322void 323db_trace_until_call_cmd(addr, have_addr, count, modif) 324 db_expr_t addr; 325 int have_addr; 326 db_expr_t count; 327 char * modif; 328{ 329 boolean_t print = FALSE; 330 331 if (modif[0] == 'p') 332 print = TRUE; 333 334 db_run_mode = STEP_CALLT; 335 db_sstep_print = print; 336 db_inst_count = 0; 337 db_load_count = 0; 338 db_store_count = 0; 339 340 db_cmd_loop_done = 1; 341} 342 343/*ARGSUSED*/ 344void 345db_trace_until_matching_cmd(addr, have_addr, count, modif) 346 db_expr_t addr; 347 int have_addr; 348 db_expr_t count; 349 char * modif; 350{ 351 boolean_t print = FALSE; 352 353 if (modif[0] == 'p') 354 print = TRUE; 355 356 db_run_mode = STEP_RETURN; 357 db_call_depth = 1; 358 db_sstep_print = print; 359 db_inst_count = 0; 360 db_load_count = 0; 361 db_store_count = 0; 362 363 db_cmd_loop_done = 1; 364} 365 366/* continue */ 367/*ARGSUSED*/ 368void 369db_continue_cmd(addr, have_addr, count, modif) 370 db_expr_t addr; 371 int have_addr; 372 db_expr_t count; 373 char * modif; 374{ 375 if (modif[0] == 'c') 376 db_run_mode = STEP_COUNT; 377 else 378 db_run_mode = STEP_CONTINUE; 379 db_inst_count = 0; 380 db_load_count = 0; 381 db_store_count = 0; 382 383 db_cmd_loop_done = 1; 384} 385