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