db_run.c revision 1.2
1/*	$OpenBSD: db_run.c,v 1.2 1996/02/20 13:35:41 mickey 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@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#include <ddb/db_watch.h>
45#include <ddb/db_output.h>
46#include <ddb/db_sym.h>
47#include <ddb/db_extern.h>
48
49int	db_run_mode;
50#define	STEP_NONE	0
51#define	STEP_ONCE	1
52#define	STEP_RETURN	2
53#define	STEP_CALLT	3
54#define	STEP_CONTINUE	4
55#define STEP_INVISIBLE	5
56#define	STEP_COUNT	6
57
58boolean_t	db_sstep_print;
59int		db_loop_count;
60int		db_call_depth;
61
62boolean_t
63db_stop_at_pc(regs, is_breakpoint)
64	db_regs_t *regs;
65	boolean_t	*is_breakpoint;
66{
67	register db_addr_t	pc;
68	register db_breakpoint_t bkpt;
69
70	db_clear_single_step(regs);
71	db_clear_breakpoints();
72	db_clear_watchpoints();
73	pc = PC_REGS(regs);
74
75#ifdef	FIXUP_PC_AFTER_BREAK
76	if (*is_breakpoint) {
77	    /*
78	     * Breakpoint trap.  Fix up the PC if the
79	     * machine requires it.
80	     */
81	    FIXUP_PC_AFTER_BREAK
82	    pc = PC_REGS(regs);
83	}
84#endif
85
86	/*
87	 * Now check for a breakpoint at this address.
88	 */
89	bkpt = db_find_breakpoint_here(pc);
90	if (bkpt) {
91	    if (--bkpt->count == 0) {
92		bkpt->count = bkpt->init_count;
93		*is_breakpoint = TRUE;
94		return (TRUE);	/* stop here */
95	    }
96	} else if (*is_breakpoint) {
97		PC_REGS(regs) += BKPT_SIZE;
98	}
99
100	*is_breakpoint = FALSE;
101
102	if (db_run_mode == STEP_INVISIBLE) {
103	    db_run_mode = STEP_CONTINUE;
104	    return (FALSE);	/* continue */
105	}
106	if (db_run_mode == STEP_COUNT) {
107	    return (FALSE); /* continue */
108	}
109	if (db_run_mode == STEP_ONCE) {
110	    if (--db_loop_count > 0) {
111		if (db_sstep_print) {
112		    db_printf("\t\t");
113		    db_print_loc_and_inst(pc);
114		    db_printf("\n");
115		}
116		return (FALSE);	/* continue */
117	    }
118	}
119	if (db_run_mode == STEP_RETURN) {
120	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
121
122	    /* continue until matching return */
123
124	    if (!inst_trap_return(ins) &&
125		(!inst_return(ins) || --db_call_depth != 0)) {
126		if (db_sstep_print) {
127		    if (inst_call(ins) || inst_return(ins)) {
128			register int i;
129
130			db_printf("[after %6d]     ", db_inst_count);
131			for (i = db_call_depth; --i > 0; )
132			    db_printf("  ");
133			db_print_loc_and_inst(pc);
134			db_printf("\n");
135		    }
136		}
137		if (inst_call(ins))
138		    db_call_depth++;
139		return (FALSE);	/* continue */
140	    }
141	}
142	if (db_run_mode == STEP_CALLT) {
143	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
144
145	    /* continue until call or return */
146
147	    if (!inst_call(ins) &&
148		!inst_return(ins) &&
149		!inst_trap_return(ins)) {
150		return (FALSE);	/* continue */
151	    }
152	}
153	db_run_mode = STEP_NONE;
154	return (TRUE);
155}
156
157void
158db_restart_at_pc(regs, watchpt)
159	db_regs_t *regs;
160	boolean_t watchpt;
161{
162	register db_addr_t pc = PC_REGS(regs);
163
164	if ((db_run_mode == STEP_COUNT) ||
165	    (db_run_mode == STEP_RETURN) ||
166	    (db_run_mode == STEP_CALLT)) {
167	    db_expr_t		ins;
168
169	    /*
170	     * We are about to execute this instruction,
171	     * so count it now.
172	     */
173
174	    ins = db_get_value(pc, sizeof(int), FALSE);
175	    db_inst_count++;
176	    db_load_count += inst_load(ins);
177	    db_store_count += inst_store(ins);
178#ifdef	SOFTWARE_SSTEP
179	    /* XXX works on mips, but... */
180	    if (inst_branch(ins) || inst_call(ins)) {
181		ins = db_get_value(next_instr_address(pc,1),
182				   sizeof(int), FALSE);
183		db_inst_count++;
184		db_load_count += inst_load(ins);
185		db_store_count += inst_store(ins);
186	    }
187#endif	SOFTWARE_SSTEP
188	}
189
190	if (db_run_mode == STEP_CONTINUE) {
191	    if (watchpt || db_find_breakpoint_here(pc)) {
192		/*
193		 * Step over breakpoint/watchpoint.
194		 */
195		db_run_mode = STEP_INVISIBLE;
196		db_set_single_step(regs);
197	    } else {
198		db_set_breakpoints();
199		db_set_watchpoints();
200	    }
201	} else {
202	    db_set_single_step(regs);
203	}
204}
205
206void
207db_single_step(regs)
208	db_regs_t *regs;
209{
210	if (db_run_mode == STEP_CONTINUE) {
211	    db_run_mode = STEP_INVISIBLE;
212	    db_set_single_step(regs);
213	}
214}
215
216#ifdef	SOFTWARE_SSTEP
217/*
218 *	Software implementation of single-stepping.
219 *	If your machine does not have a trace mode
220 *	similar to the vax or sun ones you can use
221 *	this implementation, done for the mips.
222 *	Just define the above conditional and provide
223 *	the functions/macros defined below.
224 *
225 * extern boolean_t
226 *	inst_branch(),		returns true if the instruction might branch
227 * extern unsigned
228 *	branch_taken(),		return the address the instruction might
229 *				branch to
230 *	db_getreg_val();	return the value of a user register,
231 *				as indicated in the hardware instruction
232 *				encoding, e.g. 8 for r8
233 *
234 * next_instr_address(pc,bd)	returns the address of the first
235 *				instruction following the one at "pc",
236 *				which is either in the taken path of
237 *				the branch (bd==1) or not.  This is
238 *				for machines (mips) with branch delays.
239 *
240 *	A single-step may involve at most 2 breakpoints -
241 *	one for branch-not-taken and one for branch taken.
242 *	If one of these addresses does not already have a breakpoint,
243 *	we allocate a breakpoint and save it here.
244 *	These breakpoints are deleted on return.
245 */
246db_breakpoint_t	db_not_taken_bkpt = 0;
247db_breakpoint_t	db_taken_bkpt = 0;
248
249void
250db_set_single_step(regs)
251	register db_regs_t *regs;
252{
253	db_addr_t pc = PC_REGS(regs);
254	register unsigned	 inst, brpc;
255
256	/*
257	 *	User was stopped at pc, e.g. the instruction
258	 *	at pc was not executed.
259	 */
260	inst = db_get_value(pc, sizeof(int), FALSE);
261	if (inst_branch(inst) || inst_call(inst)) {
262
263	    brpc = branch_taken(inst, pc, getreg_val, regs);
264	    if (brpc != pc) {	/* self-branches are hopeless */
265		db_taken_bkpt = db_set_temp_breakpoint(brpc);
266	    }
267	    pc = next_instr_address(pc,1);
268	}
269	pc = next_instr_address(pc,0);
270	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
271}
272
273void
274db_clear_single_step(regs)
275	db_regs_t *regs;
276{
277	register db_breakpoint_t	bkpt;
278
279	if (db_taken_bkpt != 0) {
280	    db_delete_temp_breakpoint(db_taken_bkpt);
281	    db_taken_bkpt = 0;
282	}
283	if (db_not_taken_bkpt != 0) {
284	    db_delete_temp_breakpoint(db_not_taken_bkpt);
285	    db_not_taken_bkpt = 0;
286	}
287}
288
289#endif	SOFTWARE_SSTEP
290
291extern int	db_cmd_loop_done;
292
293/* single-step */
294/*ARGSUSED*/
295void
296db_single_step_cmd(addr, have_addr, count, modif)
297	db_expr_t	addr;
298	int		have_addr;
299	db_expr_t	count;
300	char *		modif;
301{
302	boolean_t	print = FALSE;
303
304	if (count == -1)
305	    count = 1;
306
307	if (modif[0] == 'p')
308	    print = TRUE;
309
310	db_run_mode = STEP_ONCE;
311	db_loop_count = count;
312	db_sstep_print = print;
313	db_inst_count = 0;
314	db_load_count = 0;
315	db_store_count = 0;
316
317	db_cmd_loop_done = 1;
318}
319
320/* trace and print until call/return */
321/*ARGSUSED*/
322void
323db_trace_until_call_cmd(addr, have_addr, count, modif)
324	db_expr_t	addr;
325	int		have_addr;
326	db_expr_t	count;
327	char *		modif;
328{
329	boolean_t	print = FALSE;
330
331	if (modif[0] == 'p')
332	    print = TRUE;
333
334	db_run_mode = STEP_CALLT;
335	db_sstep_print = print;
336	db_inst_count = 0;
337	db_load_count = 0;
338	db_store_count = 0;
339
340	db_cmd_loop_done = 1;
341}
342
343/*ARGSUSED*/
344void
345db_trace_until_matching_cmd(addr, have_addr, count, modif)
346	db_expr_t	addr;
347	int		have_addr;
348	db_expr_t	count;
349	char *		modif;
350{
351	boolean_t	print = FALSE;
352
353	if (modif[0] == 'p')
354	    print = TRUE;
355
356	db_run_mode = STEP_RETURN;
357	db_call_depth = 1;
358	db_sstep_print = print;
359	db_inst_count = 0;
360	db_load_count = 0;
361	db_store_count = 0;
362
363	db_cmd_loop_done = 1;
364}
365
366/* continue */
367/*ARGSUSED*/
368void
369db_continue_cmd(addr, have_addr, count, modif)
370	db_expr_t	addr;
371	int		have_addr;
372	db_expr_t	count;
373	char *		modif;
374{
375	if (modif[0] == 'c')
376	    db_run_mode = STEP_COUNT;
377	else
378	    db_run_mode = STEP_CONTINUE;
379	db_inst_count = 0;
380	db_load_count = 0;
381	db_store_count = 0;
382
383	db_cmd_loop_done = 1;
384}
385