db_run.c revision 1.18
1/*	$OpenBSD: db_run.c,v 1.18 2005/09/06 19:49:21 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
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# ifdef SET_PC_REGS
121		SET_PC_REGS(regs, old_pc);
122# else
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