db_run.c revision 2056
14Srgrimes/* 24Srgrimes * Mach Operating System 34Srgrimes * Copyright (c) 1991,1990 Carnegie Mellon University 44Srgrimes * All Rights Reserved. 54Srgrimes * 64Srgrimes * Permission to use, copy, modify and distribute this software and its 74Srgrimes * documentation is hereby granted, provided that both the copyright 84Srgrimes * notice and this permission notice appear in all copies of the 94Srgrimes * software, derivative works or modified versions, and any portions 104Srgrimes * thereof, and that both notices appear in supporting documentation. 114Srgrimes * 124Srgrimes * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 134Srgrimes * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 144Srgrimes * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 154Srgrimes * 164Srgrimes * Carnegie Mellon requests users of this software to return to 174Srgrimes * 184Srgrimes * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 194Srgrimes * School of Computer Science 204Srgrimes * Carnegie Mellon University 214Srgrimes * Pittsburgh PA 15213-3890 224Srgrimes * 234Srgrimes * any improvements or extensions that they make and grant Carnegie the 244Srgrimes * rights to redistribute these changes. 254Srgrimes * 262056Swollman * $Id: db_run.c,v 1.3 1993/11/25 01:30:10 wollman Exp $ 274Srgrimes */ 28623Srgrimes 294Srgrimes/* 304Srgrimes * Author: David B. Golub, Carnegie Mellon University 314Srgrimes * Date: 7/90 324Srgrimes */ 334Srgrimes 344Srgrimes/* 354Srgrimes * Commands to run process. 364Srgrimes */ 372056Swollman#include <sys/param.h> 382056Swollman#include <sys/systm.h> 392056Swollman#include <sys/proc.h> 402056Swollman#include <ddb/ddb.h> 414Srgrimes 424Srgrimes#include <ddb/db_lex.h> 434Srgrimes#include <ddb/db_break.h> 444Srgrimes#include <ddb/db_access.h> 454Srgrimes 464Srgrimesint db_run_mode; 474Srgrimes#define STEP_NONE 0 484Srgrimes#define STEP_ONCE 1 494Srgrimes#define STEP_RETURN 2 504Srgrimes#define STEP_CALLT 3 514Srgrimes#define STEP_CONTINUE 4 524Srgrimes#define STEP_INVISIBLE 5 534Srgrimes#define STEP_COUNT 6 544Srgrimes 554Srgrimesboolean_t db_sstep_print; 564Srgrimesint db_loop_count; 574Srgrimesint db_call_depth; 584Srgrimes 594Srgrimesint db_inst_count; 604Srgrimesint db_load_count; 614Srgrimesint db_store_count; 624Srgrimes 634Srgrimes#ifndef db_set_single_step 644Srgrimesvoid db_set_single_step(/* db_regs_t *regs */); /* forward */ 654Srgrimes#endif 664Srgrimes#ifndef db_clear_single_step 674Srgrimesvoid db_clear_single_step(/* db_regs_t *regs */); 684Srgrimes#endif 694Srgrimes 704Srgrimesboolean_t 714Srgrimesdb_stop_at_pc(is_breakpoint) 724Srgrimes boolean_t *is_breakpoint; 734Srgrimes{ 744Srgrimes register db_addr_t pc; 754Srgrimes register db_breakpoint_t bkpt; 764Srgrimes 774Srgrimes db_clear_single_step(DDB_REGS); 784Srgrimes db_clear_breakpoints(); 794Srgrimes db_clear_watchpoints(); 804Srgrimes pc = PC_REGS(DDB_REGS); 814Srgrimes 824Srgrimes#ifdef FIXUP_PC_AFTER_BREAK 834Srgrimes if (*is_breakpoint) { 844Srgrimes /* 854Srgrimes * Breakpoint trap. Fix up the PC if the 864Srgrimes * machine requires it. 874Srgrimes */ 884Srgrimes FIXUP_PC_AFTER_BREAK 894Srgrimes pc = PC_REGS(DDB_REGS); 904Srgrimes } 914Srgrimes#endif 924Srgrimes 934Srgrimes /* 944Srgrimes * Now check for a breakpoint at this address. 954Srgrimes */ 964Srgrimes bkpt = db_find_breakpoint_here(pc); 974Srgrimes if (bkpt) { 984Srgrimes if (--bkpt->count == 0) { 994Srgrimes bkpt->count = bkpt->init_count; 1004Srgrimes *is_breakpoint = TRUE; 1014Srgrimes return (TRUE); /* stop here */ 1024Srgrimes } 1034Srgrimes } else if (*is_breakpoint) { 1044Srgrimes ddb_regs.tf_eip += 1; 1054Srgrimes } 1064Srgrimes 1074Srgrimes *is_breakpoint = FALSE; 1084Srgrimes 1094Srgrimes if (db_run_mode == STEP_INVISIBLE) { 1104Srgrimes db_run_mode = STEP_CONTINUE; 1114Srgrimes return (FALSE); /* continue */ 1124Srgrimes } 1134Srgrimes if (db_run_mode == STEP_COUNT) { 1144Srgrimes return (FALSE); /* continue */ 1154Srgrimes } 1164Srgrimes if (db_run_mode == STEP_ONCE) { 1174Srgrimes if (--db_loop_count > 0) { 1184Srgrimes if (db_sstep_print) { 1194Srgrimes db_printf("\t\t"); 1204Srgrimes db_print_loc_and_inst(pc); 1214Srgrimes db_printf("\n"); 1224Srgrimes } 1234Srgrimes return (FALSE); /* continue */ 1244Srgrimes } 1254Srgrimes } 1264Srgrimes if (db_run_mode == STEP_RETURN) { 1274Srgrimes db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 1284Srgrimes 1294Srgrimes /* continue until matching return */ 1304Srgrimes 1314Srgrimes if (!inst_trap_return(ins) && 1324Srgrimes (!inst_return(ins) || --db_call_depth != 0)) { 1334Srgrimes if (db_sstep_print) { 1344Srgrimes if (inst_call(ins) || inst_return(ins)) { 1354Srgrimes register int i; 1364Srgrimes 1374Srgrimes db_printf("[after %6d] ", db_inst_count); 1384Srgrimes for (i = db_call_depth; --i > 0; ) 1394Srgrimes db_printf(" "); 1404Srgrimes db_print_loc_and_inst(pc); 1414Srgrimes db_printf("\n"); 1424Srgrimes } 1434Srgrimes } 1444Srgrimes if (inst_call(ins)) 1454Srgrimes db_call_depth++; 1464Srgrimes return (FALSE); /* continue */ 1474Srgrimes } 1484Srgrimes } 1494Srgrimes if (db_run_mode == STEP_CALLT) { 1504Srgrimes db_expr_t ins = db_get_value(pc, sizeof(int), FALSE); 1514Srgrimes 1524Srgrimes /* continue until call or return */ 1534Srgrimes 1544Srgrimes if (!inst_call(ins) && 1554Srgrimes !inst_return(ins) && 1564Srgrimes !inst_trap_return(ins)) { 1574Srgrimes return (FALSE); /* continue */ 1584Srgrimes } 1594Srgrimes } 1604Srgrimes db_run_mode = STEP_NONE; 1614Srgrimes return (TRUE); 1624Srgrimes} 1634Srgrimes 1644Srgrimesvoid 1654Srgrimesdb_restart_at_pc(watchpt) 1664Srgrimes boolean_t watchpt; 1674Srgrimes{ 1684Srgrimes register db_addr_t pc = PC_REGS(DDB_REGS); 1694Srgrimes 1704Srgrimes if ((db_run_mode == STEP_COUNT) || 1714Srgrimes (db_run_mode == STEP_RETURN) || 1724Srgrimes (db_run_mode == STEP_CALLT)) { 1734Srgrimes db_expr_t ins; 1744Srgrimes 1754Srgrimes /* 1764Srgrimes * We are about to execute this instruction, 1774Srgrimes * so count it now. 1784Srgrimes */ 1794Srgrimes 1804Srgrimes ins = db_get_value(pc, sizeof(int), FALSE); 1814Srgrimes db_inst_count++; 1824Srgrimes db_load_count += inst_load(ins); 1834Srgrimes db_store_count += inst_store(ins); 1844Srgrimes#ifdef SOFTWARE_SSTEP 1854Srgrimes /* XXX works on mips, but... */ 1864Srgrimes if (inst_branch(ins) || inst_call(ins)) { 1874Srgrimes ins = db_get_value(next_instr_address(pc,1), 1884Srgrimes sizeof(int), FALSE); 1894Srgrimes db_inst_count++; 1904Srgrimes db_load_count += inst_load(ins); 1914Srgrimes db_store_count += inst_store(ins); 1924Srgrimes } 1934Srgrimes#endif SOFTWARE_SSTEP 1944Srgrimes } 1954Srgrimes 1964Srgrimes if (db_run_mode == STEP_CONTINUE) { 1974Srgrimes if (watchpt || db_find_breakpoint_here(pc)) { 1984Srgrimes /* 1994Srgrimes * Step over breakpoint/watchpoint. 2004Srgrimes */ 2014Srgrimes db_run_mode = STEP_INVISIBLE; 2024Srgrimes db_set_single_step(DDB_REGS); 2034Srgrimes } else { 2044Srgrimes db_set_breakpoints(); 2054Srgrimes db_set_watchpoints(); 2064Srgrimes } 2074Srgrimes } else { 2084Srgrimes db_set_single_step(DDB_REGS); 2094Srgrimes } 2104Srgrimes} 2114Srgrimes 2124Srgrimesvoid 2134Srgrimesdb_single_step(regs) 2144Srgrimes db_regs_t *regs; 2154Srgrimes{ 2164Srgrimes if (db_run_mode == STEP_CONTINUE) { 2174Srgrimes db_run_mode = STEP_INVISIBLE; 2184Srgrimes db_set_single_step(regs); 2194Srgrimes } 2204Srgrimes} 2214Srgrimes 2224Srgrimes#ifdef SOFTWARE_SSTEP 2234Srgrimes/* 2244Srgrimes * Software implementation of single-stepping. 2254Srgrimes * If your machine does not have a trace mode 2264Srgrimes * similar to the vax or sun ones you can use 2274Srgrimes * this implementation, done for the mips. 2284Srgrimes * Just define the above conditional and provide 2294Srgrimes * the functions/macros defined below. 2304Srgrimes * 2314Srgrimes * extern boolean_t 2324Srgrimes * inst_branch(), returns true if the instruction might branch 2334Srgrimes * extern unsigned 2344Srgrimes * branch_taken(), return the address the instruction might 2354Srgrimes * branch to 2364Srgrimes * db_getreg_val(); return the value of a user register, 2374Srgrimes * as indicated in the hardware instruction 2384Srgrimes * encoding, e.g. 8 for r8 2394Srgrimes * 2404Srgrimes * next_instr_address(pc,bd) returns the address of the first 2414Srgrimes * instruction following the one at "pc", 2424Srgrimes * which is either in the taken path of 2434Srgrimes * the branch (bd==1) or not. This is 2444Srgrimes * for machines (mips) with branch delays. 2454Srgrimes * 2464Srgrimes * A single-step may involve at most 2 breakpoints - 2474Srgrimes * one for branch-not-taken and one for branch taken. 2484Srgrimes * If one of these addresses does not already have a breakpoint, 2494Srgrimes * we allocate a breakpoint and save it here. 2504Srgrimes * These breakpoints are deleted on return. 2514Srgrimes */ 2524Srgrimesdb_breakpoint_t db_not_taken_bkpt = 0; 2534Srgrimesdb_breakpoint_t db_taken_bkpt = 0; 2544Srgrimes 2554Srgrimesvoid 2564Srgrimesdb_set_single_step(regs) 2574Srgrimes register db_regs_t *regs; 2584Srgrimes{ 2594Srgrimes db_addr_t pc = PC_REGS(regs); 2604Srgrimes register unsigned inst, brpc; 2614Srgrimes 2624Srgrimes /* 2634Srgrimes * User was stopped at pc, e.g. the instruction 2644Srgrimes * at pc was not executed. 2654Srgrimes */ 2664Srgrimes inst = db_get_value(pc, sizeof(int), FALSE); 2674Srgrimes if (inst_branch(inst) || inst_call(inst)) { 2684Srgrimes extern unsigned getreg_val(); 2694Srgrimes 2704Srgrimes brpc = branch_taken(inst, pc, getreg_val, regs); 2714Srgrimes if (brpc != pc) { /* self-branches are hopeless */ 2724Srgrimes db_taken_bkpt = db_set_temp_breakpoint(brpc); 2734Srgrimes } 2744Srgrimes pc = next_instr_address(pc,1); 2754Srgrimes } 2764Srgrimes pc = next_instr_address(pc,0); 2774Srgrimes db_not_taken_bkpt = db_set_temp_breakpoint(pc); 2784Srgrimes} 2794Srgrimes 2804Srgrimesvoid 2814Srgrimesdb_clear_single_step(regs) 2824Srgrimes db_regs_t *regs; 2834Srgrimes{ 2844Srgrimes register db_breakpoint_t bkpt; 2854Srgrimes 2864Srgrimes if (db_taken_bkpt != 0) { 2874Srgrimes db_delete_temp_breakpoint(db_taken_bkpt); 2884Srgrimes db_taken_bkpt = 0; 2894Srgrimes } 2904Srgrimes if (db_not_taken_bkpt != 0) { 2914Srgrimes db_delete_temp_breakpoint(db_not_taken_bkpt); 2924Srgrimes db_not_taken_bkpt = 0; 2934Srgrimes } 2944Srgrimes} 2954Srgrimes 2964Srgrimes#endif SOFTWARE_SSTEP 2974Srgrimes 2984Srgrimesextern int db_cmd_loop_done; 2994Srgrimes 3004Srgrimes/* single-step */ 3014Srgrimes/*ARGSUSED*/ 3024Srgrimesvoid 3034Srgrimesdb_single_step_cmd(addr, have_addr, count, modif) 3044Srgrimes db_expr_t addr; 3054Srgrimes int have_addr; 3064Srgrimes db_expr_t count; 3074Srgrimes char * modif; 3084Srgrimes{ 3094Srgrimes boolean_t print = FALSE; 3104Srgrimes 3114Srgrimes if (count == -1) 3124Srgrimes count = 1; 3134Srgrimes 3144Srgrimes if (modif[0] == 'p') 3154Srgrimes print = TRUE; 3164Srgrimes 3174Srgrimes db_run_mode = STEP_ONCE; 3184Srgrimes db_loop_count = count; 3194Srgrimes db_sstep_print = print; 3204Srgrimes db_inst_count = 0; 3214Srgrimes db_load_count = 0; 3224Srgrimes db_store_count = 0; 3234Srgrimes 3244Srgrimes db_cmd_loop_done = 1; 3254Srgrimes} 3264Srgrimes 3274Srgrimes/* trace and print until call/return */ 3284Srgrimes/*ARGSUSED*/ 3294Srgrimesvoid 3304Srgrimesdb_trace_until_call_cmd(addr, have_addr, count, modif) 3314Srgrimes db_expr_t addr; 3324Srgrimes int have_addr; 3334Srgrimes db_expr_t count; 3344Srgrimes char * modif; 3354Srgrimes{ 3364Srgrimes boolean_t print = FALSE; 3374Srgrimes 3384Srgrimes if (modif[0] == 'p') 3394Srgrimes print = TRUE; 3404Srgrimes 3414Srgrimes db_run_mode = STEP_CALLT; 3424Srgrimes db_sstep_print = print; 3434Srgrimes db_inst_count = 0; 3444Srgrimes db_load_count = 0; 3454Srgrimes db_store_count = 0; 3464Srgrimes 3474Srgrimes db_cmd_loop_done = 1; 3484Srgrimes} 3494Srgrimes 3504Srgrimes/*ARGSUSED*/ 3514Srgrimesvoid 3524Srgrimesdb_trace_until_matching_cmd(addr, have_addr, count, modif) 3534Srgrimes db_expr_t addr; 3544Srgrimes int have_addr; 3554Srgrimes db_expr_t count; 3564Srgrimes char * modif; 3574Srgrimes{ 3584Srgrimes boolean_t print = FALSE; 3594Srgrimes 3604Srgrimes if (modif[0] == 'p') 3614Srgrimes print = TRUE; 3624Srgrimes 3634Srgrimes db_run_mode = STEP_RETURN; 3644Srgrimes db_call_depth = 1; 3654Srgrimes db_sstep_print = print; 3664Srgrimes db_inst_count = 0; 3674Srgrimes db_load_count = 0; 3684Srgrimes db_store_count = 0; 3694Srgrimes 3704Srgrimes db_cmd_loop_done = 1; 3714Srgrimes} 3724Srgrimes 3734Srgrimes/* continue */ 3744Srgrimes/*ARGSUSED*/ 3754Srgrimesvoid 3764Srgrimesdb_continue_cmd(addr, have_addr, count, modif) 3774Srgrimes db_expr_t addr; 3784Srgrimes int have_addr; 3794Srgrimes db_expr_t count; 3804Srgrimes char * modif; 3814Srgrimes{ 3824Srgrimes if (modif[0] == 'c') 3834Srgrimes db_run_mode = STEP_COUNT; 3844Srgrimes else 3854Srgrimes db_run_mode = STEP_CONTINUE; 3864Srgrimes db_inst_count = 0; 3874Srgrimes db_load_count = 0; 3884Srgrimes db_store_count = 0; 3894Srgrimes 3904Srgrimes db_cmd_loop_done = 1; 3914Srgrimes} 392