|
|
1.1 root 1: /*
2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /*
23: * @OSF_COPYRIGHT@
24: */
25: /*
26: * Mach Operating System
27: * Copyright (c) 1991,1990 Carnegie Mellon University
28: * All Rights Reserved.
29: *
30: * Permission to use, copy, modify and distribute this software and its
31: * documentation is hereby granted, provided that both the copyright
32: * notice and this permission notice appear in all copies of the
33: * software, derivative works or modified versions, and any portions
34: * thereof, and that both notices appear in supporting documentation.
35: *
36: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
37: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
38: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
39: *
40: * Carnegie Mellon requests users of this software to return to
41: *
42: * Software Distribution Coordinator or [email protected]
43: * School of Computer Science
44: * Carnegie Mellon University
45: * Pittsburgh PA 15213-3890
46: *
47: * any improvements or extensions that they make and grant Carnegie Mellon
48: * the rights to redistribute these changes.
49: */
50: /*
51: */
52: /*
53: * Author: David B. Golub, Carnegie Mellon University
54: * Date: 7/90
55: */
56:
57: /*
58: * Commands to run process.
59: */
60: #include <mach/boolean.h>
61: #include <machine/db_machdep.h>
62:
63: #include <ddb/db_lex.h>
64: #include <ddb/db_break.h>
65: #include <ddb/db_access.h>
66: #include <ddb/db_run.h>
67: #include <ddb/db_cond.h>
68: #include <ddb/db_examine.h>
69: #include <ddb/db_output.h> /* For db_printf() */
70: #include <ddb/db_watch.h>
71: #include <kern/misc_protos.h>
72: #include <kern/debug.h>
73:
74: boolean_t db_sstep_print;
75: int db_loop_count;
76: int db_call_depth;
77:
78: int db_inst_count;
79: int db_last_inst_count;
80: int db_load_count;
81: int db_store_count;
82: int db_max_inst_count = 1000;
83:
84: #ifndef db_set_single_step
85: void db_set_task_single_step(
86: register db_regs_t *regs,
87: task_t task);
88: #else
89: #define db_set_task_single_step(regs,task) db_set_single_step(regs)
90: #endif
91: #ifndef db_clear_single_step
92: void db_clear_task_single_step(
93: db_regs_t *regs,
94: task_t task);
95: #else
96: #define db_clear_task_single_step(regs,task) db_clear_single_step(regs)
97: #endif
98:
99: extern jmp_buf_t *db_recover;
100: boolean_t db_step_again(void);
101:
102: boolean_t
103: db_stop_at_pc(
104: boolean_t *is_breakpoint,
105: task_t task,
106: task_t space)
107: {
108: register db_addr_t pc;
109: register db_thread_breakpoint_t bkpt;
110:
111: db_clear_task_single_step(DDB_REGS, space);
112: db_clear_breakpoints();
113: db_clear_watchpoints();
114: pc = PC_REGS(DDB_REGS);
115:
116: #ifdef FIXUP_PC_AFTER_BREAK
117: if (*is_breakpoint) {
118: /*
119: * Breakpoint trap. Fix up the PC if the
120: * machine requires it.
121: */
122: FIXUP_PC_AFTER_BREAK
123: pc = PC_REGS(DDB_REGS);
124: }
125: #endif
126:
127: /*
128: * Now check for a breakpoint at this address.
129: */
130: bkpt = db_find_thread_breakpoint_here(space, pc);
131: if (bkpt) {
132: if (db_cond_check(bkpt)) {
133: *is_breakpoint = TRUE;
134: return (TRUE); /* stop here */
135: }
136: }
137: *is_breakpoint = FALSE;
138:
139: if (db_run_mode == STEP_INVISIBLE) {
140: db_run_mode = STEP_CONTINUE;
141: return (FALSE); /* continue */
142: }
143: if (db_run_mode == STEP_COUNT) {
144: return (FALSE); /* continue */
145: }
146: if (db_run_mode == STEP_ONCE) {
147: if (--db_loop_count > 0) {
148: if (db_sstep_print) {
149: db_print_loc_and_inst(pc, task);
150: }
151: return (FALSE); /* continue */
152: }
153: }
154: if (db_run_mode == STEP_RETURN) {
155: jmp_buf_t *prev;
156: jmp_buf_t db_jmpbuf;
157: /* WARNING: the following assumes an instruction fits an int */
158: db_expr_t ins = db_get_task_value(pc, sizeof(int), FALSE, space);
159:
160: /* continue until matching return */
161:
162: prev = db_recover;
163: if (_setjmp(db_recover = &db_jmpbuf) == 0) {
164: if (!inst_trap_return(ins) &&
165: (!inst_return(ins) || --db_call_depth != 0)) {
166: if (db_sstep_print) {
167: if (inst_call(ins) || inst_return(ins)) {
168: register int i;
169:
170: db_printf("[after %6d /%4d] ",
171: db_inst_count,
172: db_inst_count - db_last_inst_count);
173: db_last_inst_count = db_inst_count;
174: for (i = db_call_depth; --i > 0; )
175: db_printf(" ");
176: db_print_loc_and_inst(pc, task);
177: db_printf("\n");
178: }
179: }
180: if (inst_call(ins))
181: db_call_depth++;
182: db_recover = prev;
183: if (db_step_again())
184: return (FALSE); /* continue */
185: }
186: }
187: db_recover = prev;
188: }
189: if (db_run_mode == STEP_CALLT) {
190: /* WARNING: the following assumes an instruction fits an int */
191: db_expr_t ins = db_get_task_value(pc, sizeof(int), FALSE, space);
192:
193: /* continue until call or return */
194:
195: if (!inst_call(ins) &&
196: !inst_return(ins) &&
197: !inst_trap_return(ins)) {
198: if (db_step_again())
199: return (FALSE); /* continue */
200: }
201: }
202: if (db_find_breakpoint_here(space, pc))
203: return(FALSE);
204: db_run_mode = STEP_NONE;
205: return (TRUE);
206: }
207:
208: void
209: db_restart_at_pc(
210: boolean_t watchpt,
211: task_t task)
212: {
213: register db_addr_t pc = PC_REGS(DDB_REGS), brpc;
214:
215: if ((db_run_mode == STEP_COUNT) ||
216: (db_run_mode == STEP_RETURN) ||
217: (db_run_mode == STEP_CALLT)) {
218: db_expr_t ins;
219:
220: /*
221: * We are about to execute this instruction,
222: * so count it now.
223: */
224:
225: ins = db_get_task_value(pc, sizeof(int), FALSE, task);
226: db_inst_count++;
227: db_load_count += db_inst_load(ins);
228: db_store_count += db_inst_store(ins);
229: #ifdef SOFTWARE_SSTEP
230: /* Account for instructions in delay slots */
231: brpc = next_instr_address(pc,1,task);
232: if ((brpc != pc) && (inst_branch(ins) || inst_call(ins))) {
233: /* Note: this ~assumes an instruction <= sizeof(int) */
234: ins = db_get_task_value(brpc, sizeof(int), FALSE, task);
235: db_inst_count++;
236: db_load_count += db_inst_load(ins);
237: db_store_count += db_inst_store(ins);
238: }
239: #endif /* SOFTWARE_SSTEP */
240: }
241:
242: if (db_run_mode == STEP_CONTINUE) {
243: if (watchpt || db_find_breakpoint_here(task, pc)) {
244: /*
245: * Step over breakpoint/watchpoint.
246: */
247: db_run_mode = STEP_INVISIBLE;
248: db_set_task_single_step(DDB_REGS, task);
249: } else {
250: db_set_breakpoints();
251: db_set_watchpoints();
252: }
253: } else {
254: db_set_task_single_step(DDB_REGS, task);
255: }
256: }
257:
258: /*
259: * 'n' and 'u' commands might never return.
260: * Limit the maximum number of steps.
261: */
262:
263: boolean_t
264: db_step_again(void)
265: {
266: if (db_inst_count && !(db_inst_count%db_max_inst_count)) {
267: char c;
268: db_printf("%d instructions, continue ? (y/n) ",
269: db_inst_count);
270: c = cngetc();
271: db_printf("\n");
272: if(c == 'n')
273: return(FALSE);
274: }
275: return(TRUE);
276: }
277:
278: void
279: db_single_step(
280: db_regs_t *regs,
281: task_t task)
282: {
283: if (db_run_mode == STEP_CONTINUE) {
284: db_run_mode = STEP_INVISIBLE;
285: db_set_task_single_step(regs, task);
286: }
287: }
288:
289: #ifdef SOFTWARE_SSTEP
290: /*
291: * Software implementation of single-stepping.
292: * If your machine does not have a trace mode
293: * similar to the vax or sun ones you can use
294: * this implementation, done for the mips.
295: * Just define the above conditional and provide
296: * the functions/macros defined below.
297: *
298: * extern boolean_t
299: * inst_branch(), returns true if the instruction might branch
300: * extern unsigned
301: * branch_taken(), return the address the instruction might
302: * branch to
303: * db_getreg_val(); return the value of a user register,
304: * as indicated in the hardware instruction
305: * encoding, e.g. 8 for r8
306: *
307: * next_instr_address(pc,bd,task) returns the address of the first
308: * instruction following the one at "pc",
309: * which is either in the taken path of
310: * the branch (bd==1) or not. This is
311: * for machines (mips) with branch delays.
312: *
313: * A single-step may involve at most 2 breakpoints -
314: * one for branch-not-taken and one for branch taken.
315: * If one of these addresses does not already have a breakpoint,
316: * we allocate a breakpoint and save it here.
317: * These breakpoints are deleted on return.
318: */
319: db_breakpoint_t db_not_taken_bkpt = 0;
320: db_breakpoint_t db_taken_bkpt = 0;
321:
322: db_breakpoint_t
323: db_find_temp_breakpoint(
324: task_t task,
325: db_addr_t addr)
326: {
327: if (db_taken_bkpt && (db_taken_bkpt->address == addr) &&
328: db_taken_bkpt->task == task)
329: return db_taken_bkpt;
330: if (db_not_taken_bkpt && (db_not_taken_bkpt->address == addr) &&
331: db_not_taken_bkpt->task == task)
332: return db_not_taken_bkpt;
333: return 0;
334: }
335:
336: void
337: db_set_task_single_step(
338: register db_regs_t *regs,
339: task_t task)
340: {
341: db_addr_t pc = PC_REGS(regs), brpc;
342: register unsigned int inst;
343: register boolean_t unconditional;
344:
345: /*
346: * User was stopped at pc, e.g. the instruction
347: * at pc was not executed.
348: */
349: inst = db_get_task_value(pc, sizeof(int), FALSE, task);
350: if (inst_branch(inst) || inst_call(inst)) {
351: extern db_expr_t getreg_val(); /* XXX -- need prototype! */
352:
353: brpc = branch_taken(inst, pc, getreg_val, (unsigned char*)regs);
354: if (brpc != pc) { /* self-branches are hopeless */
355: db_taken_bkpt = db_set_temp_breakpoint(task, brpc);
356: } else
357: db_taken_bkpt = 0;
358: pc = next_instr_address(pc,1,task);
359: } else
360: pc = next_instr_address(pc,0,task);
361:
362: /*
363: * check if this control flow instruction is an
364: * unconditional transfer
365: */
366:
367: unconditional = inst_unconditional_flow_transfer(inst);
368:
369: /*
370: We only set the sequential breakpoint if previous instruction was not
371: an unconditional change of flow of control. If the previous instruction
372: is an unconditional change of flow of control, setting a breakpoint in the
373: next sequential location may set a breakpoint in data or in another routine,
374: which could screw up either the program or the debugger.
375: (Consider, for instance, that the next sequential instruction is the
376: start of a routine needed by the debugger.)
377: */
378: if (!unconditional && db_find_breakpoint_here(task, pc) == 0 &&
379: (db_taken_bkpt == 0 || db_taken_bkpt->address != pc)) {
380: db_not_taken_bkpt = db_set_temp_breakpoint(task, pc);
381: } else
382: db_not_taken_bkpt = 0;
383: }
384:
385: void
386: db_clear_task_single_step(
387: db_regs_t *regs,
388: task_t task)
389: {
390: if (db_taken_bkpt != 0) {
391: db_delete_temp_breakpoint(task, db_taken_bkpt);
392: db_taken_bkpt = 0;
393: }
394: if (db_not_taken_bkpt != 0) {
395: db_delete_temp_breakpoint(task, db_not_taken_bkpt);
396: db_not_taken_bkpt = 0;
397: }
398: }
399:
400: #endif /* SOFTWARE_SSTEP */
401:
402: extern int db_cmd_loop_done;
403:
404: /* single-step */
405: void
406: db_single_step_cmd(
407: db_expr_t addr,
408: int have_addr,
409: db_expr_t count,
410: char * modif)
411: {
412: boolean_t print = FALSE;
413:
414: if (count == -1)
415: count = 1;
416:
417: if (modif[0] == 'p')
418: print = TRUE;
419:
420: db_run_mode = STEP_ONCE;
421: db_loop_count = count;
422: db_sstep_print = print;
423: db_inst_count = 0;
424: db_last_inst_count = 0;
425: db_load_count = 0;
426: db_store_count = 0;
427:
428: db_cmd_loop_done = 1;
429: }
430:
431: /* trace and print until call/return */
432: void
433: db_trace_until_call_cmd(
434: db_expr_t addr,
435: int have_addr,
436: db_expr_t count,
437: char * modif)
438: {
439: boolean_t print = FALSE;
440:
441: if (modif[0] == 'p')
442: print = TRUE;
443:
444: db_run_mode = STEP_CALLT;
445: db_sstep_print = print;
446: db_inst_count = 0;
447: db_last_inst_count = 0;
448: db_load_count = 0;
449: db_store_count = 0;
450:
451: db_cmd_loop_done = 1;
452: }
453:
454: void
455: db_trace_until_matching_cmd(
456: db_expr_t addr,
457: int have_addr,
458: db_expr_t count,
459: char * modif)
460: {
461: boolean_t print = FALSE;
462:
463: if (modif[0] == 'p')
464: print = TRUE;
465:
466: db_run_mode = STEP_RETURN;
467: db_call_depth = 1;
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: /* continue */
478: void
479: db_continue_cmd(
480: db_expr_t addr,
481: int have_addr,
482: db_expr_t count,
483: char * modif)
484: {
485: /*
486: * Though "cont/c" works fairly well, it's not really robust
487: * enough to use in arbitrary situations, so disable it.
488: * (Doesn't seem cost-effective to debug and fix what ails
489: * it.)
490: */
491: #if 0
492: if (modif[0] == 'c')
493: db_run_mode = STEP_COUNT;
494: else
495: db_run_mode = STEP_CONTINUE;
496: #else
497: db_run_mode = STEP_CONTINUE;
498: #endif
499: db_inst_count = 0;
500: db_last_inst_count = 0;
501: db_load_count = 0;
502: db_store_count = 0;
503:
504: db_cmd_loop_done = 1;
505: }
506:
507: /* gdb */
508: void
509: db_continue_gdb(
510: db_expr_t addr,
511: int have_addr,
512: db_expr_t count,
513: char * modif)
514: {
515: #if defined(__ppc__)
516: db_to_gdb();
517: #endif
518: db_run_mode = STEP_CONTINUE;
519: db_inst_count = 0;
520: db_last_inst_count = 0;
521: db_load_count = 0;
522: db_store_count = 0;
523:
524: db_cmd_loop_done = 1;
525: }
526:
527:
528:
529: boolean_t
530: db_in_single_step(void)
531: {
532: return(db_run_mode != STEP_NONE && db_run_mode != STEP_CONTINUE);
533: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.