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