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