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