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