|
|
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.