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