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