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