db_run.c revision 1.28
1139825Simp/* $OpenBSD: db_run.c,v 1.28 2019/11/06 07:30:08 mpi Exp $ */ 2116964Sgrehan/* $NetBSD: db_run.c,v 1.8 1996/02/05 01:57:12 christos Exp $ */ 3116964Sgrehan 4116964Sgrehan/* 5116964Sgrehan * Mach Operating System 6116964Sgrehan * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University 7116964Sgrehan * All Rights Reserved. 8116964Sgrehan * 9116964Sgrehan * Permission to use, copy, modify and distribute this software and its 10116964Sgrehan * documentation is hereby granted, provided that both the copyright 11116964Sgrehan * notice and this permission notice appear in all copies of the 12116964Sgrehan * software, derivative works or modified versions, and any portions 13116964Sgrehan * thereof, and that both notices appear in supporting documentation. 14116964Sgrehan * 15116964Sgrehan * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 16116964Sgrehan * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 17116964Sgrehan * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 18116964Sgrehan * 19116964Sgrehan * Carnegie Mellon requests users of this software to return to 20116964Sgrehan * 21116964Sgrehan * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 22116964Sgrehan * School of Computer Science 23116964Sgrehan * Carnegie Mellon University 24116964Sgrehan * Pittsburgh PA 15213-3890 25116964Sgrehan * 26116964Sgrehan * any improvements or extensions that they make and grant Carnegie Mellon 27116964Sgrehan * the rights to redistribute these changes. 28227843Smarius * 29227843Smarius * Author: David B. Golub, Carnegie Mellon University 30227843Smarius * Date: 7/90 31116964Sgrehan */ 32116964Sgrehan 33131102Sgrehan/* 34116964Sgrehan * Commands to run process. 35116964Sgrehan */ 36116964Sgrehan#include <sys/param.h> 37266019Sian#include <sys/systm.h> 38116964Sgrehan 39116964Sgrehan#include <machine/db_machdep.h> 40116964Sgrehan 41183882Snwhitehorn#include <ddb/db_run.h> 42186128Snwhitehorn#include <ddb/db_break.h> 43116964Sgrehan#include <ddb/db_access.h> 44116964Sgrehan 45116964Sgrehan#ifdef SOFTWARE_SSTEP 46116964Sgrehandb_breakpoint_t db_not_taken_bkpt = 0; 47116964Sgrehandb_breakpoint_t db_taken_bkpt = 0; 48209298Snwhitehorn#endif 49116964Sgrehan 50174782Smarcelint db_inst_count; 51116964Sgrehan 52116964Sgrehan#include <ddb/db_watch.h> 53116964Sgrehan#include <ddb/db_output.h> 54116964Sgrehan#include <ddb/db_sym.h> 55230993Snwhitehorn#include <ddb/db_extern.h> 56116964Sgrehan 57116964Sgrehanint db_run_mode; 58116964Sgrehan#define STEP_NONE 0 59116964Sgrehan#define STEP_ONCE 1 60116964Sgrehan#define STEP_RETURN 2 61116964Sgrehan#define STEP_CALLT 3 62116964Sgrehan#define STEP_CONTINUE 4 63116964Sgrehan#define STEP_INVISIBLE 5 64116964Sgrehan#define STEP_COUNT 6 65116964Sgrehan 66116964Sgrehanint db_sstep_print; 67116964Sgrehanint db_loop_count; 68116964Sgrehanint db_call_depth; 69116964Sgrehan 70116964Sgrehanint 71116964Sgrehandb_stop_at_pc(db_regs_t *regs, int *is_breakpoint) 72116964Sgrehan{ 73116964Sgrehan db_addr_t pc, old_pc; 74116964Sgrehan db_breakpoint_t bkpt; 75116964Sgrehan 76116964Sgrehan db_clear_breakpoints(); 77116964Sgrehan db_clear_watchpoints(); 78116964Sgrehan old_pc = pc = PC_REGS(regs); 79116964Sgrehan 80116964Sgrehan#ifdef FIXUP_PC_AFTER_BREAK 81116964Sgrehan if (*is_breakpoint) { 82116964Sgrehan /* 83266019Sian * Breakpoint trap. Fix up the PC if the 84116964Sgrehan * machine requires it. 85266019Sian */ 86266019Sian FIXUP_PC_AFTER_BREAK(regs); 87116964Sgrehan pc = PC_REGS(regs); 88116964Sgrehan } 89116964Sgrehan#endif 90116964Sgrehan 91116964Sgrehan /* 92116964Sgrehan * Now check for a breakpoint at this address. 93116964Sgrehan */ 94116964Sgrehan bkpt = db_find_breakpoint(pc); 95116964Sgrehan if (bkpt) { 96116964Sgrehan if (--bkpt->count == 0) { 97116964Sgrehan db_clear_single_step(regs); 98116964Sgrehan bkpt->count = bkpt->init_count; 99227843Smarius *is_breakpoint = 1; 100116964Sgrehan return 1; /* stop here */ 101116964Sgrehan } 102116964Sgrehan } else if (*is_breakpoint 103230994Snwhitehorn#ifdef SOFTWARE_SSTEP 104230993Snwhitehorn && !((db_taken_bkpt && db_taken_bkpt->address == pc) || 105266160Sian (db_not_taken_bkpt && db_not_taken_bkpt->address == pc)) 106116964Sgrehan#endif 107116964Sgrehan ) { 108116964Sgrehan#ifdef PC_ADVANCE 109116964Sgrehan PC_ADVANCE(regs); 110183882Snwhitehorn#else 111116964Sgrehan# ifdef SET_PC_REGS 112183882Snwhitehorn SET_PC_REGS(regs, old_pc); 113183882Snwhitehorn# else 114116964Sgrehan PC_REGS(regs) = old_pc; 115116964Sgrehan# endif 116116964Sgrehan#endif 117116964Sgrehan } 118116964Sgrehan db_clear_single_step(regs); 119116964Sgrehan 120116964Sgrehan *is_breakpoint = 0; 121116964Sgrehan 122116964Sgrehan if (db_run_mode == STEP_INVISIBLE) { 123116964Sgrehan db_run_mode = STEP_CONTINUE; 124116964Sgrehan return 0; /* continue */ 125116964Sgrehan } 126116964Sgrehan if (db_run_mode == STEP_COUNT) { 127116964Sgrehan return 0; /* continue */ 128116964Sgrehan } 129116964Sgrehan if (db_run_mode == STEP_ONCE) { 130116964Sgrehan if (--db_loop_count > 0) { 131116964Sgrehan if (db_sstep_print) { 132116964Sgrehan db_printf("\t\t"); 133116964Sgrehan db_print_loc_and_inst(pc); 134116964Sgrehan db_printf("\n"); 135116964Sgrehan } 136116964Sgrehan return 0; /* continue */ 137116964Sgrehan } 138116964Sgrehan } 139116964Sgrehan if (db_run_mode == STEP_RETURN) { 140116964Sgrehan db_expr_t ins = db_get_value(pc, sizeof(int), 0); 141230993Snwhitehorn 142116964Sgrehan /* continue until matching return */ 143116964Sgrehan 144116964Sgrehan if (!inst_trap_return(ins) && 145116964Sgrehan (!inst_return(ins) || --db_call_depth != 0)) { 146116964Sgrehan if (db_sstep_print) { 147116964Sgrehan if (inst_call(ins) || inst_return(ins)) { 148116964Sgrehan int i; 149116964Sgrehan 150116964Sgrehan db_printf("[after %6d] ", db_inst_count); 151116964Sgrehan for (i = db_call_depth; --i > 0; ) 152116964Sgrehan db_printf(" "); 153116964Sgrehan db_print_loc_and_inst(pc); 154116964Sgrehan db_printf("\n"); 155116964Sgrehan } 156116964Sgrehan } 157116964Sgrehan if (inst_call(ins)) 158116964Sgrehan db_call_depth++; 159116964Sgrehan return 0; /* continue */ 160116964Sgrehan } 161116964Sgrehan } 162116964Sgrehan if (db_run_mode == STEP_CALLT) { 163125688Sgrehan db_expr_t ins = db_get_value(pc, sizeof(int), 0); 164116964Sgrehan 165116964Sgrehan /* continue until call or return */ 166116964Sgrehan 167125688Sgrehan if (!inst_call(ins) && !inst_return(ins) && 168116964Sgrehan !inst_trap_return(ins)) { 169116964Sgrehan return 0; /* continue */ 170116964Sgrehan } 171116964Sgrehan } 172116964Sgrehan db_run_mode = STEP_NONE; 173116964Sgrehan return 1; 174116964Sgrehan} 175116964Sgrehan 176116964Sgrehanvoid 177116964Sgrehandb_restart_at_pc(db_regs_t *regs, int watchpt) 178116964Sgrehan{ 179116964Sgrehan db_addr_t pc = PC_REGS(regs); 180116964Sgrehan 181116964Sgrehan if ((db_run_mode == STEP_COUNT) || (db_run_mode == STEP_RETURN) || 182116964Sgrehan (db_run_mode == STEP_CALLT)) { 183116964Sgrehan db_expr_t ins; 184116964Sgrehan 185116964Sgrehan /* 186116964Sgrehan * We are about to execute this instruction, 187116964Sgrehan * so count it now. 188116964Sgrehan */ 189116964Sgrehan ins = db_get_value(pc, sizeof(int), 0); 190116964Sgrehan db_inst_count++; 191116964Sgrehan#ifdef SOFTWARE_SSTEP 192116964Sgrehan /* XXX works on mips, but... */ 193116964Sgrehan if (inst_branch(ins) || inst_call(ins)) { 194116964Sgrehan ins = db_get_value(next_instr_address(pc, 1), 195116964Sgrehan sizeof(int), 0); 196116964Sgrehan db_inst_count++; 197116964Sgrehan } 198116964Sgrehan#endif /* SOFTWARE_SSTEP */ 199116964Sgrehan } 200116964Sgrehan 201116964Sgrehan if (db_run_mode == STEP_CONTINUE) { 202116964Sgrehan if (watchpt || db_find_breakpoint(pc)) { 203116964Sgrehan /* 204116964Sgrehan * Step over breakpoint/watchpoint. 205116964Sgrehan */ 206116964Sgrehan db_run_mode = STEP_INVISIBLE; 207116964Sgrehan db_set_single_step(regs); 208116964Sgrehan } else { 209116964Sgrehan db_set_breakpoints(); 210116964Sgrehan db_set_watchpoints(); 211116964Sgrehan } 212116964Sgrehan } else { 213116964Sgrehan db_set_single_step(regs); 214116964Sgrehan } 215116964Sgrehan} 216116964Sgrehan 217116964Sgrehanvoid 218116964Sgrehandb_single_step(db_regs_t *regs) 219116964Sgrehan{ 220116964Sgrehan if (db_run_mode == STEP_CONTINUE) { 221116964Sgrehan db_run_mode = STEP_INVISIBLE; 222116964Sgrehan db_set_single_step(regs); 223116964Sgrehan } 224116964Sgrehan} 225116964Sgrehan 226125688Sgrehan/* single-step */ 227116964Sgrehan/*ARGSUSED*/ 228116964Sgrehanvoid 229116964Sgrehandb_single_step_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 230116964Sgrehan{ 231116964Sgrehan int print = 0; 232116964Sgrehan 233116964Sgrehan if (count == -1) 234116964Sgrehan count = 1; 235116964Sgrehan 236116964Sgrehan if (modif[0] == 'p') 237116964Sgrehan print = 1; 238116964Sgrehan 239116964Sgrehan db_run_mode = STEP_ONCE; 240116964Sgrehan db_loop_count = count; 241116964Sgrehan db_sstep_print = print; 242116964Sgrehan db_inst_count = 0; 243266019Sian 244266019Sian db_cmd_loop_done = 1; 245266019Sian} 246266019Sian 247266019Sian/* trace and print until call/return */ 248266019Sian/*ARGSUSED*/ 249266019Sianvoid 250266019Siandb_trace_until_call_cmd(db_expr_t addr, int have_addr, db_expr_t count, 251266019Sian char *modif) 252266019Sian{ 253266019Sian int print = 0; 254266019Sian 255266019Sian if (modif[0] == 'p') 256266019Sian print = 1; 257266019Sian 258266019Sian db_run_mode = STEP_CALLT; 259266019Sian db_sstep_print = print; 260266019Sian db_inst_count = 0; 261266019Sian 262266019Sian db_cmd_loop_done = 1; 263266019Sian} 264266019Sian 265266019Sian/*ARGSUSED*/ 266266019Sianvoid 267266019Siandb_trace_until_matching_cmd(db_expr_t addr, int have_addr, db_expr_t count, 268266019Sian char *modif) 269266019Sian{ 270266019Sian int print = 0; 271266019Sian 272266019Sian if (modif[0] == 'p') 273266019Sian print = 1; 274266019Sian 275266019Sian db_run_mode = STEP_RETURN; 276266019Sian db_call_depth = 1; 277266019Sian db_sstep_print = print; 278266019Sian db_inst_count = 0; 279266019Sian 280266019Sian db_cmd_loop_done = 1; 281266019Sian} 282266019Sian 283266019Sian/* continue */ 284266019Sian/*ARGSUSED*/ 285266019Sianvoid 286266019Siandb_continue_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif) 287116964Sgrehan{ 288116964Sgrehan if (modif[0] == 'c') 289116964Sgrehan db_run_mode = STEP_COUNT; 290116964Sgrehan else 291116964Sgrehan db_run_mode = STEP_CONTINUE; 292116964Sgrehan db_inst_count = 0; 293116964Sgrehan 294127703Sgrehan db_cmd_loop_done = 1; 295127703Sgrehan} 296116964Sgrehan 297127703Sgrehan#ifdef SOFTWARE_SSTEP 298116964Sgrehan/* 299116964Sgrehan * Software implementation of single-stepping. 300116964Sgrehan * If your machine does not have a trace mode 301116964Sgrehan * similar to the vax or sun ones you can use 302116964Sgrehan * this implementation, done for the mips. 303116964Sgrehan * Just define the above conditional and provide 304116964Sgrehan * the functions/macros defined below. 305116964Sgrehan * 306116964Sgrehan * extern int 307116964Sgrehan * inst_branch(ins), returns true if the instruction might branch 308116964Sgrehan * extern unsigned 309116964Sgrehan * branch_taken(ins, pc, getreg_val, regs), 310116964Sgrehan * return the address the instruction might 311116964Sgrehan * branch to 312116964Sgrehan * getreg_val(regs, reg), return the value of a user register, 313116964Sgrehan * as indicated in the hardware instruction 314116964Sgrehan * encoding, e.g. 8 for r8 315116964Sgrehan * 316116964Sgrehan * next_instr_address(pc, bd) returns the address of the first 317116964Sgrehan * instruction following the one at "pc", 318116964Sgrehan * which is either in the taken path of 319116964Sgrehan * the branch (bd==1) or not. This is 320116964Sgrehan * for machines (mips) with branch delays. 321116964Sgrehan * 322116964Sgrehan * A single-step may involve at most 2 breakpoints - 323116964Sgrehan * one for branch-not-taken and one for branch taken. 324116964Sgrehan * If one of these addresses does not already have a breakpoint, 325116964Sgrehan * 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 db_addr_t pc = PC_REGS(regs); 333#ifndef SOFTWARE_SSTEP_EMUL 334 db_addr_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