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