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