Home | History | Annotate | Line # | Download | only in ddb
db_run.c revision 1.2
      1 /*
      2  * Mach Operating System
      3  * Copyright (c) 1991,1990 Carnegie Mellon University
      4  * All Rights Reserved.
      5  *
      6  * Permission to use, copy, modify and distribute this software and its
      7  * documentation is hereby granted, provided that both the copyright
      8  * notice and this permission notice appear in all copies of the
      9  * software, derivative works or modified versions, and any portions
     10  * thereof, and that both notices appear in supporting documentation.
     11  *
     12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
     13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
     14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     15  *
     16  * Carnegie Mellon requests users of this software to return to
     17  *
     18  *  Software Distribution Coordinator  or  Software.Distribution (at) CS.CMU.EDU
     19  *  School of Computer Science
     20  *  Carnegie Mellon University
     21  *  Pittsburgh PA 15213-3890
     22  *
     23  * any improvements or extensions that they make and grant Carnegie the
     24  * rights to redistribute these changes.
     25  */
     26 /*
     27  * $Id: db_run.c,v 1.2 1993/05/20 03:39:28 cgd Exp $
     28  *
     29  * HISTORY
     30  * $Log: db_run.c,v $
     31  * Revision 1.2  1993/05/20 03:39:28  cgd
     32  * add explicit rcs id
     33  *
     34  * Revision 1.1.1.1  1993/03/21  09:46:27  cgd
     35  * initial import of 386bsd-0.1 sources
     36  *
     37  * Revision 1.1  1992/03/25  21:45:24  pace
     38  * Initial revision
     39  *
     40  * Revision 2.5  91/02/05  17:06:58  mrt
     41  * 	Changed to new Mach copyright
     42  * 	[91/01/31  16:19:05  mrt]
     43  *
     44  * Revision 2.4  91/01/08  15:09:10  rpd
     45  * 	Fixed bug in db_restart_at_pc.
     46  * 	[90/12/07            rpd]
     47  * 	Added STEP_COUNT and count option to db_continue_cmd.
     48  * 	Changed db_stop_at_pc to return (modified) is_breakpoint.
     49  * 	Fixed db_stop_at_pc to print newlines in the right places.
     50  * 	[90/11/27            rpd]
     51  *
     52  * Revision 2.3  90/10/25  14:43:59  rwd
     53  * 	Changed db_find_breakpoint to db_find_breakpoint_here.
     54  * 	[90/10/18            rpd]
     55  *
     56  * 	Fixed db_set_single_step to pass regs to branch_taken.
     57  * 	Added watchpoint argument to db_restart_at_pc.
     58  * 	[90/10/17            rpd]
     59  * 	Generalized the watchpoint support.
     60  * 	[90/10/16            rwd]
     61  * 	Added watchpoint support.
     62  * 	[90/10/16            rpd]
     63  *
     64  * Revision 2.2  90/08/27  21:51:59  dbg
     65  * 	Fixed names for single-step functions.
     66  * 	[90/08/20            af]
     67  * 	Reduce lint.
     68  * 	[90/08/07            dbg]
     69  * 	Created.
     70  * 	[90/07/25            dbg]
     71  *
     72  */
     73 /*
     74  * 	Author: David B. Golub, Carnegie Mellon University
     75  *	Date:	7/90
     76  */
     77 
     78 /*
     79  * Commands to run process.
     80  */
     81 #include "param.h"
     82 #include "proc.h"
     83 #include <machine/db_machdep.h>
     84 
     85 #include <ddb/db_lex.h>
     86 #include <ddb/db_break.h>
     87 #include <ddb/db_access.h>
     88 
     89 int	db_run_mode;
     90 #define	STEP_NONE	0
     91 #define	STEP_ONCE	1
     92 #define	STEP_RETURN	2
     93 #define	STEP_CALLT	3
     94 #define	STEP_CONTINUE	4
     95 #define STEP_INVISIBLE	5
     96 #define	STEP_COUNT	6
     97 
     98 boolean_t	db_sstep_print;
     99 int		db_loop_count;
    100 int		db_call_depth;
    101 
    102 int		db_inst_count;
    103 int		db_load_count;
    104 int		db_store_count;
    105 
    106 #ifndef db_set_single_step
    107 void		db_set_single_step(/* db_regs_t *regs */);	/* forward */
    108 #endif
    109 #ifndef db_clear_single_step
    110 void		db_clear_single_step(/* db_regs_t *regs */);
    111 #endif
    112 
    113 boolean_t
    114 db_stop_at_pc(is_breakpoint)
    115 	boolean_t	*is_breakpoint;
    116 {
    117 	register db_addr_t	pc;
    118 	register db_breakpoint_t bkpt;
    119 
    120 	db_clear_single_step(DDB_REGS);
    121 	db_clear_breakpoints();
    122 	db_clear_watchpoints();
    123 	pc = PC_REGS(DDB_REGS);
    124 
    125 #ifdef	FIXUP_PC_AFTER_BREAK
    126 	if (*is_breakpoint) {
    127 	    /*
    128 	     * Breakpoint trap.  Fix up the PC if the
    129 	     * machine requires it.
    130 	     */
    131 	    FIXUP_PC_AFTER_BREAK
    132 	    pc = PC_REGS(DDB_REGS);
    133 	}
    134 #endif
    135 
    136 	/*
    137 	 * Now check for a breakpoint at this address.
    138 	 */
    139 	bkpt = db_find_breakpoint_here(pc);
    140 	if (bkpt) {
    141 	    if (--bkpt->count == 0) {
    142 		bkpt->count = bkpt->init_count;
    143 		*is_breakpoint = TRUE;
    144 		return (TRUE);	/* stop here */
    145 	    }
    146 	} else if (*is_breakpoint) {
    147 		ddb_regs.tf_eip += 1;
    148 	}
    149 
    150 	*is_breakpoint = FALSE;
    151 
    152 	if (db_run_mode == STEP_INVISIBLE) {
    153 	    db_run_mode = STEP_CONTINUE;
    154 	    return (FALSE);	/* continue */
    155 	}
    156 	if (db_run_mode == STEP_COUNT) {
    157 	    return (FALSE); /* continue */
    158 	}
    159 	if (db_run_mode == STEP_ONCE) {
    160 	    if (--db_loop_count > 0) {
    161 		if (db_sstep_print) {
    162 		    db_printf("\t\t");
    163 		    db_print_loc_and_inst(pc);
    164 		    db_printf("\n");
    165 		}
    166 		return (FALSE);	/* continue */
    167 	    }
    168 	}
    169 	if (db_run_mode == STEP_RETURN) {
    170 	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
    171 
    172 	    /* continue until matching return */
    173 
    174 	    if (!inst_trap_return(ins) &&
    175 		(!inst_return(ins) || --db_call_depth != 0)) {
    176 		if (db_sstep_print) {
    177 		    if (inst_call(ins) || inst_return(ins)) {
    178 			register int i;
    179 
    180 			db_printf("[after %6d]     ", db_inst_count);
    181 			for (i = db_call_depth; --i > 0; )
    182 			    db_printf("  ");
    183 			db_print_loc_and_inst(pc);
    184 			db_printf("\n");
    185 		    }
    186 		}
    187 		if (inst_call(ins))
    188 		    db_call_depth++;
    189 		return (FALSE);	/* continue */
    190 	    }
    191 	}
    192 	if (db_run_mode == STEP_CALLT) {
    193 	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
    194 
    195 	    /* continue until call or return */
    196 
    197 	    if (!inst_call(ins) &&
    198 		!inst_return(ins) &&
    199 		!inst_trap_return(ins)) {
    200 		return (FALSE);	/* continue */
    201 	    }
    202 	}
    203 	db_run_mode = STEP_NONE;
    204 	return (TRUE);
    205 }
    206 
    207 void
    208 db_restart_at_pc(watchpt)
    209 	boolean_t watchpt;
    210 {
    211 	register db_addr_t	pc = PC_REGS(DDB_REGS);
    212 
    213 	if ((db_run_mode == STEP_COUNT) ||
    214 	    (db_run_mode == STEP_RETURN) ||
    215 	    (db_run_mode == STEP_CALLT)) {
    216 	    db_expr_t		ins;
    217 
    218 	    /*
    219 	     * We are about to execute this instruction,
    220 	     * so count it now.
    221 	     */
    222 
    223 	    ins = db_get_value(pc, sizeof(int), FALSE);
    224 	    db_inst_count++;
    225 	    db_load_count += inst_load(ins);
    226 	    db_store_count += inst_store(ins);
    227 #ifdef	SOFTWARE_SSTEP
    228 	    /* XXX works on mips, but... */
    229 	    if (inst_branch(ins) || inst_call(ins)) {
    230 		ins = db_get_value(next_instr_address(pc,1),
    231 				   sizeof(int), FALSE);
    232 		db_inst_count++;
    233 		db_load_count += inst_load(ins);
    234 		db_store_count += inst_store(ins);
    235 	    }
    236 #endif	SOFTWARE_SSTEP
    237 	}
    238 
    239 	if (db_run_mode == STEP_CONTINUE) {
    240 	    if (watchpt || db_find_breakpoint_here(pc)) {
    241 		/*
    242 		 * Step over breakpoint/watchpoint.
    243 		 */
    244 		db_run_mode = STEP_INVISIBLE;
    245 		db_set_single_step(DDB_REGS);
    246 	    } else {
    247 		db_set_breakpoints();
    248 		db_set_watchpoints();
    249 	    }
    250 	} else {
    251 	    db_set_single_step(DDB_REGS);
    252 	}
    253 }
    254 
    255 void
    256 db_single_step(regs)
    257 	db_regs_t *regs;
    258 {
    259 	if (db_run_mode == STEP_CONTINUE) {
    260 	    db_run_mode = STEP_INVISIBLE;
    261 	    db_set_single_step(regs);
    262 	}
    263 }
    264 
    265 #ifdef	SOFTWARE_SSTEP
    266 /*
    267  *	Software implementation of single-stepping.
    268  *	If your machine does not have a trace mode
    269  *	similar to the vax or sun ones you can use
    270  *	this implementation, done for the mips.
    271  *	Just define the above conditional and provide
    272  *	the functions/macros defined below.
    273  *
    274  * extern boolean_t
    275  *	inst_branch(),		returns true if the instruction might branch
    276  * extern unsigned
    277  *	branch_taken(),		return the address the instruction might
    278  *				branch to
    279  *	db_getreg_val();	return the value of a user register,
    280  *				as indicated in the hardware instruction
    281  *				encoding, e.g. 8 for r8
    282  *
    283  * next_instr_address(pc,bd)	returns the address of the first
    284  *				instruction following the one at "pc",
    285  *				which is either in the taken path of
    286  *				the branch (bd==1) or not.  This is
    287  *				for machines (mips) with branch delays.
    288  *
    289  *	A single-step may involve at most 2 breakpoints -
    290  *	one for branch-not-taken and one for branch taken.
    291  *	If one of these addresses does not already have a breakpoint,
    292  *	we allocate a breakpoint and save it here.
    293  *	These breakpoints are deleted on return.
    294  */
    295 db_breakpoint_t	db_not_taken_bkpt = 0;
    296 db_breakpoint_t	db_taken_bkpt = 0;
    297 
    298 void
    299 db_set_single_step(regs)
    300 	register db_regs_t *regs;
    301 {
    302 	db_addr_t pc = PC_REGS(regs);
    303 	register unsigned	 inst, brpc;
    304 
    305 	/*
    306 	 *	User was stopped at pc, e.g. the instruction
    307 	 *	at pc was not executed.
    308 	 */
    309 	inst = db_get_value(pc, sizeof(int), FALSE);
    310 	if (inst_branch(inst) || inst_call(inst)) {
    311 	    extern unsigned getreg_val();
    312 
    313 	    brpc = branch_taken(inst, pc, getreg_val, regs);
    314 	    if (brpc != pc) {	/* self-branches are hopeless */
    315 		db_taken_bkpt = db_set_temp_breakpoint(brpc);
    316 	    }
    317 	    pc = next_instr_address(pc,1);
    318 	}
    319 	pc = next_instr_address(pc,0);
    320 	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
    321 }
    322 
    323 void
    324 db_clear_single_step(regs)
    325 	db_regs_t *regs;
    326 {
    327 	register db_breakpoint_t	bkpt;
    328 
    329 	if (db_taken_bkpt != 0) {
    330 	    db_delete_temp_breakpoint(db_taken_bkpt);
    331 	    db_taken_bkpt = 0;
    332 	}
    333 	if (db_not_taken_bkpt != 0) {
    334 	    db_delete_temp_breakpoint(db_not_taken_bkpt);
    335 	    db_not_taken_bkpt = 0;
    336 	}
    337 }
    338 
    339 #endif	SOFTWARE_SSTEP
    340 
    341 extern int	db_cmd_loop_done;
    342 
    343 /* single-step */
    344 /*ARGSUSED*/
    345 void
    346 db_single_step_cmd(addr, have_addr, count, modif)
    347 	db_expr_t	addr;
    348 	int		have_addr;
    349 	db_expr_t	count;
    350 	char *		modif;
    351 {
    352 	boolean_t	print = FALSE;
    353 
    354 	if (count == -1)
    355 	    count = 1;
    356 
    357 	if (modif[0] == 'p')
    358 	    print = TRUE;
    359 
    360 	db_run_mode = STEP_ONCE;
    361 	db_loop_count = count;
    362 	db_sstep_print = print;
    363 	db_inst_count = 0;
    364 	db_load_count = 0;
    365 	db_store_count = 0;
    366 
    367 	db_cmd_loop_done = 1;
    368 }
    369 
    370 /* trace and print until call/return */
    371 /*ARGSUSED*/
    372 void
    373 db_trace_until_call_cmd(addr, have_addr, count, modif)
    374 	db_expr_t	addr;
    375 	int		have_addr;
    376 	db_expr_t	count;
    377 	char *		modif;
    378 {
    379 	boolean_t	print = FALSE;
    380 
    381 	if (modif[0] == 'p')
    382 	    print = TRUE;
    383 
    384 	db_run_mode = STEP_CALLT;
    385 	db_sstep_print = print;
    386 	db_inst_count = 0;
    387 	db_load_count = 0;
    388 	db_store_count = 0;
    389 
    390 	db_cmd_loop_done = 1;
    391 }
    392 
    393 /*ARGSUSED*/
    394 void
    395 db_trace_until_matching_cmd(addr, have_addr, count, modif)
    396 	db_expr_t	addr;
    397 	int		have_addr;
    398 	db_expr_t	count;
    399 	char *		modif;
    400 {
    401 	boolean_t	print = FALSE;
    402 
    403 	if (modif[0] == 'p')
    404 	    print = TRUE;
    405 
    406 	db_run_mode = STEP_RETURN;
    407 	db_call_depth = 1;
    408 	db_sstep_print = print;
    409 	db_inst_count = 0;
    410 	db_load_count = 0;
    411 	db_store_count = 0;
    412 
    413 	db_cmd_loop_done = 1;
    414 }
    415 
    416 /* continue */
    417 /*ARGSUSED*/
    418 void
    419 db_continue_cmd(addr, have_addr, count, modif)
    420 	db_expr_t	addr;
    421 	int		have_addr;
    422 	db_expr_t	count;
    423 	char *		modif;
    424 {
    425 	if (modif[0] == 'c')
    426 	    db_run_mode = STEP_COUNT;
    427 	else
    428 	    db_run_mode = STEP_CONTINUE;
    429 	db_inst_count = 0;
    430 	db_load_count = 0;
    431 	db_store_count = 0;
    432 
    433 	db_cmd_loop_done = 1;
    434 }
    435