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