|
|
1.1 root 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 [email protected]
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: * Author: David B. Golub, Carnegie Mellon University
28: * Date: 7/90
29: */
30:
31: #include "mach_kdb.h"
32: #if MACH_KDB
33:
34: /*
35: * Commands to run process.
36: */
37: #include <mach/boolean.h>
38: #include <machine/db_machdep.h>
39:
40: #include <ddb/db_lex.h>
41: #include <ddb/db_break.h>
42: #include <ddb/db_access.h>
43: #include <ddb/db_run.h>
44: #include <ddb/db_task_thread.h>
45:
46: int db_run_mode;
47:
48: boolean_t db_sstep_print;
49: int db_loop_count;
50: int db_call_depth;
51:
52: int db_inst_count;
53: int db_last_inst_count;
54: int db_load_count;
55: int db_store_count;
56:
57: #ifndef db_set_single_step
58: void db_set_task_single_step(/* db_regs_t *, task_t */);/* forward */
59: #else
60: #define db_set_task_single_step(regs,task) db_set_single_step(regs)
61: #endif
62: #ifndef db_clear_single_step
63: void db_clear_task_single_step(/* db_regs_t *, task_t */);
64: #else
65: #define db_clear_task_single_step(regs,task) db_clear_single_step(regs)
66: #endif
67:
68: boolean_t
69: db_stop_at_pc(is_breakpoint, task)
70: boolean_t *is_breakpoint;
71: task_t task;
72: {
73: register db_addr_t pc;
74: register db_thread_breakpoint_t bkpt;
75: boolean_t db_cond_check();
76:
77: db_clear_task_single_step(DDB_REGS, task);
78: db_clear_breakpoints();
79: db_clear_watchpoints();
80: pc = PC_REGS(DDB_REGS);
81:
82: #ifdef FIXUP_PC_AFTER_BREAK
83: if (*is_breakpoint) {
84: /*
85: * Breakpoint trap. Fix up the PC if the
86: * machine requires it.
87: */
88: FIXUP_PC_AFTER_BREAK
89: pc = PC_REGS(DDB_REGS);
90: }
91: #endif
92:
93: /*
94: * Now check for a breakpoint at this address.
95: */
96: bkpt = db_find_thread_breakpoint_here(task, pc);
97: if (bkpt) {
98: if (db_cond_check(bkpt)) {
99: *is_breakpoint = TRUE;
100: return (TRUE); /* stop here */
101: }
102: }
103: *is_breakpoint = FALSE;
104:
105: if (db_run_mode == STEP_INVISIBLE) {
106: db_run_mode = STEP_CONTINUE;
107: return (FALSE); /* continue */
108: }
109: if (db_run_mode == STEP_COUNT) {
110: return (FALSE); /* continue */
111: }
112: if (db_run_mode == STEP_ONCE) {
113: if (--db_loop_count > 0) {
114: if (db_sstep_print) {
115: db_print_loc_and_inst(pc, task);
116: }
117: return (FALSE); /* continue */
118: }
119: }
120: if (db_run_mode == STEP_RETURN) {
121: /* WARNING: the following assumes an instruction fits an int */
122: db_expr_t ins = db_get_task_value(pc, sizeof(int), FALSE, task);
123:
124: /* continue until matching return */
125:
126: if (!inst_trap_return(ins) &&
127: (!inst_return(ins) || --db_call_depth != 0)) {
128: if (db_sstep_print) {
129: if (inst_call(ins) || inst_return(ins)) {
130: register int i;
131:
132: db_printf("[after %6d /%4d] ",
133: db_inst_count,
134: db_inst_count - db_last_inst_count);
135: db_last_inst_count = db_inst_count;
136: for (i = db_call_depth; --i > 0; )
137: db_printf(" ");
138: db_print_loc_and_inst(pc, task);
139: db_printf("\n");
140: }
141: }
142: if (inst_call(ins))
143: db_call_depth++;
144: return (FALSE); /* continue */
145: }
146: }
147: if (db_run_mode == STEP_CALLT) {
148: /* WARNING: the following assumes an instruction fits an int */
149: db_expr_t ins = db_get_task_value(pc, sizeof(int), FALSE, task);
150:
151: /* continue until call or return */
152:
153: if (!inst_call(ins) &&
154: !inst_return(ins) &&
155: !inst_trap_return(ins)) {
156: return (FALSE); /* continue */
157: }
158: }
159: if (db_find_breakpoint_here(task, pc))
160: return(FALSE);
161: db_run_mode = STEP_NONE;
162: return (TRUE);
163: }
164:
165: void
166: db_restart_at_pc(watchpt, task)
167: boolean_t watchpt;
168: task_t task;
169: {
170: register db_addr_t pc = PC_REGS(DDB_REGS), brpc;
171:
172: if ((db_run_mode == STEP_COUNT) ||
173: (db_run_mode == STEP_RETURN) ||
174: (db_run_mode == STEP_CALLT)) {
175: db_expr_t ins;
176:
177: /*
178: * We are about to execute this instruction,
179: * so count it now.
180: */
181:
182: ins = db_get_task_value(pc, sizeof(int), FALSE, task);
183: db_inst_count++;
184: db_load_count += inst_load(ins);
185: db_store_count += inst_store(ins);
186: #ifdef SOFTWARE_SSTEP
187: /* Account for instructions in delay slots */
188: brpc = next_instr_address(pc,1,task);
189: if ((brpc != pc) && (inst_branch(ins) || inst_call(ins))) {
190: /* Note: this ~assumes an instruction <= sizeof(int) */
191: ins = db_get_task_value(brpc, sizeof(int), FALSE, task);
192: db_inst_count++;
193: db_load_count += inst_load(ins);
194: db_store_count += inst_store(ins);
195: }
196: #endif /* SOFTWARE_SSTEP */
197: }
198:
199: if (db_run_mode == STEP_CONTINUE) {
200: if (watchpt || db_find_breakpoint_here(task, pc)) {
201: /*
202: * Step over breakpoint/watchpoint.
203: */
204: db_run_mode = STEP_INVISIBLE;
205: db_set_task_single_step(DDB_REGS, task);
206: } else {
207: db_set_breakpoints();
208: db_set_watchpoints();
209: }
210: } else {
211: db_set_task_single_step(DDB_REGS, task);
212: }
213: }
214:
215: void
216: db_single_step(regs, task)
217: db_regs_t *regs;
218: task_t task;
219: {
220: if (db_run_mode == STEP_CONTINUE) {
221: db_run_mode = STEP_INVISIBLE;
222: db_set_task_single_step(regs, task);
223: }
224: }
225:
226: #ifdef SOFTWARE_SSTEP
227: /*
228: * Software implementation of single-stepping.
229: * If your machine does not have a trace mode
230: * similar to the vax or sun ones you can use
231: * this implementation, done for the mips.
232: * Just define the above conditional and provide
233: * the functions/macros defined below.
234: *
235: * extern boolean_t
236: * inst_branch(), returns true if the instruction might branch
237: * extern unsigned
238: * branch_taken(), return the address the instruction might
239: * branch to
240: * db_getreg_val(); return the value of a user register,
241: * as indicated in the hardware instruction
242: * encoding, e.g. 8 for r8
243: *
244: * next_instr_address(pc,bd,task) returns the address of the first
245: * instruction following the one at "pc",
246: * which is either in the taken path of
247: * the branch (bd==1) or not. This is
248: * for machines (mips) with branch delays.
249: *
250: * A single-step may involve at most 2 breakpoints -
251: * one for branch-not-taken and one for branch taken.
252: * If one of these addresses does not already have a breakpoint,
253: * we allocate a breakpoint and save it here.
254: * These breakpoints are deleted on return.
255: */
256: db_breakpoint_t db_not_taken_bkpt = 0;
257: db_breakpoint_t db_taken_bkpt = 0;
258:
259: db_breakpoint_t
260: db_find_temp_breakpoint(task, addr)
261: task_t task;
262: db_addr_t addr;
263: {
264: if (db_taken_bkpt && (db_taken_bkpt->address == addr) &&
265: db_taken_bkpt->task == task)
266: return db_taken_bkpt;
267: if (db_not_taken_bkpt && (db_not_taken_bkpt->address == addr) &&
268: db_not_taken_bkpt->task == task)
269: return db_not_taken_bkpt;
270: return 0;
271: }
272:
273: void
274: db_set_task_single_step(regs, task)
275: register db_regs_t *regs;
276: task_t task;
277: {
278: db_addr_t pc = PC_REGS(regs), brpc;
279: register unsigned int inst;
280: register boolean_t unconditional;
281:
282: /*
283: * User was stopped at pc, e.g. the instruction
284: * at pc was not executed.
285: */
286: inst = db_get_task_value(pc, sizeof(int), FALSE, task);
287: if (inst_branch(inst) || inst_call(inst)) {
288: extern db_expr_t getreg_val();
289:
290: brpc = branch_taken(inst, pc, getreg_val, regs);
291: if (brpc != pc) { /* self-branches are hopeless */
292: db_taken_bkpt = db_set_temp_breakpoint(task, brpc);
293: } else
294: db_taken_bkpt = 0;
295: pc = next_instr_address(pc,1,task);
296: }
297:
298: /* check if this control flow instruction is an unconditional transfer */
299: unconditional = inst_unconditional_flow_transfer(inst);
300:
301: pc = next_instr_address(pc,0,task);
302: /*
303: We only set the sequential breakpoint if previous instruction was not
304: an unconditional change of flow of control. If the previous instruction
305: is an unconditional change of flow of control, setting a breakpoint in the
306: next sequential location may set a breakpoint in data or in another routine,
307: which could screw up either the program or the debugger.
308: (Consider, for instance, that the next sequential instruction is the
309: start of a routine needed by the debugger.)
310: */
311: if (!unconditional && db_find_breakpoint_here(task, pc) == 0) {
312: db_not_taken_bkpt = db_set_temp_breakpoint(task, pc);
313: }
314: else
315: db_not_taken_bkpt = 0;
316: }
317:
318: void
319: db_clear_task_single_step(regs, task)
320: db_regs_t *regs;
321: task_t task;
322: {
323: if (db_taken_bkpt != 0) {
324: db_delete_temp_breakpoint(task, db_taken_bkpt);
325: db_taken_bkpt = 0;
326: }
327: if (db_not_taken_bkpt != 0) {
328: db_delete_temp_breakpoint(task, db_not_taken_bkpt);
329: db_not_taken_bkpt = 0;
330: }
331: }
332:
333: #endif /* SOFTWARE_SSTEP */
334:
335:
336: extern int db_cmd_loop_done;
337:
338: /* single-step */
339: /*ARGSUSED*/
340: void
341: db_single_step_cmd(addr, have_addr, count, modif)
342: db_expr_t addr;
343: int have_addr;
344: db_expr_t count;
345: char * modif;
346: {
347: boolean_t print = FALSE;
348:
349: if (count == -1)
350: count = 1;
351:
352: if (modif[0] == 'p')
353: print = TRUE;
354:
355: db_run_mode = STEP_ONCE;
356: db_loop_count = count;
357: db_sstep_print = print;
358: db_inst_count = 0;
359: db_last_inst_count = 0;
360: db_load_count = 0;
361: db_store_count = 0;
362:
363: db_cmd_loop_done = 1;
364: }
365:
366: /* trace and print until call/return */
367: /*ARGSUSED*/
368: void
369: db_trace_until_call_cmd(addr, have_addr, count, modif)
370: db_expr_t addr;
371: int have_addr;
372: db_expr_t count;
373: char * modif;
374: {
375: boolean_t print = FALSE;
376:
377: if (modif[0] == 'p')
378: print = TRUE;
379:
380: db_run_mode = STEP_CALLT;
381: db_sstep_print = print;
382: db_inst_count = 0;
383: db_last_inst_count = 0;
384: db_load_count = 0;
385: db_store_count = 0;
386:
387: db_cmd_loop_done = 1;
388: }
389:
390: /*ARGSUSED*/
391: void
392: db_trace_until_matching_cmd(addr, have_addr, count, modif)
393: db_expr_t addr;
394: int have_addr;
395: db_expr_t count;
396: char * modif;
397: {
398: boolean_t print = FALSE;
399:
400: if (modif[0] == 'p')
401: print = TRUE;
402:
403: db_run_mode = STEP_RETURN;
404: db_call_depth = 1;
405: db_sstep_print = print;
406: db_inst_count = 0;
407: db_last_inst_count = 0;
408: db_load_count = 0;
409: db_store_count = 0;
410:
411: db_cmd_loop_done = 1;
412: }
413:
414: /* continue */
415: /*ARGSUSED*/
416: void
417: db_continue_cmd(addr, have_addr, count, modif)
418: db_expr_t addr;
419: int have_addr;
420: db_expr_t count;
421: char * modif;
422: {
423: if (modif[0] == 'c')
424: db_run_mode = STEP_COUNT;
425: else
426: db_run_mode = STEP_CONTINUE;
427: db_inst_count = 0;
428: db_last_inst_count = 0;
429: db_load_count = 0;
430: db_store_count = 0;
431:
432: db_cmd_loop_done = 1;
433: }
434:
435: boolean_t
436: db_in_single_step()
437: {
438: return(db_run_mode != STEP_NONE && db_run_mode != STEP_CONTINUE);
439: }
440:
441: #endif MACH_KDB
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.