db_run.c revision 37392
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 *
2637392Sdfr *	$Id: db_run.c,v 1.15 1998/06/28 00:52:50 dfr 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>
3812734Sbde
3912662Sdg#include <vm/vm.h>
4012734Sbde
412056Swollman#include <ddb/ddb.h>
424Srgrimes#include <ddb/db_break.h>
434Srgrimes#include <ddb/db_access.h>
444Srgrimes
4512720Sphkstatic int	db_run_mode;
464Srgrimes#define	STEP_NONE	0
474Srgrimes#define	STEP_ONCE	1
484Srgrimes#define	STEP_RETURN	2
494Srgrimes#define	STEP_CALLT	3
504Srgrimes#define	STEP_CONTINUE	4
514Srgrimes#define STEP_INVISIBLE	5
524Srgrimes#define	STEP_COUNT	6
534Srgrimes
5412720Sphkstatic boolean_t	db_sstep_print;
5512720Sphkstatic int		db_loop_count;
5612720Sphkstatic int		db_call_depth;
574Srgrimes
584Srgrimesint		db_inst_count;
594Srgrimesint		db_load_count;
604Srgrimesint		db_store_count;
614Srgrimes
624Srgrimes#ifndef db_set_single_step
6336735Sdfrextern void	db_set_single_step __P((db_regs_t *regs));
644Srgrimes#endif
654Srgrimes#ifndef db_clear_single_step
6612473Sbdeextern void	db_clear_single_step __P((db_regs_t *regs));
674Srgrimes#endif
684Srgrimes
6912515Sphk#ifdef notused
7012515Sphkstatic void	db_single_step __P((db_regs_t *regs));
7112515Sphk#endif
7212515Sphk
734Srgrimesboolean_t
744Srgrimesdb_stop_at_pc(is_breakpoint)
754Srgrimes	boolean_t	*is_breakpoint;
764Srgrimes{
774Srgrimes	register db_addr_t	pc;
784Srgrimes	register db_breakpoint_t bkpt;
794Srgrimes
804Srgrimes	db_clear_single_step(DDB_REGS);
814Srgrimes	db_clear_breakpoints();
824Srgrimes	db_clear_watchpoints();
834Srgrimes	pc = PC_REGS(DDB_REGS);
844Srgrimes
854Srgrimes#ifdef	FIXUP_PC_AFTER_BREAK
864Srgrimes	if (*is_breakpoint) {
874Srgrimes	    /*
884Srgrimes	     * Breakpoint trap.  Fix up the PC if the
894Srgrimes	     * machine requires it.
904Srgrimes	     */
914Srgrimes	    FIXUP_PC_AFTER_BREAK
924Srgrimes	    pc = PC_REGS(DDB_REGS);
934Srgrimes	}
944Srgrimes#endif
954Srgrimes
964Srgrimes	/*
974Srgrimes	 * Now check for a breakpoint at this address.
984Srgrimes	 */
994Srgrimes	bkpt = db_find_breakpoint_here(pc);
1004Srgrimes	if (bkpt) {
1014Srgrimes	    if (--bkpt->count == 0) {
1024Srgrimes		bkpt->count = bkpt->init_count;
1034Srgrimes		*is_breakpoint = TRUE;
1044Srgrimes		return (TRUE);	/* stop here */
1054Srgrimes	    }
1064Srgrimes	} else if (*is_breakpoint) {
10736735Sdfr#ifdef __i386__			/* XXx */
1084Srgrimes		ddb_regs.tf_eip += 1;
10936735Sdfr#endif
1104Srgrimes	}
1118876Srgrimes
1124Srgrimes	*is_breakpoint = FALSE;
1134Srgrimes
1144Srgrimes	if (db_run_mode == STEP_INVISIBLE) {
1154Srgrimes	    db_run_mode = STEP_CONTINUE;
1164Srgrimes	    return (FALSE);	/* continue */
1174Srgrimes	}
1184Srgrimes	if (db_run_mode == STEP_COUNT) {
1194Srgrimes	    return (FALSE); /* continue */
1204Srgrimes	}
1214Srgrimes	if (db_run_mode == STEP_ONCE) {
1224Srgrimes	    if (--db_loop_count > 0) {
1234Srgrimes		if (db_sstep_print) {
1244Srgrimes		    db_printf("\t\t");
1254Srgrimes		    db_print_loc_and_inst(pc);
1264Srgrimes		    db_printf("\n");
1274Srgrimes		}
1284Srgrimes		return (FALSE);	/* continue */
1294Srgrimes	    }
1304Srgrimes	}
1314Srgrimes	if (db_run_mode == STEP_RETURN) {
1324Srgrimes	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
1334Srgrimes
1344Srgrimes	    /* continue until matching return */
1354Srgrimes
1364Srgrimes	    if (!inst_trap_return(ins) &&
1374Srgrimes		(!inst_return(ins) || --db_call_depth != 0)) {
1384Srgrimes		if (db_sstep_print) {
1394Srgrimes		    if (inst_call(ins) || inst_return(ins)) {
1404Srgrimes			register int i;
1414Srgrimes
1424Srgrimes			db_printf("[after %6d]     ", db_inst_count);
1434Srgrimes			for (i = db_call_depth; --i > 0; )
1444Srgrimes			    db_printf("  ");
1454Srgrimes			db_print_loc_and_inst(pc);
1464Srgrimes			db_printf("\n");
1474Srgrimes		    }
1484Srgrimes		}
1494Srgrimes		if (inst_call(ins))
1504Srgrimes		    db_call_depth++;
1514Srgrimes		return (FALSE);	/* continue */
1524Srgrimes	    }
1534Srgrimes	}
1544Srgrimes	if (db_run_mode == STEP_CALLT) {
1554Srgrimes	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
1564Srgrimes
1574Srgrimes	    /* continue until call or return */
1584Srgrimes
1594Srgrimes	    if (!inst_call(ins) &&
1604Srgrimes		!inst_return(ins) &&
1614Srgrimes		!inst_trap_return(ins)) {
1624Srgrimes		return (FALSE);	/* continue */
1634Srgrimes	    }
1644Srgrimes	}
1654Srgrimes	db_run_mode = STEP_NONE;
1664Srgrimes	return (TRUE);
1674Srgrimes}
1684Srgrimes
1694Srgrimesvoid
1704Srgrimesdb_restart_at_pc(watchpt)
1714Srgrimes	boolean_t watchpt;
1724Srgrimes{
1734Srgrimes	register db_addr_t	pc = PC_REGS(DDB_REGS);
1744Srgrimes
1754Srgrimes	if ((db_run_mode == STEP_COUNT) ||
1764Srgrimes	    (db_run_mode == STEP_RETURN) ||
1774Srgrimes	    (db_run_mode == STEP_CALLT)) {
1784Srgrimes	    db_expr_t		ins;
1794Srgrimes
1804Srgrimes	    /*
1814Srgrimes	     * We are about to execute this instruction,
1824Srgrimes	     * so count it now.
1834Srgrimes	     */
1844Srgrimes
1854Srgrimes	    ins = db_get_value(pc, sizeof(int), FALSE);
1864Srgrimes	    db_inst_count++;
1874Srgrimes	    db_load_count += inst_load(ins);
1884Srgrimes	    db_store_count += inst_store(ins);
1894Srgrimes#ifdef	SOFTWARE_SSTEP
1904Srgrimes	    /* XXX works on mips, but... */
1914Srgrimes	    if (inst_branch(ins) || inst_call(ins)) {
1924Srgrimes		ins = db_get_value(next_instr_address(pc,1),
1934Srgrimes				   sizeof(int), FALSE);
1944Srgrimes		db_inst_count++;
1954Srgrimes		db_load_count += inst_load(ins);
1964Srgrimes		db_store_count += inst_store(ins);
1974Srgrimes	    }
1984Srgrimes#endif	SOFTWARE_SSTEP
1994Srgrimes	}
2004Srgrimes
2014Srgrimes	if (db_run_mode == STEP_CONTINUE) {
2024Srgrimes	    if (watchpt || db_find_breakpoint_here(pc)) {
2034Srgrimes		/*
2044Srgrimes		 * Step over breakpoint/watchpoint.
2054Srgrimes		 */
2064Srgrimes		db_run_mode = STEP_INVISIBLE;
2074Srgrimes		db_set_single_step(DDB_REGS);
2084Srgrimes	    } else {
2094Srgrimes		db_set_breakpoints();
2104Srgrimes		db_set_watchpoints();
2114Srgrimes	    }
2124Srgrimes	} else {
2134Srgrimes	    db_set_single_step(DDB_REGS);
2144Srgrimes	}
2154Srgrimes}
2164Srgrimes
21712515Sphk#ifdef notused
21812515Sphkstatic void
2194Srgrimesdb_single_step(regs)
2204Srgrimes	db_regs_t *regs;
2214Srgrimes{
2224Srgrimes	if (db_run_mode == STEP_CONTINUE) {
2234Srgrimes	    db_run_mode = STEP_INVISIBLE;
2244Srgrimes	    db_set_single_step(regs);
2254Srgrimes	}
2264Srgrimes}
22712515Sphk#endif
2284Srgrimes
2294Srgrimes#ifdef	SOFTWARE_SSTEP
2304Srgrimes/*
2314Srgrimes *	Software implementation of single-stepping.
2324Srgrimes *	If your machine does not have a trace mode
2334Srgrimes *	similar to the vax or sun ones you can use
2344Srgrimes *	this implementation, done for the mips.
2354Srgrimes *	Just define the above conditional and provide
2364Srgrimes *	the functions/macros defined below.
2374Srgrimes *
2384Srgrimes * extern boolean_t
2394Srgrimes *	inst_branch(),		returns true if the instruction might branch
2404Srgrimes * extern unsigned
2414Srgrimes *	branch_taken(),		return the address the instruction might
2424Srgrimes *				branch to
2434Srgrimes *	db_getreg_val();	return the value of a user register,
2444Srgrimes *				as indicated in the hardware instruction
2454Srgrimes *				encoding, e.g. 8 for r8
2468876Srgrimes *
2474Srgrimes * next_instr_address(pc,bd)	returns the address of the first
2484Srgrimes *				instruction following the one at "pc",
2494Srgrimes *				which is either in the taken path of
2504Srgrimes *				the branch (bd==1) or not.  This is
2514Srgrimes *				for machines (mips) with branch delays.
2524Srgrimes *
2534Srgrimes *	A single-step may involve at most 2 breakpoints -
2544Srgrimes *	one for branch-not-taken and one for branch taken.
2554Srgrimes *	If one of these addresses does not already have a breakpoint,
2564Srgrimes *	we allocate a breakpoint and save it here.
2574Srgrimes *	These breakpoints are deleted on return.
2588876Srgrimes */
2594Srgrimesdb_breakpoint_t	db_not_taken_bkpt = 0;
2604Srgrimesdb_breakpoint_t	db_taken_bkpt = 0;
2614Srgrimes
2624Srgrimesvoid
2634Srgrimesdb_set_single_step(regs)
2644Srgrimes	register db_regs_t *regs;
2654Srgrimes{
26637217Sdfr	db_addr_t pc = PC_REGS(regs), brpc;
26737217Sdfr	 unsigned	 inst;
2684Srgrimes
2694Srgrimes	/*
2704Srgrimes	 *	User was stopped at pc, e.g. the instruction
2714Srgrimes	 *	at pc was not executed.
2724Srgrimes	 */
2734Srgrimes	inst = db_get_value(pc, sizeof(int), FALSE);
2744Srgrimes	if (inst_branch(inst) || inst_call(inst)) {
27536735Sdfr	    brpc = branch_taken(inst, pc, regs);
2764Srgrimes	    if (brpc != pc) {	/* self-branches are hopeless */
2774Srgrimes		db_taken_bkpt = db_set_temp_breakpoint(brpc);
2784Srgrimes	    }
2794Srgrimes	    pc = next_instr_address(pc,1);
2804Srgrimes	}
2814Srgrimes	pc = next_instr_address(pc,0);
2824Srgrimes	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
2834Srgrimes}
2844Srgrimes
2854Srgrimesvoid
2864Srgrimesdb_clear_single_step(regs)
2874Srgrimes	db_regs_t *regs;
2884Srgrimes{
2894Srgrimes	register db_breakpoint_t	bkpt;
2904Srgrimes
29137392Sdfr	if (db_not_taken_bkpt != 0) {
29237392Sdfr	    db_delete_temp_breakpoint(db_not_taken_bkpt);
29337392Sdfr	    db_not_taken_bkpt = 0;
29437392Sdfr	}
2954Srgrimes	if (db_taken_bkpt != 0) {
2964Srgrimes	    db_delete_temp_breakpoint(db_taken_bkpt);
2974Srgrimes	    db_taken_bkpt = 0;
2984Srgrimes	}
2994Srgrimes}
3004Srgrimes
3014Srgrimes#endif	SOFTWARE_SSTEP
3024Srgrimes
3034Srgrimesextern int	db_cmd_loop_done;
3044Srgrimes
3054Srgrimes/* single-step */
3064Srgrimes/*ARGSUSED*/
3074Srgrimesvoid
3084Srgrimesdb_single_step_cmd(addr, have_addr, count, modif)
3094Srgrimes	db_expr_t	addr;
31012473Sbde	boolean_t	have_addr;
3114Srgrimes	db_expr_t	count;
3124Srgrimes	char *		modif;
3134Srgrimes{
3144Srgrimes	boolean_t	print = FALSE;
3154Srgrimes
3164Srgrimes	if (count == -1)
3174Srgrimes	    count = 1;
3184Srgrimes
3194Srgrimes	if (modif[0] == 'p')
3204Srgrimes	    print = TRUE;
3214Srgrimes
3224Srgrimes	db_run_mode = STEP_ONCE;
3234Srgrimes	db_loop_count = count;
3244Srgrimes	db_sstep_print = print;
3254Srgrimes	db_inst_count = 0;
3264Srgrimes	db_load_count = 0;
3274Srgrimes	db_store_count = 0;
3284Srgrimes
3294Srgrimes	db_cmd_loop_done = 1;
3304Srgrimes}
3314Srgrimes
3324Srgrimes/* trace and print until call/return */
3334Srgrimes/*ARGSUSED*/
3344Srgrimesvoid
3354Srgrimesdb_trace_until_call_cmd(addr, have_addr, count, modif)
3364Srgrimes	db_expr_t	addr;
33712473Sbde	boolean_t	have_addr;
3384Srgrimes	db_expr_t	count;
3394Srgrimes	char *		modif;
3404Srgrimes{
3414Srgrimes	boolean_t	print = FALSE;
3424Srgrimes
3434Srgrimes	if (modif[0] == 'p')
3444Srgrimes	    print = TRUE;
3454Srgrimes
3464Srgrimes	db_run_mode = STEP_CALLT;
3474Srgrimes	db_sstep_print = print;
3484Srgrimes	db_inst_count = 0;
3494Srgrimes	db_load_count = 0;
3504Srgrimes	db_store_count = 0;
3514Srgrimes
3524Srgrimes	db_cmd_loop_done = 1;
3534Srgrimes}
3544Srgrimes
3554Srgrimes/*ARGSUSED*/
3564Srgrimesvoid
3574Srgrimesdb_trace_until_matching_cmd(addr, have_addr, count, modif)
3584Srgrimes	db_expr_t	addr;
35912473Sbde	boolean_t	have_addr;
3604Srgrimes	db_expr_t	count;
3614Srgrimes	char *		modif;
3624Srgrimes{
3634Srgrimes	boolean_t	print = FALSE;
3644Srgrimes
3654Srgrimes	if (modif[0] == 'p')
3664Srgrimes	    print = TRUE;
3674Srgrimes
3684Srgrimes	db_run_mode = STEP_RETURN;
3694Srgrimes	db_call_depth = 1;
3704Srgrimes	db_sstep_print = print;
3714Srgrimes	db_inst_count = 0;
3724Srgrimes	db_load_count = 0;
3734Srgrimes	db_store_count = 0;
3744Srgrimes
3754Srgrimes	db_cmd_loop_done = 1;
3764Srgrimes}
3774Srgrimes
3784Srgrimes/* continue */
3794Srgrimes/*ARGSUSED*/
3804Srgrimesvoid
3814Srgrimesdb_continue_cmd(addr, have_addr, count, modif)
3824Srgrimes	db_expr_t	addr;
38312473Sbde	boolean_t	have_addr;
3844Srgrimes	db_expr_t	count;
3854Srgrimes	char *		modif;
3864Srgrimes{
3874Srgrimes	if (modif[0] == 'c')
3884Srgrimes	    db_run_mode = STEP_COUNT;
3894Srgrimes	else
3904Srgrimes	    db_run_mode = STEP_CONTINUE;
3914Srgrimes	db_inst_count = 0;
3924Srgrimes	db_load_count = 0;
3934Srgrimes	db_store_count = 0;
3944Srgrimes
3954Srgrimes	db_cmd_loop_done = 1;
3964Srgrimes}
397