xref: /netbsd-src/sys/ddb/db_run.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: db_run.c,v 1.13 1997/12/10 23:09:31 pk Exp $	*/
2 
3 /*
4  * Mach Operating System
5  * Copyright (c) 1993-1990 Carnegie Mellon University
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify and distribute this software and its
9  * documentation is hereby granted, provided that both the copyright
10  * notice and this permission notice appear in all copies of the
11  * software, derivative works or modified versions, and any portions
12  * thereof, and that both notices appear in supporting documentation.
13  *
14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17  *
18  * Carnegie Mellon requests users of this software to return to
19  *
20  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21  *  School of Computer Science
22  *  Carnegie Mellon University
23  *  Pittsburgh PA 15213-3890
24  *
25  * any improvements or extensions that they make and grant Carnegie the
26  * rights to redistribute these changes.
27  *
28  * 	Author: David B. Golub, Carnegie Mellon University
29  *	Date:	7/90
30  */
31 
32 /*
33  * Commands to run process.
34  */
35 #include <sys/param.h>
36 #include <sys/proc.h>
37 
38 #include <machine/db_machdep.h>
39 
40 #include <ddb/db_run.h>
41 #include <ddb/db_lex.h>
42 #include <ddb/db_break.h>
43 #include <ddb/db_access.h>
44 #include <ddb/db_watch.h>
45 #include <ddb/db_output.h>
46 #include <ddb/db_sym.h>
47 #include <ddb/db_extern.h>
48 
49 int	db_run_mode;
50 #define	STEP_NONE	0
51 #define	STEP_ONCE	1
52 #define	STEP_RETURN	2
53 #define	STEP_CALLT	3
54 #define	STEP_CONTINUE	4
55 #define STEP_INVISIBLE	5
56 #define	STEP_COUNT	6
57 
58 boolean_t	db_sstep_print;
59 int		db_loop_count;
60 int		db_call_depth;
61 
62 #ifdef	SOFTWARE_SSTEP
63 db_breakpoint_t	db_not_taken_bkpt = 0;
64 db_breakpoint_t	db_taken_bkpt = 0;
65 #endif
66 
67 boolean_t
68 db_stop_at_pc(regs, is_breakpoint)
69 	db_regs_t *regs;
70 	boolean_t	*is_breakpoint;
71 {
72 	register db_addr_t	pc;
73 	register db_breakpoint_t bkpt;
74 
75 	pc = PC_REGS(regs);
76 
77 #ifdef	SOFTWARE_SSTEP
78 	/*
79 	 * If we stopped at one of the single-step breakpoints,
80 	 * say it's not really a breakpoint so that
81 	 * we don't skip over the real instruction.
82 	 */
83 	if ((db_taken_bkpt != NULL && db_taken_bkpt->address == pc) ||
84 	    (db_not_taken_bkpt != NULL && db_not_taken_bkpt->address == pc))
85 		*is_breakpoint = FALSE;
86 #endif
87 
88 	db_clear_single_step(regs);
89 	db_clear_breakpoints();
90 	db_clear_watchpoints();
91 
92 #ifdef	FIXUP_PC_AFTER_BREAK
93 	if (*is_breakpoint) {
94 	    /*
95 	     * Breakpoint trap.  Fix up the PC if the
96 	     * machine requires it.
97 	     */
98 	    FIXUP_PC_AFTER_BREAK(regs);
99 	    pc = PC_REGS(regs);
100 	}
101 #endif
102 
103 	/*
104 	 * Now check for a breakpoint at this address.
105 	 */
106 	bkpt = db_find_breakpoint_here(pc);
107 	if (bkpt) {
108 	    if (--bkpt->count == 0) {
109 		bkpt->count = bkpt->init_count;
110 		*is_breakpoint = TRUE;
111 		return (TRUE);	/* stop here */
112 	    }
113 	} else if (*is_breakpoint) {
114 #ifdef PC_ADVANCE
115 		PC_ADVANCE(regs);
116 #else
117 		PC_REGS(regs) += BKPT_SIZE;
118 #endif
119 	}
120 
121 	*is_breakpoint = FALSE;
122 
123 	if (db_run_mode == STEP_INVISIBLE) {
124 	    db_run_mode = STEP_CONTINUE;
125 	    return (FALSE);	/* continue */
126 	}
127 	if (db_run_mode == STEP_COUNT) {
128 	    return (FALSE); /* continue */
129 	}
130 	if (db_run_mode == STEP_ONCE) {
131 	    if (--db_loop_count > 0) {
132 		if (db_sstep_print) {
133 		    db_printf("\t\t");
134 		    db_print_loc_and_inst(pc);
135 		    db_printf("\n");
136 		}
137 		return (FALSE);	/* continue */
138 	    }
139 	}
140 	if (db_run_mode == STEP_RETURN) {
141 	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
142 
143 	    /* continue until matching return */
144 
145 	    if (!inst_trap_return(ins) &&
146 		(!inst_return(ins) || --db_call_depth != 0)) {
147 		if (db_sstep_print) {
148 		    if (inst_call(ins) || inst_return(ins)) {
149 			register int i;
150 
151 			db_printf("[after %6d]     ", db_inst_count);
152 			for (i = db_call_depth; --i > 0; )
153 			    db_printf("  ");
154 			db_print_loc_and_inst(pc);
155 			db_printf("\n");
156 		    }
157 		}
158 		if (inst_call(ins))
159 		    db_call_depth++;
160 		return (FALSE);	/* continue */
161 	    }
162 	}
163 	if (db_run_mode == STEP_CALLT) {
164 	    db_expr_t ins = db_get_value(pc, sizeof(int), FALSE);
165 
166 	    /* continue until call or return */
167 
168 	    if (!inst_call(ins) &&
169 		!inst_return(ins) &&
170 		!inst_trap_return(ins)) {
171 		return (FALSE);	/* continue */
172 	    }
173 	}
174 	db_run_mode = STEP_NONE;
175 	return (TRUE);
176 }
177 
178 void
179 db_restart_at_pc(regs, watchpt)
180 	db_regs_t *regs;
181 	boolean_t watchpt;
182 {
183 	register db_addr_t pc = PC_REGS(regs);
184 
185 	if ((db_run_mode == STEP_COUNT) ||
186 	    (db_run_mode == STEP_RETURN) ||
187 	    (db_run_mode == STEP_CALLT)) {
188 	    db_expr_t		ins;
189 
190 	    /*
191 	     * We are about to execute this instruction,
192 	     * so count it now.
193 	     */
194 	    ins = db_get_value(pc, sizeof(int), FALSE);
195 	    db_inst_count++;
196 	    db_load_count += inst_load(ins);
197 	    db_store_count += inst_store(ins);
198 
199 #ifdef SOFTWARE_SSTEP
200 	    /*
201 	     * Account for instructions in delay slots.
202 	     */
203 	    {
204 		db_addr_t brpc;
205 
206 		brpc = next_instr_address(pc, TRUE);
207 		if ((brpc != pc) && (inst_branch(ins) || inst_call(ins))) {
208 		    ins = db_get_value(brpc, sizeof(int), FALSE);
209 		    db_inst_count++;
210 		    db_load_count += inst_load(ins);
211 		    db_store_count += inst_store(ins);
212 		}
213 	    }
214 #endif
215 	}
216 
217 	if (db_run_mode == STEP_CONTINUE) {
218 	    if (watchpt || db_find_breakpoint_here(pc)) {
219 		/*
220 		 * Step over breakpoint/watchpoint.
221 		 */
222 		db_run_mode = STEP_INVISIBLE;
223 		db_set_single_step(regs);
224 	    } else {
225 		db_set_breakpoints();
226 		db_set_watchpoints();
227 	    }
228 	} else {
229 	    db_set_single_step(regs);
230 	}
231 }
232 
233 void
234 db_single_step(regs)
235 	db_regs_t *regs;
236 {
237 	if (db_run_mode == STEP_CONTINUE) {
238 	    db_run_mode = STEP_INVISIBLE;
239 	    db_set_single_step(regs);
240 	}
241 }
242 
243 #ifdef SOFTWARE_SSTEP
244 /*
245  *	Software implementation of single-stepping.
246  *	If your machine does not have a trace mode
247  *	similar to the vax or sun ones you can use
248  *	this implementation, done for the mips.
249  *	Just define the above conditional and provide
250  *	the functions/macros defined below.
251  *
252  * boolean_t inst_branch(int inst)
253  * boolean_t inst_call(int inst)
254  *	returns TRUE if the instruction might branch
255  *
256  * boolean_t inst_unconditional_flow_transfer(int inst)
257  *	returns TRUE if the instruction is an unconditional
258  *	transter of flow (i.e. unconditional branch)
259  *
260  * db_addr_t branch_taken(int inst, db_addr_t pc, db_regs_t *regs)
261  *	returns the target address of the branch
262  *
263  * db_addr_t next_instr_address(db_addr_t pc, boolean_t bd)
264  *	returns the address of the first instruction following the
265  *	one at "pc", which is either in the taken path of the branch
266  *	(bd == TRUE) or not.  This is for machines (e.g. mips) with
267  *	branch delays.
268  *
269  *	A single-step may involve at most 2 breakpoints -
270  *	one for branch-not-taken and one for branch taken.
271  *	If one of these addresses does not already have a breakpoint,
272  *	we allocate a breakpoint and save it here.
273  *	These breakpoints are deleted on return.
274  */
275 
276 void
277 db_set_single_step(regs)
278 	register db_regs_t *regs;
279 {
280 	db_addr_t pc = PC_REGS(regs), brpc = pc;
281 	boolean_t unconditional;
282 	unsigned int inst;
283 
284 	/*
285 	 *	User was stopped at pc, e.g. the instruction
286 	 *	at pc was not executed.
287 	 */
288 	inst = db_get_value(pc, sizeof(int), FALSE);
289 	if (inst_branch(inst) || inst_call(inst)) {
290 		brpc = branch_taken(inst, pc, regs);
291 		if (brpc != pc) {	/* self-branches are hopeless */
292 			db_taken_bkpt = db_set_temp_breakpoint(brpc);
293 		} else
294 			db_taken_bkpt = 0;
295 		pc = next_instr_address(pc, TRUE);
296 	}
297 
298 	/*
299 	 *	Check if this control flow instruction is an
300 	 *	unconditional transfer.
301 	 */
302 	unconditional = inst_unconditional_flow_transfer(inst);
303 
304 	pc = next_instr_address(pc, FALSE);
305 
306 	/*
307 	 *	We only set the sequential breakpoint if previous
308 	 *	instruction was not an unconditional change of flow
309 	 *	control.  If the previous instruction is an
310 	 *	unconditional change of flow control, setting a
311 	 *	breakpoint in the next sequential location may set
312 	 *	a breakpoint in data or in another routine, which
313 	 *	could screw up in either the program or the debugger.
314 	 *	(Consider, for instance, that the next sequential
315 	 *	instruction is the start of a routine needed by the
316 	 *	debugger.)
317 	 *
318 	 *	Also, don't set both the taken and not-taken breakpoints
319 	 *	in the same place even if the MD code would otherwise
320 	 *	have us do so.
321 	 */
322 	if (unconditional == FALSE &&
323 	    db_find_breakpoint_here(pc) == 0 &&
324 	    pc != brpc)
325 		db_not_taken_bkpt = db_set_temp_breakpoint(pc);
326 	else
327 		db_not_taken_bkpt = 0;
328 }
329 
330 void
331 db_clear_single_step(regs)
332 	db_regs_t *regs;
333 {
334 
335 	if (db_taken_bkpt != 0) {
336 	    db_delete_temp_breakpoint(db_taken_bkpt);
337 	    db_taken_bkpt = 0;
338 	}
339 	if (db_not_taken_bkpt != 0) {
340 	    db_delete_temp_breakpoint(db_not_taken_bkpt);
341 	    db_not_taken_bkpt = 0;
342 	}
343 }
344 
345 #endif /* SOFTWARE_SSTEP */
346 
347 extern int	db_cmd_loop_done;
348 
349 /* single-step */
350 /*ARGSUSED*/
351 void
352 db_single_step_cmd(addr, have_addr, count, modif)
353 	db_expr_t	addr;
354 	int		have_addr;
355 	db_expr_t	count;
356 	char *		modif;
357 {
358 	boolean_t	print = FALSE;
359 
360 	if (count == -1)
361 	    count = 1;
362 
363 	if (modif[0] == 'p')
364 	    print = TRUE;
365 
366 	db_run_mode = STEP_ONCE;
367 	db_loop_count = count;
368 	db_sstep_print = print;
369 	db_inst_count = 0;
370 	db_load_count = 0;
371 	db_store_count = 0;
372 
373 	db_cmd_loop_done = 1;
374 }
375 
376 /* trace and print until call/return */
377 /*ARGSUSED*/
378 void
379 db_trace_until_call_cmd(addr, have_addr, count, modif)
380 	db_expr_t	addr;
381 	int		have_addr;
382 	db_expr_t	count;
383 	char *		modif;
384 {
385 	boolean_t	print = FALSE;
386 
387 	if (modif[0] == 'p')
388 	    print = TRUE;
389 
390 	db_run_mode = STEP_CALLT;
391 	db_sstep_print = print;
392 	db_inst_count = 0;
393 	db_load_count = 0;
394 	db_store_count = 0;
395 
396 	db_cmd_loop_done = 1;
397 }
398 
399 /*ARGSUSED*/
400 void
401 db_trace_until_matching_cmd(addr, have_addr, count, modif)
402 	db_expr_t	addr;
403 	int		have_addr;
404 	db_expr_t	count;
405 	char *		modif;
406 {
407 	boolean_t	print = FALSE;
408 
409 	if (modif[0] == 'p')
410 	    print = TRUE;
411 
412 	db_run_mode = STEP_RETURN;
413 	db_call_depth = 1;
414 	db_sstep_print = print;
415 	db_inst_count = 0;
416 	db_load_count = 0;
417 	db_store_count = 0;
418 
419 	db_cmd_loop_done = 1;
420 }
421 
422 /* continue */
423 /*ARGSUSED*/
424 void
425 db_continue_cmd(addr, have_addr, count, modif)
426 	db_expr_t	addr;
427 	int		have_addr;
428 	db_expr_t	count;
429 	char *		modif;
430 {
431 	if (modif[0] == 'c')
432 	    db_run_mode = STEP_COUNT;
433 	else
434 	    db_run_mode = STEP_CONTINUE;
435 	db_inst_count = 0;
436 	db_load_count = 0;
437 	db_store_count = 0;
438 
439 	db_cmd_loop_done = 1;
440 }
441