1139747Simp/*-
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 */
264Srgrimes/*
274Srgrimes * 	Author: David B. Golub, Carnegie Mellon University
284Srgrimes *	Date:	7/90
294Srgrimes */
304Srgrimes
314Srgrimes/*
324Srgrimes * Commands to run process.
334Srgrimes */
34116176Sobrien
35116176Sobrien#include <sys/cdefs.h>
36116176Sobrien__FBSDID("$FreeBSD$");
37116176Sobrien
382056Swollman#include <sys/param.h>
39131952Smarcel#include <sys/kdb.h>
40145053Speter#include <sys/proc.h>
4112734Sbde
42131952Smarcel#include <machine/kdb.h>
43131952Smarcel#include <machine/pcb.h>
44131952Smarcel
4512662Sdg#include <vm/vm.h>
4612734Sbde
472056Swollman#include <ddb/ddb.h>
484Srgrimes#include <ddb/db_break.h>
494Srgrimes#include <ddb/db_access.h>
504Srgrimes
5112720Sphkstatic int	db_run_mode;
524Srgrimes#define	STEP_NONE	0
534Srgrimes#define	STEP_ONCE	1
544Srgrimes#define	STEP_RETURN	2
554Srgrimes#define	STEP_CALLT	3
564Srgrimes#define	STEP_CONTINUE	4
574Srgrimes#define STEP_INVISIBLE	5
584Srgrimes#define	STEP_COUNT	6
594Srgrimes
6012720Sphkstatic boolean_t	db_sstep_print;
6112720Sphkstatic int		db_loop_count;
6212720Sphkstatic int		db_call_depth;
634Srgrimes
644Srgrimesint		db_inst_count;
654Srgrimesint		db_load_count;
664Srgrimesint		db_store_count;
674Srgrimes
684Srgrimes#ifndef db_set_single_step
69131952Smarcelvoid db_set_single_step(void);
704Srgrimes#endif
714Srgrimes#ifndef db_clear_single_step
72131952Smarcelvoid db_clear_single_step(void);
734Srgrimes#endif
744Srgrimes
75137974Scognet#ifdef SOFTWARE_SSTEP
76137974Scognetdb_breakpoint_t	db_not_taken_bkpt = 0;
77137974Scognetdb_breakpoint_t	db_taken_bkpt = 0;
78137974Scognet#endif
79137974Scognet
804Srgrimesboolean_t
814Srgrimesdb_stop_at_pc(is_breakpoint)
824Srgrimes	boolean_t	*is_breakpoint;
834Srgrimes{
844Srgrimes	register db_addr_t	pc;
854Srgrimes	register db_breakpoint_t bkpt;
864Srgrimes
87137974Scognet	pc = PC_REGS();
88137974Scognet#ifdef SOFTWARE_SSTEP
89137974Scognet	if ((db_not_taken_bkpt != 0 && pc == db_not_taken_bkpt->address)
90137974Scognet	    || (db_taken_bkpt != 0 && pc == db_taken_bkpt->address))
91137974Scognet		*is_breakpoint = FALSE;
92137974Scognet#endif
93137974Scognet
94131952Smarcel	db_clear_single_step();
954Srgrimes	db_clear_breakpoints();
964Srgrimes	db_clear_watchpoints();
974Srgrimes
984Srgrimes#ifdef	FIXUP_PC_AFTER_BREAK
994Srgrimes	if (*is_breakpoint) {
1004Srgrimes	    /*
1014Srgrimes	     * Breakpoint trap.  Fix up the PC if the
1024Srgrimes	     * machine requires it.
1034Srgrimes	     */
1044Srgrimes	    FIXUP_PC_AFTER_BREAK
105131952Smarcel	    pc = PC_REGS();
1064Srgrimes	}
1074Srgrimes#endif
1084Srgrimes
1094Srgrimes	/*
1104Srgrimes	 * Now check for a breakpoint at this address.
1114Srgrimes	 */
1124Srgrimes	bkpt = db_find_breakpoint_here(pc);
1134Srgrimes	if (bkpt) {
1144Srgrimes	    if (--bkpt->count == 0) {
1154Srgrimes		bkpt->count = bkpt->init_count;
1164Srgrimes		*is_breakpoint = TRUE;
1174Srgrimes		return (TRUE);	/* stop here */
1184Srgrimes	    }
1194Srgrimes	} else if (*is_breakpoint) {
12083506Sdfr#ifdef BKPT_SKIP
12183506Sdfr		BKPT_SKIP;
12236735Sdfr#endif
1234Srgrimes	}
1248876Srgrimes
1254Srgrimes	*is_breakpoint = FALSE;
1264Srgrimes
1274Srgrimes	if (db_run_mode == STEP_INVISIBLE) {
1284Srgrimes	    db_run_mode = STEP_CONTINUE;
1294Srgrimes	    return (FALSE);	/* continue */
1304Srgrimes	}
1314Srgrimes	if (db_run_mode == STEP_COUNT) {
1324Srgrimes	    return (FALSE); /* continue */
1334Srgrimes	}
1344Srgrimes	if (db_run_mode == STEP_ONCE) {
1354Srgrimes	    if (--db_loop_count > 0) {
1364Srgrimes		if (db_sstep_print) {
1374Srgrimes		    db_printf("\t\t");
1384Srgrimes		    db_print_loc_and_inst(pc);
1394Srgrimes		    db_printf("\n");
1404Srgrimes		}
1414Srgrimes		return (FALSE);	/* continue */
1424Srgrimes	    }
1434Srgrimes	}
1444Srgrimes	if (db_run_mode == STEP_RETURN) {
1454Srgrimes	    /* continue until matching return */
14698694Smux	    db_expr_t ins;
1474Srgrimes
14898694Smux	    ins = db_get_value(pc, sizeof(int), FALSE);
1494Srgrimes	    if (!inst_trap_return(ins) &&
1504Srgrimes		(!inst_return(ins) || --db_call_depth != 0)) {
1514Srgrimes		if (db_sstep_print) {
1524Srgrimes		    if (inst_call(ins) || inst_return(ins)) {
1534Srgrimes			register int i;
1544Srgrimes
1554Srgrimes			db_printf("[after %6d]     ", db_inst_count);
1564Srgrimes			for (i = db_call_depth; --i > 0; )
1574Srgrimes			    db_printf("  ");
1584Srgrimes			db_print_loc_and_inst(pc);
1594Srgrimes			db_printf("\n");
1604Srgrimes		    }
1614Srgrimes		}
1624Srgrimes		if (inst_call(ins))
1634Srgrimes		    db_call_depth++;
1644Srgrimes		return (FALSE);	/* continue */
1654Srgrimes	    }
1664Srgrimes	}
1674Srgrimes	if (db_run_mode == STEP_CALLT) {
1684Srgrimes	    /* continue until call or return */
16998694Smux	    db_expr_t ins;
1704Srgrimes
17198694Smux	    ins = db_get_value(pc, sizeof(int), FALSE);
1724Srgrimes	    if (!inst_call(ins) &&
1734Srgrimes		!inst_return(ins) &&
1744Srgrimes		!inst_trap_return(ins)) {
1754Srgrimes		return (FALSE);	/* continue */
1764Srgrimes	    }
1774Srgrimes	}
1784Srgrimes	db_run_mode = STEP_NONE;
1794Srgrimes	return (TRUE);
1804Srgrimes}
1814Srgrimes
1824Srgrimesvoid
1834Srgrimesdb_restart_at_pc(watchpt)
1844Srgrimes	boolean_t watchpt;
1854Srgrimes{
186131952Smarcel	register db_addr_t	pc = PC_REGS();
1874Srgrimes
1884Srgrimes	if ((db_run_mode == STEP_COUNT) ||
1894Srgrimes	    (db_run_mode == STEP_RETURN) ||
1904Srgrimes	    (db_run_mode == STEP_CALLT)) {
1914Srgrimes	    db_expr_t		ins;
1924Srgrimes
1934Srgrimes	    /*
1944Srgrimes	     * We are about to execute this instruction,
1954Srgrimes	     * so count it now.
1964Srgrimes	     */
1974Srgrimes
1984Srgrimes	    ins = db_get_value(pc, sizeof(int), FALSE);
1994Srgrimes	    db_inst_count++;
2004Srgrimes	    db_load_count += inst_load(ins);
2014Srgrimes	    db_store_count += inst_store(ins);
2024Srgrimes#ifdef	SOFTWARE_SSTEP
2034Srgrimes	    /* XXX works on mips, but... */
2044Srgrimes	    if (inst_branch(ins) || inst_call(ins)) {
2054Srgrimes		ins = db_get_value(next_instr_address(pc,1),
2064Srgrimes				   sizeof(int), FALSE);
2074Srgrimes		db_inst_count++;
2084Srgrimes		db_load_count += inst_load(ins);
2094Srgrimes		db_store_count += inst_store(ins);
2104Srgrimes	    }
21181670Sobrien#endif	/* SOFTWARE_SSTEP */
2124Srgrimes	}
2134Srgrimes
2144Srgrimes	if (db_run_mode == STEP_CONTINUE) {
2154Srgrimes	    if (watchpt || db_find_breakpoint_here(pc)) {
2164Srgrimes		/*
2174Srgrimes		 * Step over breakpoint/watchpoint.
2184Srgrimes		 */
2194Srgrimes		db_run_mode = STEP_INVISIBLE;
220131952Smarcel		db_set_single_step();
2214Srgrimes	    } else {
2224Srgrimes		db_set_breakpoints();
2234Srgrimes		db_set_watchpoints();
2244Srgrimes	    }
2254Srgrimes	} else {
226131952Smarcel	    db_set_single_step();
2274Srgrimes	}
2284Srgrimes}
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 */
2604Srgrimes
2614Srgrimesvoid
262131952Smarceldb_set_single_step(void)
2634Srgrimes{
264131952Smarcel	db_addr_t pc = PC_REGS(), brpc;
265131952Smarcel	unsigned inst;
2664Srgrimes
2674Srgrimes	/*
2684Srgrimes	 *	User was stopped at pc, e.g. the instruction
2694Srgrimes	 *	at pc was not executed.
2704Srgrimes	 */
2714Srgrimes	inst = db_get_value(pc, sizeof(int), FALSE);
272181175Scognet	if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) {
273131952Smarcel		brpc = branch_taken(inst, pc);
274131952Smarcel		if (brpc != pc) {	/* self-branches are hopeless */
275131952Smarcel			db_taken_bkpt = db_set_temp_breakpoint(brpc);
276131952Smarcel		}
277131952Smarcel		pc = next_instr_address(pc, 1);
2784Srgrimes	}
279131952Smarcel	pc = next_instr_address(pc, 0);
2804Srgrimes	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
2814Srgrimes}
2824Srgrimes
2834Srgrimesvoid
284131952Smarceldb_clear_single_step(void)
2854Srgrimes{
2864Srgrimes
28737392Sdfr	if (db_not_taken_bkpt != 0) {
288131952Smarcel		db_delete_temp_breakpoint(db_not_taken_bkpt);
289131952Smarcel		db_not_taken_bkpt = 0;
29037392Sdfr	}
2914Srgrimes	if (db_taken_bkpt != 0) {
292131952Smarcel		db_delete_temp_breakpoint(db_taken_bkpt);
293131952Smarcel		db_taken_bkpt = 0;
2944Srgrimes	}
2954Srgrimes}
2964Srgrimes
29781670Sobrien#endif	/* SOFTWARE_SSTEP */
2984Srgrimes
2994Srgrimesextern int	db_cmd_loop_done;
3004Srgrimes
3014Srgrimes/* single-step */
3024Srgrimes/*ARGSUSED*/
3034Srgrimesvoid
3044Srgrimesdb_single_step_cmd(addr, have_addr, count, modif)
3054Srgrimes	db_expr_t	addr;
30612473Sbde	boolean_t	have_addr;
3074Srgrimes	db_expr_t	count;
3084Srgrimes	char *		modif;
3094Srgrimes{
3104Srgrimes	boolean_t	print = FALSE;
3114Srgrimes
3124Srgrimes	if (count == -1)
3134Srgrimes	    count = 1;
3144Srgrimes
3154Srgrimes	if (modif[0] == 'p')
3164Srgrimes	    print = TRUE;
3174Srgrimes
3184Srgrimes	db_run_mode = STEP_ONCE;
3194Srgrimes	db_loop_count = count;
3204Srgrimes	db_sstep_print = print;
3214Srgrimes	db_inst_count = 0;
3224Srgrimes	db_load_count = 0;
3234Srgrimes	db_store_count = 0;
3244Srgrimes
3254Srgrimes	db_cmd_loop_done = 1;
3264Srgrimes}
3274Srgrimes
3284Srgrimes/* trace and print until call/return */
3294Srgrimes/*ARGSUSED*/
3304Srgrimesvoid
3314Srgrimesdb_trace_until_call_cmd(addr, have_addr, count, modif)
3324Srgrimes	db_expr_t	addr;
33312473Sbde	boolean_t	have_addr;
3344Srgrimes	db_expr_t	count;
3354Srgrimes	char *		modif;
3364Srgrimes{
3374Srgrimes	boolean_t	print = FALSE;
3384Srgrimes
3394Srgrimes	if (modif[0] == 'p')
3404Srgrimes	    print = TRUE;
3414Srgrimes
3424Srgrimes	db_run_mode = STEP_CALLT;
3434Srgrimes	db_sstep_print = print;
3444Srgrimes	db_inst_count = 0;
3454Srgrimes	db_load_count = 0;
3464Srgrimes	db_store_count = 0;
3474Srgrimes
3484Srgrimes	db_cmd_loop_done = 1;
3494Srgrimes}
3504Srgrimes
3514Srgrimes/*ARGSUSED*/
3524Srgrimesvoid
3534Srgrimesdb_trace_until_matching_cmd(addr, have_addr, count, modif)
3544Srgrimes	db_expr_t	addr;
35512473Sbde	boolean_t	have_addr;
3564Srgrimes	db_expr_t	count;
3574Srgrimes	char *		modif;
3584Srgrimes{
3594Srgrimes	boolean_t	print = FALSE;
3604Srgrimes
3614Srgrimes	if (modif[0] == 'p')
3624Srgrimes	    print = TRUE;
3634Srgrimes
3644Srgrimes	db_run_mode = STEP_RETURN;
3654Srgrimes	db_call_depth = 1;
3664Srgrimes	db_sstep_print = print;
3674Srgrimes	db_inst_count = 0;
3684Srgrimes	db_load_count = 0;
3694Srgrimes	db_store_count = 0;
3704Srgrimes
3714Srgrimes	db_cmd_loop_done = 1;
3724Srgrimes}
3734Srgrimes
3744Srgrimes/* continue */
3754Srgrimes/*ARGSUSED*/
3764Srgrimesvoid
3774Srgrimesdb_continue_cmd(addr, have_addr, count, modif)
3784Srgrimes	db_expr_t	addr;
37912473Sbde	boolean_t	have_addr;
3804Srgrimes	db_expr_t	count;
3814Srgrimes	char *		modif;
3824Srgrimes{
3834Srgrimes	if (modif[0] == 'c')
3844Srgrimes	    db_run_mode = STEP_COUNT;
3854Srgrimes	else
3864Srgrimes	    db_run_mode = STEP_CONTINUE;
3874Srgrimes	db_inst_count = 0;
3884Srgrimes	db_load_count = 0;
3894Srgrimes	db_store_count = 0;
3904Srgrimes
3914Srgrimes	db_cmd_loop_done = 1;
3924Srgrimes}
393