FreeBSD/Linux Kernel Cross Reference
sys/ddb/db_run.c
1 /*
2 * Mach Operating System
3 * Copyright (c) 1993-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 "AS IS"
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 Mellon
24 * the rights to redistribute these changes.
25 */
26 /*
27 * HISTORY
28 * $Log: db_run.c,v $
29 * Revision 2.15 93/11/17 16:24:43 dbg
30 * Added ANSI function prototypes.
31 * [93/10/11 dbg]
32 *
33 * Revision 2.14 93/01/19 09:00:47 danner
34 * 64bit cleanup. software single step code revision.
35 * comment correction (jfriedl).
36 * [92/12/10 16:20:16 af]
37 *
38 * Revision 2.13 93/01/14 17:25:38 danner
39 * added use of db_branch_is_delayed in db_restart_at_pc.
40 * [92/11/28 jfriedl]
41 * 64bit cleanup.
42 * [92/12/10 16:20:16 af]
43 *
44 * Revision 2.12 92/08/03 17:31:57 jfriedl
45 * removed silly prototypes
46 * [92/08/02 jfriedl]
47 *
48 * Revision 2.11 92/05/21 17:07:41 jfriedl
49 * Removed unused variable 'bkpt' from db_clear_task_single_step().
50 * [92/05/16 jfriedl]
51 *
52 * Revision 2.10 92/04/01 10:54:05 rpd
53 * Added missing newline print in n/p command.
54 * [92/03/20 danner]
55 *
56 * Revision 2.9 91/10/09 16:02:08 af
57 * Added task parameter for user space break point, and changed
58 * db_find_breakpoint_here to db_find_thread_breakpoint_here
59 * to support thread based break point.
60 * Changed db_{set,clear}_single_step to db_{set,clear}_task_single
61 * _step to add task paramter and to maintain compatibility.
62 * [91/08/29 tak]
63 *
64 * Revision 2.8 91/07/09 23:16:01 danner
65 * Added logic to db_set_single_step not to set a breakpoint at the
66 * next sequential instruction if the current instruction is an
67 * unconditional transfer of flow of control instruction. This
68 * avoids problems with the debugger overwriting data or clobbering
69 * routines that the debugger itself might need. This is determined
70 * by calling the predicate inst_unconditional_flow_transfer. This
71 * predicate now needs to be defined for all architectures using
72 * the software single step.
73 *
74 * Added include of ddb/db_run.h, where all the STEP defines have been
75 * moved.
76 *
77 * [91/07/08 danner]
78 *
79 * Revision 2.7 91/06/06 17:03:51 jsb
80 * Removed redundant newlines and tabs.
81 * Added delta to instruction count printf.
82 * [91/05/25 10:55:01 jsb]
83 *
84 * Revision 2.6 91/05/14 15:35:39 mrt
85 * Correcting copyright
86 *
87 * Revision 2.5 91/02/05 17:06:58 mrt
88 * Changed to new Mach copyright
89 * [91/01/31 16:19:05 mrt]
90 *
91 * Revision 2.4 91/01/08 15:09:10 rpd
92 * Fixed bug in db_restart_at_pc.
93 * [90/12/07 rpd]
94 * Added STEP_COUNT and count option to db_continue_cmd.
95 * Changed db_stop_at_pc to return (modified) is_breakpoint.
96 * Fixed db_stop_at_pc to print newlines in the right places.
97 * [90/11/27 rpd]
98 *
99 * Revision 2.3 90/10/25 14:43:59 rwd
100 * Changed db_find_breakpoint to db_find_breakpoint_here.
101 * [90/10/18 rpd]
102 *
103 * Fixed db_set_single_step to pass regs to branch_taken.
104 * Added watchpoint argument to db_restart_at_pc.
105 * [90/10/17 rpd]
106 * Generalized the watchpoint support.
107 * [90/10/16 rwd]
108 * Added watchpoint support.
109 * [90/10/16 rpd]
110 *
111 * Revision 2.2 90/08/27 21:51:59 dbg
112 * Fixed names for single-step functions.
113 * [90/08/20 af]
114 * Reduce lint.
115 * [90/08/07 dbg]
116 * Created.
117 * [90/07/25 dbg]
118 *
119 */
120 /*
121 * Author: David B. Golub, Carnegie Mellon University
122 * Date: 7/90
123 */
124
125 /*
126 * Commands to run process.
127 */
128 #include <mach/boolean.h>
129 #include <machine/db_machdep.h>
130
131 #include <ddb/db_access.h>
132 #include <ddb/db_break.h>
133 #include <ddb/db_examine.h>
134 #include <ddb/db_lex.h>
135 #include <ddb/db_output.h>
136 #include <ddb/db_run.h>
137 #include <ddb/db_task_thread.h>
138 #include <ddb/db_watch.h>
139
140 int db_run_mode;
141
142 boolean_t db_sstep_print;
143 int db_loop_count;
144 int db_call_depth;
145
146 int db_inst_count;
147 int db_last_inst_count;
148 int db_load_count;
149 int db_store_count;
150
151 #ifndef db_set_single_step
152 void db_set_task_single_step(db_regs_t *, task_t); /* forward */
153 #else
154 #define db_set_task_single_step(regs,task) db_set_single_step(regs)
155 #endif
156 #ifndef db_clear_single_step
157 void db_clear_task_single_step(db_regs_t *, task_t);
158 #else
159 #define db_clear_task_single_step(regs,task) db_clear_single_step(regs)
160 #endif
161
162 boolean_t
163 db_stop_at_pc(
164 boolean_t *is_breakpoint,
165 task_t task)
166 {
167 register db_addr_t pc;
168 register db_thread_breakpoint_t bkpt;
169 boolean_t db_cond_check();
170
171 db_clear_task_single_step(DDB_REGS, task);
172 db_clear_breakpoints();
173 db_clear_watchpoints();
174 pc = PC_REGS(DDB_REGS);
175
176 #ifdef FIXUP_PC_AFTER_BREAK
177 if (*is_breakpoint) {
178 /*
179 * Breakpoint trap. Fix up the PC if the
180 * machine requires it.
181 */
182 FIXUP_PC_AFTER_BREAK
183 pc = PC_REGS(DDB_REGS);
184 }
185 #endif
186
187 /*
188 * Now check for a breakpoint at this address.
189 */
190 bkpt = db_find_thread_breakpoint_here(task, pc);
191 if (bkpt) {
192 if (db_cond_check(bkpt)) {
193 *is_breakpoint = TRUE;
194 return TRUE; /* stop here */
195 }
196 }
197 *is_breakpoint = FALSE;
198
199 if (db_run_mode == STEP_INVISIBLE) {
200 db_run_mode = STEP_CONTINUE;
201 return FALSE; /* continue */
202 }
203 if (db_run_mode == STEP_COUNT) {
204 return FALSE; /* continue */
205 }
206 if (db_run_mode == STEP_ONCE) {
207 if (--db_loop_count > 0) {
208 if (db_sstep_print) {
209 db_print_loc_and_inst(pc, task);
210 }
211 return FALSE; /* continue */
212 }
213 }
214 if (db_run_mode == STEP_RETURN) {
215 /* WARNING: the following assumes an instruction fits an int */
216 db_expr_t ins = db_get_task_value(pc, sizeof(int), FALSE, task);
217
218 /* continue until matching return */
219
220 if (!inst_trap_return(ins) &&
221 (!inst_return(ins) || --db_call_depth != 0)) {
222 if (db_sstep_print) {
223 if (inst_call(ins) || inst_return(ins)) {
224 register int i;
225
226 db_printf("[after %6d /%4d] ",
227 db_inst_count,
228 db_inst_count - db_last_inst_count);
229 db_last_inst_count = db_inst_count;
230 for (i = db_call_depth; --i > 0; )
231 db_printf(" ");
232 db_print_loc_and_inst(pc, task);
233 db_printf("\n");
234 }
235 }
236 if (inst_call(ins))
237 db_call_depth++;
238 return FALSE; /* continue */
239 }
240 }
241 if (db_run_mode == STEP_CALLT) {
242 /* WARNING: the following assumes an instruction fits an int */
243 db_expr_t ins = db_get_task_value(pc, sizeof(int), FALSE, task);
244
245 /* continue until call or return */
246
247 if (!inst_call(ins) &&
248 !inst_return(ins) &&
249 !inst_trap_return(ins)) {
250 return FALSE; /* continue */
251 }
252 }
253 if (db_find_breakpoint_here(task, pc))
254 return FALSE;
255
256 db_run_mode = STEP_NONE;
257 return TRUE;
258 }
259
260 void
261 db_restart_at_pc(
262 boolean_t watchpt,
263 task_t task)
264 {
265 register db_addr_t pc = PC_REGS(DDB_REGS);
266
267 if ((db_run_mode == STEP_COUNT) ||
268 (db_run_mode == STEP_RETURN) ||
269 (db_run_mode == STEP_CALLT)) {
270 db_expr_t ins;
271
272 /*
273 * We are about to execute this instruction,
274 * so count it now.
275 */
276
277 ins = db_get_task_value(pc, sizeof(int), FALSE, task);
278 db_inst_count++;
279 db_load_count += inst_load(ins);
280 db_store_count += inst_store(ins);
281 #ifdef SOFTWARE_SSTEP
282 /* Account for instructions in delay slots */
283 {
284 db_addr_t brpc;
285
286 brpc = next_instr_address(pc,1,task);
287 if ((brpc != pc) && (inst_branch(ins) || inst_call(ins))) {
288 /* Note: this ~assumes an instruction <= sizeof(int) */
289 ins = db_get_task_value(brpc, sizeof(int), FALSE, task);
290 db_inst_count++;
291 db_load_count += inst_load(ins);
292 db_store_count += inst_store(ins);
293 }
294 }
295 #endif /* SOFTWARE_SSTEP */
296 }
297
298 if (db_run_mode == STEP_CONTINUE) {
299 if (watchpt || db_find_breakpoint_here(task, pc)) {
300 /*
301 * Step over breakpoint/watchpoint.
302 */
303 db_run_mode = STEP_INVISIBLE;
304 db_set_task_single_step(DDB_REGS, task);
305 } else {
306 db_set_breakpoints();
307 db_set_watchpoints();
308 }
309 } else {
310 db_set_task_single_step(DDB_REGS, task);
311 }
312 }
313
314 void
315 db_single_step(
316 db_regs_t *regs,
317 task_t task)
318 {
319 if (db_run_mode == STEP_CONTINUE) {
320 db_run_mode = STEP_INVISIBLE;
321 db_set_task_single_step(regs, task);
322 }
323 }
324
325 #ifdef SOFTWARE_SSTEP
326 /*
327 * Software implementation of single-stepping.
328 * If your machine does not have a trace mode
329 * similar to the vax or sun ones you can use
330 * this implementation, done for the mips.
331 * Just define the above conditional and provide
332 * the functions/macros defined below.
333 *
334 * extern boolean_t
335 * inst_branch(), returns true if the instruction might branch
336 * extern unsigned
337 * branch_taken(), return the address the instruction might
338 * branch to
339 * db_getreg_val(); return the value of a user register,
340 * as indicated in the hardware instruction
341 * encoding, e.g. 8 for r8
342 *
343 * next_instr_address(pc,bd,task) returns the address of the first
344 * instruction following the one at "pc",
345 * which is either in the taken path of
346 * the branch (bd==1) or not. This is
347 * for machines (mips) with branch delays.
348 *
349 * A single-step may involve at most 2 breakpoints -
350 * one for branch-not-taken and one for branch taken.
351 * If one of these addresses does not already have a breakpoint,
352 * we allocate a breakpoint and save it here.
353 * These breakpoints are deleted on return.
354 */
355 db_breakpoint_t db_not_taken_bkpt = 0;
356 db_breakpoint_t db_taken_bkpt = 0;
357
358 db_breakpoint_t
359 db_find_temp_breakpoint(
360 task_t task,
361 db_addr_t addr)
362 {
363 if (db_taken_bkpt && (db_taken_bkpt->address == addr) &&
364 db_taken_bkpt->task == task)
365 return db_taken_bkpt;
366 if (db_not_taken_bkpt && (db_not_taken_bkpt->address == addr) &&
367 db_not_taken_bkpt->task == task)
368 return db_not_taken_bkpt;
369 return 0;
370 }
371
372 void
373 db_set_task_single_step(
374 register db_regs_t *regs,
375 task_t task)
376 {
377 db_addr_t pc = PC_REGS(regs), brpc;
378 register unsigned int inst;
379 register boolean_t unconditional;
380
381 /*
382 * User was stopped at pc, e.g. the instruction
383 * at pc was not executed.
384 */
385 inst = db_get_task_value(pc, sizeof(int), FALSE, task);
386 if (inst_branch(inst) || inst_call(inst)) {
387 extern db_expr_t getreg_val(unsigned int regno,
388 db_regs_t *regs);
389
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(task, brpc);
393 } else
394 db_taken_bkpt = 0;
395 pc = next_instr_address(pc,1,task);
396 }
397
398 /* check if this control flow instruction is an unconditional transfer */
399 unconditional = inst_unconditional_flow_transfer(inst);
400
401 pc = next_instr_address(pc,0,task);
402 /*
403 We only set the sequential breakpoint if previous instruction was not
404 an unconditional change of flow of control. If the previous instruction
405 is an unconditional change of flow of control, setting a breakpoint in the
406 next sequential location may set a breakpoint in data or in another routine,
407 which could screw up either the program or the debugger.
408 (Consider, for instance, that the next sequential instruction is the
409 start of a routine needed by the debugger.)
410 */
411 if (!unconditional && db_find_breakpoint_here(task, pc) == 0) {
412 db_not_taken_bkpt = db_set_temp_breakpoint(task, pc);
413 }
414 else
415 db_not_taken_bkpt = 0;
416 }
417
418 void
419 db_clear_task_single_step(
420 db_regs_t *regs,
421 task_t task)
422 {
423 if (db_taken_bkpt != 0) {
424 db_delete_temp_breakpoint(task, db_taken_bkpt);
425 db_taken_bkpt = 0;
426 }
427 if (db_not_taken_bkpt != 0) {
428 db_delete_temp_breakpoint(task, db_not_taken_bkpt);
429 db_not_taken_bkpt = 0;
430 }
431 }
432
433 #endif /* SOFTWARE_SSTEP */
434
435
436 extern int db_cmd_loop_done;
437
438 /* single-step */
439 /*ARGSUSED*/
440 void
441 db_single_step_cmd(
442 db_expr_t addr,
443 int have_addr,
444 db_expr_t count,
445 char * modif)
446 {
447 boolean_t print = FALSE;
448
449 if (count == -1)
450 count = 1;
451
452 if (modif[0] == 'p')
453 print = TRUE;
454
455 db_run_mode = STEP_ONCE;
456 db_loop_count = count;
457 db_sstep_print = print;
458 db_inst_count = 0;
459 db_last_inst_count = 0;
460 db_load_count = 0;
461 db_store_count = 0;
462
463 db_cmd_loop_done = 1;
464 }
465
466 /* trace and print until call/return */
467 /*ARGSUSED*/
468 void
469 db_trace_until_call_cmd(
470 db_expr_t addr,
471 int have_addr,
472 db_expr_t count,
473 char * modif)
474 {
475 boolean_t print = FALSE;
476
477 if (modif[0] == 'p')
478 print = TRUE;
479
480 db_run_mode = STEP_CALLT;
481 db_sstep_print = print;
482 db_inst_count = 0;
483 db_last_inst_count = 0;
484 db_load_count = 0;
485 db_store_count = 0;
486
487 db_cmd_loop_done = 1;
488 }
489
490 /*ARGSUSED*/
491 void
492 db_trace_until_matching_cmd(
493 db_expr_t addr,
494 int have_addr,
495 db_expr_t count,
496 char * modif)
497 {
498 boolean_t print = FALSE;
499
500 if (modif[0] == 'p')
501 print = TRUE;
502
503 db_run_mode = STEP_RETURN;
504 db_call_depth = 1;
505 db_sstep_print = print;
506 db_inst_count = 0;
507 db_last_inst_count = 0;
508 db_load_count = 0;
509 db_store_count = 0;
510
511 db_cmd_loop_done = 1;
512 }
513
514 /* continue */
515 /*ARGSUSED*/
516 void
517 db_continue_cmd(
518 db_expr_t addr,
519 int have_addr,
520 db_expr_t count,
521 char * modif)
522 {
523 if (modif[0] == 'c')
524 db_run_mode = STEP_COUNT;
525 else
526 db_run_mode = STEP_CONTINUE;
527 db_inst_count = 0;
528 db_last_inst_count = 0;
529 db_load_count = 0;
530 db_store_count = 0;
531
532 db_cmd_loop_done = 1;
533 }
534
535 boolean_t
536 db_in_single_step(void)
537 {
538 return (db_run_mode != STEP_NONE && db_run_mode != STEP_CONTINUE);
539 }
Cache object: af85bdc21ff5865a39a015cdf4d76536
|