db_run.c revision 12720
18876Srgrimes/*
24Srgrimes * Mach Operating System
34Srgrimes * Copyright (c) 1991,1990 Carnegie Mellon University
44Srgrimes * All Rights Reserved.
58876Srgrimes *
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.
118876Srgrimes *
128876Srgrimes * 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.
158876Srgrimes *
164Srgrimes * Carnegie Mellon requests users of this software to return to
178876Srgrimes *
184Srgrimes *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
194Srgrimes *  School of Computer Science
204Srgrimes *  Carnegie Mellon University
214Srgrimes *  Pittsburgh PA 15213-3890
228876Srgrimes *
234Srgrimes * any improvements or extensions that they make and grant Carnegie the
244Srgrimes * rights to redistribute these changes.
254Srgrimes *
2612720Sphk *	$Id: db_run.c,v 1.8 1995/12/07 12:44:57 davidg 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>
4012662Sdg#include <vm/vm.h>
4112662Sdg#include <vm/vm_param.h>
422056Swollman#include <ddb/ddb.h>
434Srgrimes
444Srgrimes#include <ddb/db_lex.h>
454Srgrimes#include <ddb/db_break.h>
464Srgrimes#include <ddb/db_access.h>
474Srgrimes
4812720Sphkstatic int	db_run_mode;
494Srgrimes#define	STEP_NONE	0
504Srgrimes#define	STEP_ONCE	1
514Srgrimes#define	STEP_RETURN	2
524Srgrimes#define	STEP_CALLT	3
534Srgrimes#define	STEP_CONTINUE	4
544Srgrimes#define STEP_INVISIBLE	5
554Srgrimes#define	STEP_COUNT	6
564Srgrimes
5712720Sphkstatic boolean_t	db_sstep_print;
5812720Sphkstatic int		db_loop_count;
5912720Sphkstatic int		db_call_depth;
604Srgrimes
614Srgrimesint		db_inst_count;
624Srgrimesint		db_load_count;
634Srgrimesint		db_store_count;
644Srgrimes
654Srgrimes#ifndef db_set_single_step
6612473Sbdeextern void	db_set_single_step __P((db_regs_t *regs);
674Srgrimes#endif
684Srgrimes#ifndef db_clear_single_step
6912473Sbdeextern void	db_clear_single_step __P((db_regs_t *regs));
704Srgrimes#endif
714Srgrimes
7212515Sphk#ifdef notused
7312515Sphkstatic void	db_single_step __P((db_regs_t *regs));
7412515Sphk#endif
7512515Sphk
764Srgrimesboolean_t
774Srgrimesdb_stop_at_pc(is_breakpoint)
784Srgrimes	boolean_t	*is_breakpoint;
794Srgrimes{
804Srgrimes	register db_addr_t	pc;
814Srgrimes	register db_breakpoint_t bkpt;
824Srgrimes
834Srgrimes	db_clear_single_step(DDB_REGS);
844Srgrimes	db_clear_breakpoints();
854Srgrimes	db_clear_watchpoints();
864Srgrimes	pc = PC_REGS(DDB_REGS);
874Srgrimes
884Srgrimes#ifdef	FIXUP_PC_AFTER_BREAK
894Srgrimes	if (*is_breakpoint) {
904Srgrimes	    /*
914Srgrimes	     * Breakpoint trap.  Fix up the PC if the
924Srgrimes	     * machine requires it.
934Srgrimes	     */
944Srgrimes	    FIXUP_PC_AFTER_BREAK
954Srgrimes	    pc = PC_REGS(DDB_REGS);
964Srgrimes	}
974Srgrimes#endif
984Srgrimes
994Srgrimes	/*
1004Srgrimes	 * Now check for a breakpoint at this address.
1014Srgrimes	 */
1024Srgrimes	bkpt = db_find_breakpoint_here(pc);
1034Srgrimes	if (bkpt) {
1044Srgrimes	    if (--bkpt->count == 0) {
1054Srgrimes		bkpt->count = bkpt->init_count;
1064Srgrimes		*is_breakpoint = TRUE;
1074Srgrimes		return (TRUE);	/* stop here */
1084Srgrimes	    }
1094Srgrimes	} else if (*is_breakpoint) {
1104Srgrimes		ddb_regs.tf_eip += 1;
1114Srgrimes	}
1128876Srgrimes
1134Srgrimes	*is_breakpoint = FALSE;
1144Srgrimes
1154Srgrimes	if (db_run_mode == STEP_INVISIBLE) {
1164Srgrimes	    db_run_mode = STEP_CONTINUE;
1174Srgrimes	    return (FALSE);	/* continue */
1184Srgrimes	}
1194Srgrimes	if (db_run_mode == STEP_COUNT) {
1204Srgrimes	    return (FALSE); /* continue */
1214Srgrimes	}
1224Srgrimes	if (db_run_mode == STEP_ONCE) {
1234Srgrimes	    if (--db_loop_count > 0) {
1244Srgrimes		if (db_sstep_print) {
1254Srgrimes		    db_printf("\t\t");
1264Srgrimes		    db_print_loc_and_inst(pc);
1274Srgrimes		    db_printf("\n");
1284Srgrimes		}
1294Srgrimes		return (FALSE);	/* continue */
1304Srgrimes	    }
1314Srgrimes	}
1324Srgrimes	if (db_run_mode == STEP_RETURN) {
1334Srgrimes	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
1344Srgrimes
1354Srgrimes	    /* continue until matching return */
1364Srgrimes
1374Srgrimes	    if (!inst_trap_return(ins) &&
1384Srgrimes		(!inst_return(ins) || --db_call_depth != 0)) {
1394Srgrimes		if (db_sstep_print) {
1404Srgrimes		    if (inst_call(ins) || inst_return(ins)) {
1414Srgrimes			register int i;
1424Srgrimes
1434Srgrimes			db_printf("[after %6d]     ", db_inst_count);
1444Srgrimes			for (i = db_call_depth; --i > 0; )
1454Srgrimes			    db_printf("  ");
1464Srgrimes			db_print_loc_and_inst(pc);
1474Srgrimes			db_printf("\n");
1484Srgrimes		    }
1494Srgrimes		}
1504Srgrimes		if (inst_call(ins))
1514Srgrimes		    db_call_depth++;
1524Srgrimes		return (FALSE);	/* continue */
1534Srgrimes	    }
1544Srgrimes	}
1554Srgrimes	if (db_run_mode == STEP_CALLT) {
1564Srgrimes	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
1574Srgrimes
1584Srgrimes	    /* continue until call or return */
1594Srgrimes
1604Srgrimes	    if (!inst_call(ins) &&
1614Srgrimes		!inst_return(ins) &&
1624Srgrimes		!inst_trap_return(ins)) {
1634Srgrimes		return (FALSE);	/* continue */
1644Srgrimes	    }
1654Srgrimes	}
1664Srgrimes	db_run_mode = STEP_NONE;
1674Srgrimes	return (TRUE);
1684Srgrimes}
1694Srgrimes
1704Srgrimesvoid
1714Srgrimesdb_restart_at_pc(watchpt)
1724Srgrimes	boolean_t watchpt;
1734Srgrimes{
1744Srgrimes	register db_addr_t	pc = PC_REGS(DDB_REGS);
1754Srgrimes
1764Srgrimes	if ((db_run_mode == STEP_COUNT) ||
1774Srgrimes	    (db_run_mode == STEP_RETURN) ||
1784Srgrimes	    (db_run_mode == STEP_CALLT)) {
1794Srgrimes	    db_expr_t		ins;
1804Srgrimes
1814Srgrimes	    /*
1824Srgrimes	     * We are about to execute this instruction,
1834Srgrimes	     * so count it now.
1844Srgrimes	     */
1854Srgrimes
1864Srgrimes	    ins = db_get_value(pc, sizeof(int), FALSE);
1874Srgrimes	    db_inst_count++;
1884Srgrimes	    db_load_count += inst_load(ins);
1894Srgrimes	    db_store_count += inst_store(ins);
1904Srgrimes#ifdef	SOFTWARE_SSTEP
1914Srgrimes	    /* XXX works on mips, but... */
1924Srgrimes	    if (inst_branch(ins) || inst_call(ins)) {
1934Srgrimes		ins = db_get_value(next_instr_address(pc,1),
1944Srgrimes				   sizeof(int), FALSE);
1954Srgrimes		db_inst_count++;
1964Srgrimes		db_load_count += inst_load(ins);
1974Srgrimes		db_store_count += inst_store(ins);
1984Srgrimes	    }
1994Srgrimes#endif	SOFTWARE_SSTEP
2004Srgrimes	}
2014Srgrimes
2024Srgrimes	if (db_run_mode == STEP_CONTINUE) {
2034Srgrimes	    if (watchpt || db_find_breakpoint_here(pc)) {
2044Srgrimes		/*
2054Srgrimes		 * Step over breakpoint/watchpoint.
2064Srgrimes		 */
2074Srgrimes		db_run_mode = STEP_INVISIBLE;
2084Srgrimes		db_set_single_step(DDB_REGS);
2094Srgrimes	    } else {
2104Srgrimes		db_set_breakpoints();
2114Srgrimes		db_set_watchpoints();
2124Srgrimes	    }
2134Srgrimes	} else {
2144Srgrimes	    db_set_single_step(DDB_REGS);
2154Srgrimes	}
2164Srgrimes}
2174Srgrimes
21812515Sphk#ifdef notused
21912515Sphkstatic void
2204Srgrimesdb_single_step(regs)
2214Srgrimes	db_regs_t *regs;
2224Srgrimes{
2234Srgrimes	if (db_run_mode == STEP_CONTINUE) {
2244Srgrimes	    db_run_mode = STEP_INVISIBLE;
2254Srgrimes	    db_set_single_step(regs);
2264Srgrimes	}
2274Srgrimes}
22812515Sphk#endif
2294Srgrimes
2304Srgrimes#ifdef	SOFTWARE_SSTEP
2314Srgrimes/*
2324Srgrimes *	Software implementation of single-stepping.
2334Srgrimes *	If your machine does not have a trace mode
2344Srgrimes *	similar to the vax or sun ones you can use
2354Srgrimes *	this implementation, done for the mips.
2364Srgrimes *	Just define the above conditional and provide
2374Srgrimes *	the functions/macros defined below.
2384Srgrimes *
2394Srgrimes * extern boolean_t
2404Srgrimes *	inst_branch(),		returns true if the instruction might branch
2414Srgrimes * extern unsigned
2424Srgrimes *	branch_taken(),		return the address the instruction might
2434Srgrimes *				branch to
2444Srgrimes *	db_getreg_val();	return the value of a user register,
2454Srgrimes *				as indicated in the hardware instruction
2464Srgrimes *				encoding, e.g. 8 for r8
2478876Srgrimes *
2484Srgrimes * next_instr_address(pc,bd)	returns the address of the first
2494Srgrimes *				instruction following the one at "pc",
2504Srgrimes *				which is either in the taken path of
2514Srgrimes *				the branch (bd==1) or not.  This is
2524Srgrimes *				for machines (mips) with branch delays.
2534Srgrimes *
2544Srgrimes *	A single-step may involve at most 2 breakpoints -
2554Srgrimes *	one for branch-not-taken and one for branch taken.
2564Srgrimes *	If one of these addresses does not already have a breakpoint,
2574Srgrimes *	we allocate a breakpoint and save it here.
2584Srgrimes *	These breakpoints are deleted on return.
2598876Srgrimes */
2604Srgrimesdb_breakpoint_t	db_not_taken_bkpt = 0;
2614Srgrimesdb_breakpoint_t	db_taken_bkpt = 0;
2624Srgrimes
2634Srgrimesvoid
2644Srgrimesdb_set_single_step(regs)
2654Srgrimes	register db_regs_t *regs;
2664Srgrimes{
2674Srgrimes	db_addr_t pc = PC_REGS(regs);
2684Srgrimes	register unsigned	 inst, brpc;
2694Srgrimes
2704Srgrimes	/*
2714Srgrimes	 *	User was stopped at pc, e.g. the instruction
2724Srgrimes	 *	at pc was not executed.
2734Srgrimes	 */
2744Srgrimes	inst = db_get_value(pc, sizeof(int), FALSE);
2754Srgrimes	if (inst_branch(inst) || inst_call(inst)) {
2764Srgrimes	    extern unsigned getreg_val();
2774Srgrimes
2784Srgrimes	    brpc = branch_taken(inst, pc, getreg_val, regs);
2794Srgrimes	    if (brpc != pc) {	/* self-branches are hopeless */
2804Srgrimes		db_taken_bkpt = db_set_temp_breakpoint(brpc);
2814Srgrimes	    }
2824Srgrimes	    pc = next_instr_address(pc,1);
2834Srgrimes	}
2844Srgrimes	pc = next_instr_address(pc,0);
2854Srgrimes	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
2864Srgrimes}
2874Srgrimes
2884Srgrimesvoid
2894Srgrimesdb_clear_single_step(regs)
2904Srgrimes	db_regs_t *regs;
2914Srgrimes{
2924Srgrimes	register db_breakpoint_t	bkpt;
2934Srgrimes
2944Srgrimes	if (db_taken_bkpt != 0) {
2954Srgrimes	    db_delete_temp_breakpoint(db_taken_bkpt);
2964Srgrimes	    db_taken_bkpt = 0;
2974Srgrimes	}
2984Srgrimes	if (db_not_taken_bkpt != 0) {
2994Srgrimes	    db_delete_temp_breakpoint(db_not_taken_bkpt);
3004Srgrimes	    db_not_taken_bkpt = 0;
3014Srgrimes	}
3024Srgrimes}
3034Srgrimes
3044Srgrimes#endif	SOFTWARE_SSTEP
3054Srgrimes
3064Srgrimesextern int	db_cmd_loop_done;
3074Srgrimes
3084Srgrimes/* single-step */
3094Srgrimes/*ARGSUSED*/
3104Srgrimesvoid
3114Srgrimesdb_single_step_cmd(addr, have_addr, count, modif)
3124Srgrimes	db_expr_t	addr;
31312473Sbde	boolean_t	have_addr;
3144Srgrimes	db_expr_t	count;
3154Srgrimes	char *		modif;
3164Srgrimes{
3174Srgrimes	boolean_t	print = FALSE;
3184Srgrimes
3194Srgrimes	if (count == -1)
3204Srgrimes	    count = 1;
3214Srgrimes
3224Srgrimes	if (modif[0] == 'p')
3234Srgrimes	    print = TRUE;
3244Srgrimes
3254Srgrimes	db_run_mode = STEP_ONCE;
3264Srgrimes	db_loop_count = count;
3274Srgrimes	db_sstep_print = print;
3284Srgrimes	db_inst_count = 0;
3294Srgrimes	db_load_count = 0;
3304Srgrimes	db_store_count = 0;
3314Srgrimes
3324Srgrimes	db_cmd_loop_done = 1;
3334Srgrimes}
3344Srgrimes
3354Srgrimes/* trace and print until call/return */
3364Srgrimes/*ARGSUSED*/
3374Srgrimesvoid
3384Srgrimesdb_trace_until_call_cmd(addr, have_addr, count, modif)
3394Srgrimes	db_expr_t	addr;
34012473Sbde	boolean_t	have_addr;
3414Srgrimes	db_expr_t	count;
3424Srgrimes	char *		modif;
3434Srgrimes{
3444Srgrimes	boolean_t	print = FALSE;
3454Srgrimes
3464Srgrimes	if (modif[0] == 'p')
3474Srgrimes	    print = TRUE;
3484Srgrimes
3494Srgrimes	db_run_mode = STEP_CALLT;
3504Srgrimes	db_sstep_print = print;
3514Srgrimes	db_inst_count = 0;
3524Srgrimes	db_load_count = 0;
3534Srgrimes	db_store_count = 0;
3544Srgrimes
3554Srgrimes	db_cmd_loop_done = 1;
3564Srgrimes}
3574Srgrimes
3584Srgrimes/*ARGSUSED*/
3594Srgrimesvoid
3604Srgrimesdb_trace_until_matching_cmd(addr, have_addr, count, modif)
3614Srgrimes	db_expr_t	addr;
36212473Sbde	boolean_t	have_addr;
3634Srgrimes	db_expr_t	count;
3644Srgrimes	char *		modif;
3654Srgrimes{
3664Srgrimes	boolean_t	print = FALSE;
3674Srgrimes
3684Srgrimes	if (modif[0] == 'p')
3694Srgrimes	    print = TRUE;
3704Srgrimes
3714Srgrimes	db_run_mode = STEP_RETURN;
3724Srgrimes	db_call_depth = 1;
3734Srgrimes	db_sstep_print = print;
3744Srgrimes	db_inst_count = 0;
3754Srgrimes	db_load_count = 0;
3764Srgrimes	db_store_count = 0;
3774Srgrimes
3784Srgrimes	db_cmd_loop_done = 1;
3794Srgrimes}
3804Srgrimes
3814Srgrimes/* continue */
3824Srgrimes/*ARGSUSED*/
3834Srgrimesvoid
3844Srgrimesdb_continue_cmd(addr, have_addr, count, modif)
3854Srgrimes	db_expr_t	addr;
38612473Sbde	boolean_t	have_addr;
3874Srgrimes	db_expr_t	count;
3884Srgrimes	char *		modif;
3894Srgrimes{
3904Srgrimes	if (modif[0] == 'c')
3914Srgrimes	    db_run_mode = STEP_COUNT;
3924Srgrimes	else
3934Srgrimes	    db_run_mode = STEP_CONTINUE;
3944Srgrimes	db_inst_count = 0;
3954Srgrimes	db_load_count = 0;
3964Srgrimes	db_store_count = 0;
3974Srgrimes
3984Srgrimes	db_cmd_loop_done = 1;
3994Srgrimes}
400