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