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