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