db_run.c revision 1.14
1/* $OpenBSD: db_run.c,v 1.14 2001/07/04 23:14:53 espie 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_break.h> 45#include <ddb/db_access.h> 46 47#ifdef SOFTWARE_SSTEP 48db_breakpoint_t db_not_taken_bkpt = 0; 49db_breakpoint_t db_taken_bkpt = 0; 50#endif 51 52#ifndef KGDB 53 54#include <ddb/db_lex.h> 55#include <ddb/db_watch.h> 56#include <ddb/db_output.h> 57#include <ddb/db_sym.h> 58#include <ddb/db_extern.h> 59 60int db_run_mode; 61#define STEP_NONE 0 62#define STEP_ONCE 1 63#define STEP_RETURN 2 64#define STEP_CALLT 3 65#define STEP_CONTINUE 4 66#define STEP_INVISIBLE 5 67#define STEP_COUNT 6 68 69boolean_t db_sstep_print; 70int db_loop_count; 71int db_call_depth; 72 73boolean_t 74db_stop_at_pc(regs, is_breakpoint) 75 db_regs_t *regs; 76 boolean_t *is_breakpoint; 77{ 78 db_addr_t pc, old_pc; 79 db_breakpoint_t bkpt; 80 81 db_clear_breakpoints(); 82 db_clear_watchpoints(); 83 old_pc = pc = PC_REGS(regs); 84 85#ifdef FIXUP_PC_AFTER_BREAK 86 if (*is_breakpoint) { 87 /* 88 * Breakpoint trap. Fix up the PC if the 89 * machine requires it. 90 */ 91 FIXUP_PC_AFTER_BREAK(regs); 92 pc = PC_REGS(regs); 93 } 94#endif 95 96 /* 97 * Now check for a breakpoint at this address. 98 */ 99 bkpt = db_find_breakpoint_here(pc); 100 if (bkpt) { 101 if (--bkpt->count == 0) { 102 db_clear_single_step(regs); 103 bkpt->count = bkpt->init_count; 104 *is_breakpoint = TRUE; 105 return (TRUE); /* stop here */ 106 } 107 } else if (*is_breakpoint 108#ifdef SOFTWARE_SSTEP 109 && !((db_taken_bkpt && db_taken_bkpt->address == pc) || 110 (db_not_taken_bkpt && db_not_taken_bkpt->address == pc)) 111#endif 112 ) { 113#ifdef PC_ADVANCE 114 PC_ADVANCE(regs); 115#else 116 /* 117 * XXX why on earth is this ifndef'd? Please explain! 118 * I believe this was a workaround a bug where singlestep 119 * breakpoints got deleted before recognized as such. This 120 * bug is now gone and probably this #ifndef should go too. 121 */ 122#ifndef m88k 123 PC_REGS(regs) = old_pc; 124#endif 125#endif 126 } 127 db_clear_single_step(regs); 128 129 *is_breakpoint = FALSE; 130 131 if (db_run_mode == STEP_INVISIBLE) { 132 db_run_mode = STEP_CONTINUE; 133 return (FALSE); /* continue */ 134 } 135 if (db_run_mode == STEP_COUNT) { 136 return (FALSE); /* continue */ 137 } 138 if (db_run_mode == STEP_ONCE) { 139 if (--db_loop_count > 0) { 140 if (db_sstep_print) { 141 db_printf("\t\t"); 142 db_print_loc_and_inst(pc); 143 db_printf("\n"); 144 } 145 return (FALSE); /* continue */ 146 } 147 } 148 if (db_run_mode == STEP_RETURN) { 149 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 150 151 /* continue until matching return */ 152 153 if (!inst_trap_return(ins) && 154 (!inst_return(ins) || --db_call_depth != 0)) { 155 if (db_sstep_print) { 156 if (inst_call(ins) || inst_return(ins)) { 157 register int i; 158 159 db_printf("[after %6d] ", db_inst_count); 160 for (i = db_call_depth; --i > 0; ) 161 db_printf(" "); 162 db_print_loc_and_inst(pc); 163 db_printf("\n"); 164 } 165 } 166 if (inst_call(ins)) 167 db_call_depth++; 168 return (FALSE); /* continue */ 169 } 170 } 171 if (db_run_mode == STEP_CALLT) { 172 db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 173 174 /* continue until call or return */ 175 176 if (!inst_call(ins) && !inst_return(ins) && 177 !inst_trap_return(ins)) { 178 return (FALSE); /* continue */ 179 } 180 } 181 db_run_mode = STEP_NONE; 182 return (TRUE); 183} 184 185void 186db_restart_at_pc(regs, watchpt) 187 db_regs_t *regs; 188 boolean_t watchpt; 189{ 190 db_addr_t pc = PC_REGS(regs); 191 192 if ((db_run_mode == STEP_COUNT) || (db_run_mode == STEP_RETURN) || 193 (db_run_mode == STEP_CALLT)) { 194 db_expr_t ins; 195 196 /* 197 * We are about to execute this instruction, 198 * so count it now. 199 */ 200 ins = db_get_value(pc, sizeof(int), FALSE); 201 db_inst_count++; 202 db_load_count += inst_load(ins); 203 db_store_count += inst_store(ins); 204#ifdef SOFTWARE_SSTEP 205 /* XXX works on mips, but... */ 206 if (inst_branch(ins) || inst_call(ins)) { 207 ins = db_get_value(next_instr_address(pc, 1), 208 sizeof(int), FALSE); 209 db_inst_count++; 210 db_load_count += inst_load(ins); 211 db_store_count += inst_store(ins); 212 } 213#endif /* SOFTWARE_SSTEP */ 214 } 215 216 if (db_run_mode == STEP_CONTINUE) { 217 if (watchpt || db_find_breakpoint_here(pc)) { 218 /* 219 * Step over breakpoint/watchpoint. 220 */ 221 db_run_mode = STEP_INVISIBLE; 222 db_set_single_step(regs); 223 } else { 224 db_set_breakpoints(); 225 db_set_watchpoints(); 226 } 227 } else { 228 db_set_single_step(regs); 229 } 230} 231 232void 233db_single_step(regs) 234 db_regs_t *regs; 235{ 236 if (db_run_mode == STEP_CONTINUE) { 237 db_run_mode = STEP_INVISIBLE; 238 db_set_single_step(regs); 239 } 240} 241 242extern int db_cmd_loop_done; 243 244/* single-step */ 245/*ARGSUSED*/ 246void 247db_single_step_cmd(addr, have_addr, count, modif) 248 db_expr_t addr; 249 int have_addr; 250 db_expr_t count; 251 char * modif; 252{ 253 boolean_t print = FALSE; 254 255 if (count == -1) 256 count = 1; 257 258 if (modif[0] == 'p') 259 print = TRUE; 260 261 db_run_mode = STEP_ONCE; 262 db_loop_count = count; 263 db_sstep_print = print; 264 db_inst_count = 0; 265 db_load_count = 0; 266 db_store_count = 0; 267 268 db_cmd_loop_done = 1; 269} 270 271/* trace and print until call/return */ 272/*ARGSUSED*/ 273void 274db_trace_until_call_cmd(addr, have_addr, count, modif) 275 db_expr_t addr; 276 int have_addr; 277 db_expr_t count; 278 char * modif; 279{ 280 boolean_t print = FALSE; 281 282 if (modif[0] == 'p') 283 print = TRUE; 284 285 db_run_mode = STEP_CALLT; 286 db_sstep_print = print; 287 db_inst_count = 0; 288 db_load_count = 0; 289 db_store_count = 0; 290 291 db_cmd_loop_done = 1; 292} 293 294/*ARGSUSED*/ 295void 296db_trace_until_matching_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 (modif[0] == 'p') 305 print = TRUE; 306 307 db_run_mode = STEP_RETURN; 308 db_call_depth = 1; 309 db_sstep_print = print; 310 db_inst_count = 0; 311 db_load_count = 0; 312 db_store_count = 0; 313 314 db_cmd_loop_done = 1; 315} 316 317/* continue */ 318/*ARGSUSED*/ 319void 320db_continue_cmd(addr, have_addr, count, modif) 321 db_expr_t addr; 322 int have_addr; 323 db_expr_t count; 324 char * modif; 325{ 326 if (modif[0] == 'c') 327 db_run_mode = STEP_COUNT; 328 else 329 db_run_mode = STEP_CONTINUE; 330 db_inst_count = 0; 331 db_load_count = 0; 332 db_store_count = 0; 333 334 db_cmd_loop_done = 1; 335} 336#endif /* NO KGDB */ 337 338#ifdef SOFTWARE_SSTEP 339/* 340 * Software implementation of single-stepping. 341 * If your machine does not have a trace mode 342 * similar to the vax or sun ones you can use 343 * this implementation, done for the mips. 344 * Just define the above conditional and provide 345 * the functions/macros defined below. 346 * 347 * extern boolean_t 348 * inst_branch(ins), returns true if the instruction might branch 349 * extern unsigned 350 * branch_taken(ins, pc, getreg_val, regs), 351 * return the address the instruction might 352 * branch to 353 * getreg_val(regs, reg), return the value of a user register, 354 * as indicated in the hardware instruction 355 * encoding, e.g. 8 for r8 356 * 357 * next_instr_address(pc, bd) returns the address of the first 358 * instruction following the one at "pc", 359 * which is either in the taken path of 360 * the branch (bd==1) or not. This is 361 * for machines (mips) with branch delays. 362 * 363 * A single-step may involve at most 2 breakpoints - 364 * one for branch-not-taken and one for branch taken. 365 * If one of these addresses does not already have a breakpoint, 366 * we allocate a breakpoint and save it here. 367 * These breakpoints are deleted on return. 368 */ 369 370void 371db_set_single_step(regs) 372 register db_regs_t *regs; 373{ 374 db_addr_t pc = PC_REGS(regs); 375#ifndef SOFTWARE_SSTEP_EMUL 376 db_addr_t brpc; 377 u_int inst; 378 379 /* 380 * User was stopped at pc, e.g. the instruction 381 * at pc was not executed. 382 */ 383 inst = db_get_value(pc, sizeof(int), FALSE); 384 if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) { 385 brpc = branch_taken(inst, pc, getreg_val, regs); 386 if (brpc != pc) { /* self-branches are hopeless */ 387 db_taken_bkpt = db_set_temp_breakpoint(brpc); 388 } 389#if 0 390 /* XXX this seems like a true bug, no? */ 391 pc = next_instr_address(pc, 1); 392#endif 393 } 394#endif /*SOFTWARE_SSTEP_EMUL*/ 395 pc = next_instr_address(pc, 0); 396 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 397} 398 399void 400db_clear_single_step(regs) 401 db_regs_t *regs; 402{ 403 if (db_taken_bkpt != 0) { 404 db_delete_temp_breakpoint(db_taken_bkpt); 405 db_taken_bkpt = 0; 406 } 407 if (db_not_taken_bkpt != 0) { 408 db_delete_temp_breakpoint(db_not_taken_bkpt); 409 db_not_taken_bkpt = 0; 410 } 411} 412 413#endif /* SOFTWARE_SSTEP */ 414