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