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