1/*-
2 * Mach Operating System
3 * Copyright (c) 1991,1990 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19 *  School of Computer Science
20 *  Carnegie Mellon University
21 *  Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie the
24 * rights to redistribute these changes.
25 */
26/*
27 * 	Author: David B. Golub, Carnegie Mellon University
28 *	Date:	7/90
29 */
30
31/*
32 * Commands to run process.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38#include <sys/param.h>
39#include <sys/kdb.h>
40#include <sys/proc.h>
41
42#include <machine/kdb.h>
43#include <machine/pcb.h>
44
45#include <vm/vm.h>
46
47#include <ddb/ddb.h>
48#include <ddb/db_break.h>
49#include <ddb/db_access.h>
50
51static int	db_run_mode;
52#define	STEP_NONE	0
53#define	STEP_ONCE	1
54#define	STEP_RETURN	2
55#define	STEP_CALLT	3
56#define	STEP_CONTINUE	4
57#define	STEP_INVISIBLE	5
58#define	STEP_COUNT	6
59
60static boolean_t	db_sstep_print;
61static int		db_loop_count;
62static int		db_call_depth;
63
64int		db_inst_count;
65int		db_load_count;
66int		db_store_count;
67
68#ifndef db_set_single_step
69void db_set_single_step(void);
70#endif
71#ifndef db_clear_single_step
72void db_clear_single_step(void);
73#endif
74
75#ifdef SOFTWARE_SSTEP
76db_breakpoint_t	db_not_taken_bkpt = 0;
77db_breakpoint_t	db_taken_bkpt = 0;
78#endif
79
80boolean_t
81db_stop_at_pc(boolean_t *is_breakpoint)
82{
83	register db_addr_t	pc;
84	register db_breakpoint_t bkpt;
85
86	pc = PC_REGS();
87#ifdef SOFTWARE_SSTEP
88	if ((db_not_taken_bkpt != 0 && pc == db_not_taken_bkpt->address)
89	    || (db_taken_bkpt != 0 && pc == db_taken_bkpt->address))
90		*is_breakpoint = FALSE;
91#endif
92
93	db_clear_single_step();
94	db_clear_breakpoints();
95	db_clear_watchpoints();
96
97#ifdef	FIXUP_PC_AFTER_BREAK
98	if (*is_breakpoint) {
99	    /*
100	     * Breakpoint trap.  Fix up the PC if the
101	     * machine requires it.
102	     */
103	    FIXUP_PC_AFTER_BREAK
104	    pc = PC_REGS();
105	}
106#endif
107
108	/*
109	 * Now check for a breakpoint at this address.
110	 */
111	bkpt = db_find_breakpoint_here(pc);
112	if (bkpt) {
113	    if (--bkpt->count == 0) {
114		bkpt->count = bkpt->init_count;
115		*is_breakpoint = TRUE;
116		return (TRUE);	/* stop here */
117	    }
118	} else if (*is_breakpoint) {
119#ifdef BKPT_SKIP
120		BKPT_SKIP;
121#endif
122	}
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	    /* continue until matching return */
145	    db_expr_t ins;
146
147	    ins = db_get_value(pc, sizeof(int), FALSE);
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			register 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	    /* continue until call or return */
168	    db_expr_t ins;
169
170	    ins = db_get_value(pc, sizeof(int), FALSE);
171	    if (!inst_call(ins) &&
172		!inst_return(ins) &&
173		!inst_trap_return(ins)) {
174		return (FALSE);	/* continue */
175	    }
176	}
177	db_run_mode = STEP_NONE;
178	return (TRUE);
179}
180
181void
182db_restart_at_pc(boolean_t watchpt)
183{
184	register db_addr_t	pc = PC_REGS();
185
186	if ((db_run_mode == STEP_COUNT) ||
187	    (db_run_mode == STEP_RETURN) ||
188	    (db_run_mode == STEP_CALLT)) {
189	    db_expr_t		ins;
190
191	    /*
192	     * We are about to execute this instruction,
193	     * so count it now.
194	     */
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();
219	    } else {
220		db_set_breakpoints();
221		db_set_watchpoints();
222	    }
223	} else {
224	    db_set_single_step();
225	}
226}
227
228#ifdef	SOFTWARE_SSTEP
229/*
230 *	Software implementation of single-stepping.
231 *	If your machine does not have a trace mode
232 *	similar to the vax or sun ones you can use
233 *	this implementation, done for the mips.
234 *	Just define the above conditional and provide
235 *	the functions/macros defined below.
236 *
237 * extern boolean_t
238 *	inst_branch(),		returns true if the instruction might branch
239 * extern unsigned
240 *	branch_taken(),		return the address the instruction might
241 *				branch to
242 *	db_getreg_val();	return the value of a user register,
243 *				as indicated in the hardware instruction
244 *				encoding, e.g. 8 for r8
245 *
246 * next_instr_address(pc,bd)	returns the address of the first
247 *				instruction following the one at "pc",
248 *				which is either in the taken path of
249 *				the branch (bd==1) or not.  This is
250 *				for machines (mips) with branch delays.
251 *
252 *	A single-step may involve at most 2 breakpoints -
253 *	one for branch-not-taken and one for branch taken.
254 *	If one of these addresses does not already have a breakpoint,
255 *	we allocate a breakpoint and save it here.
256 *	These breakpoints are deleted on return.
257 */
258
259void
260db_set_single_step(void)
261{
262	db_addr_t pc = PC_REGS(), brpc;
263	unsigned inst;
264
265	/*
266	 *	User was stopped at pc, e.g. the instruction
267	 *	at pc was not executed.
268	 */
269	inst = db_get_value(pc, sizeof(int), FALSE);
270	if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) {
271		brpc = branch_taken(inst, pc);
272		if (brpc != pc) {	/* self-branches are hopeless */
273			db_taken_bkpt = db_set_temp_breakpoint(brpc);
274		}
275		pc = next_instr_address(pc, 1);
276	}
277	pc = next_instr_address(pc, 0);
278	db_not_taken_bkpt = db_set_temp_breakpoint(pc);
279}
280
281void
282db_clear_single_step(void)
283{
284
285	if (db_not_taken_bkpt != 0) {
286		db_delete_temp_breakpoint(db_not_taken_bkpt);
287		db_not_taken_bkpt = 0;
288	}
289	if (db_taken_bkpt != 0) {
290		db_delete_temp_breakpoint(db_taken_bkpt);
291		db_taken_bkpt = 0;
292	}
293}
294
295#endif	/* SOFTWARE_SSTEP */
296
297extern int	db_cmd_loop_done;
298
299/* single-step */
300/*ARGSUSED*/
301void
302db_single_step_cmd(addr, have_addr, count, modif)
303	db_expr_t	addr;
304	boolean_t	have_addr;
305	db_expr_t	count;
306	char *		modif;
307{
308	boolean_t	print = FALSE;
309
310	if (count == -1)
311	    count = 1;
312
313	if (modif[0] == 'p')
314	    print = TRUE;
315
316	db_run_mode = STEP_ONCE;
317	db_loop_count = count;
318	db_sstep_print = print;
319	db_inst_count = 0;
320	db_load_count = 0;
321	db_store_count = 0;
322
323	db_cmd_loop_done = 1;
324}
325
326/* trace and print until call/return */
327/*ARGSUSED*/
328void
329db_trace_until_call_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
330    char *modif)
331{
332	boolean_t	print = FALSE;
333
334	if (modif[0] == 'p')
335	    print = TRUE;
336
337	db_run_mode = STEP_CALLT;
338	db_sstep_print = print;
339	db_inst_count = 0;
340	db_load_count = 0;
341	db_store_count = 0;
342
343	db_cmd_loop_done = 1;
344}
345
346/*ARGSUSED*/
347void
348db_trace_until_matching_cmd(db_expr_t addr, boolean_t have_addr,
349    db_expr_t count, char *modif)
350{
351	boolean_t	print = FALSE;
352
353	if (modif[0] == 'p')
354	    print = TRUE;
355
356	db_run_mode = STEP_RETURN;
357	db_call_depth = 1;
358	db_sstep_print = print;
359	db_inst_count = 0;
360	db_load_count = 0;
361	db_store_count = 0;
362
363	db_cmd_loop_done = 1;
364}
365
366/* continue */
367/*ARGSUSED*/
368void
369db_continue_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
370    char *modif)
371{
372	if (modif[0] == 'c')
373	    db_run_mode = STEP_COUNT;
374	else
375	    db_run_mode = STEP_CONTINUE;
376	db_inst_count = 0;
377	db_load_count = 0;
378	db_store_count = 0;
379
380	db_cmd_loop_done = 1;
381}
382