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