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 * Author: David B. Golub, Carnegie Mellon University 28 * Date: 7/90 29 */ 30 31 /* 32 * Commands to run process. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include <sys/param.h> 39 #include <sys/kdb.h> 40 #include <sys/proc.h> 41 42 #include <machine/kdb.h> 43 #include <machine/pcb.h> 44 45 #include <vm/vm.h> 46 47 #include <ddb/ddb.h> 48 #include <ddb/db_break.h> 49 #include <ddb/db_access.h> 50 51 static int db_run_mode; 52 #define STEP_NONE 0 53 #define STEP_ONCE 1 54 #define STEP_RETURN 2 55 #define STEP_CALLT 3 56 #define STEP_CONTINUE 4 57 #define STEP_INVISIBLE 5 58 #define STEP_COUNT 6 59 60 static boolean_t db_sstep_print; 61 static int db_loop_count; 62 static int db_call_depth; 63 64 int db_inst_count; 65 int db_load_count; 66 int db_store_count; 67 68 #ifndef db_set_single_step 69 void db_set_single_step(void); 70 #endif 71 #ifndef db_clear_single_step 72 void db_clear_single_step(void); 73 #endif 74 75 #ifdef SOFTWARE_SSTEP 76 db_breakpoint_t db_not_taken_bkpt = 0; 77 db_breakpoint_t db_taken_bkpt = 0; 78 #endif 79 80 boolean_t 81 db_stop_at_pc(is_breakpoint) 82 boolean_t *is_breakpoint; 83 { 84 register db_addr_t pc; 85 register db_breakpoint_t bkpt; 86 87 pc = PC_REGS(); 88 #ifdef SOFTWARE_SSTEP 89 if ((db_not_taken_bkpt != 0 && pc == db_not_taken_bkpt->address) 90 || (db_taken_bkpt != 0 && pc == db_taken_bkpt->address)) 91 *is_breakpoint = FALSE; 92 #endif 93 94 db_clear_single_step(); 95 db_clear_breakpoints(); 96 db_clear_watchpoints(); 97 98 #ifdef FIXUP_PC_AFTER_BREAK 99 if (*is_breakpoint) { 100 /* 101 * Breakpoint trap. Fix up the PC if the 102 * machine requires it. 103 */ 104 FIXUP_PC_AFTER_BREAK 105 pc = PC_REGS(); 106 } 107 #endif 108 109 /* 110 * Now check for a breakpoint at this address. 111 */ 112 bkpt = db_find_breakpoint_here(pc); 113 if (bkpt) { 114 if (--bkpt->count == 0) { 115 bkpt->count = bkpt->init_count; 116 *is_breakpoint = TRUE; 117 return (TRUE); /* stop here */ 118 } 119 } else if (*is_breakpoint) { 120 #ifdef BKPT_SKIP 121 BKPT_SKIP; 122 #endif 123 } 124 125 *is_breakpoint = FALSE; 126 127 if (db_run_mode == STEP_INVISIBLE) { 128 db_run_mode = STEP_CONTINUE; 129 return (FALSE); /* continue */ 130 } 131 if (db_run_mode == STEP_COUNT) { 132 return (FALSE); /* continue */ 133 } 134 if (db_run_mode == STEP_ONCE) { 135 if (--db_loop_count > 0) { 136 if (db_sstep_print) { 137 db_printf("\t\t"); 138 db_print_loc_and_inst(pc); 139 db_printf("\n"); 140 } 141 return (FALSE); /* continue */ 142 } 143 } 144 if (db_run_mode == STEP_RETURN) { 145 /* continue until matching return */ 146 db_expr_t ins; 147 148 ins = db_get_value(pc, sizeof(int), FALSE); 149 if (!inst_trap_return(ins) && 150 (!inst_return(ins) || --db_call_depth != 0)) { 151 if (db_sstep_print) { 152 if (inst_call(ins) || inst_return(ins)) { 153 register int i; 154 155 db_printf("[after %6d] ", db_inst_count); 156 for (i = db_call_depth; --i > 0; ) 157 db_printf(" "); 158 db_print_loc_and_inst(pc); 159 db_printf("\n"); 160 } 161 } 162 if (inst_call(ins)) 163 db_call_depth++; 164 return (FALSE); /* continue */ 165 } 166 } 167 if (db_run_mode == STEP_CALLT) { 168 /* continue until call or return */ 169 db_expr_t ins; 170 171 ins = db_get_value(pc, sizeof(int), FALSE); 172 if (!inst_call(ins) && 173 !inst_return(ins) && 174 !inst_trap_return(ins)) { 175 return (FALSE); /* continue */ 176 } 177 } 178 db_run_mode = STEP_NONE; 179 return (TRUE); 180 } 181 182 void 183 db_restart_at_pc(watchpt) 184 boolean_t watchpt; 185 { 186 register db_addr_t pc = PC_REGS(); 187 188 if ((db_run_mode == STEP_COUNT) || 189 (db_run_mode == STEP_RETURN) || 190 (db_run_mode == STEP_CALLT)) { 191 db_expr_t ins; 192 193 /* 194 * We are about to execute this instruction, 195 * so count it now. 196 */ 197 198 ins = db_get_value(pc, sizeof(int), FALSE); 199 db_inst_count++; 200 db_load_count += inst_load(ins); 201 db_store_count += inst_store(ins); 202 #ifdef SOFTWARE_SSTEP 203 /* XXX works on mips, but... */ 204 if (inst_branch(ins) || inst_call(ins)) { 205 ins = db_get_value(next_instr_address(pc,1), 206 sizeof(int), FALSE); 207 db_inst_count++; 208 db_load_count += inst_load(ins); 209 db_store_count += inst_store(ins); 210 } 211 #endif /* SOFTWARE_SSTEP */ 212 } 213 214 if (db_run_mode == STEP_CONTINUE) { 215 if (watchpt || db_find_breakpoint_here(pc)) { 216 /* 217 * Step over breakpoint/watchpoint. 218 */ 219 db_run_mode = STEP_INVISIBLE; 220 db_set_single_step(); 221 } else { 222 db_set_breakpoints(); 223 db_set_watchpoints(); 224 } 225 } else { 226 db_set_single_step(); 227 } 228 } 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 */ 260 261 void 262 db_set_single_step(void) 263 { 264 db_addr_t pc = PC_REGS(), brpc; 265 unsigned inst; 266 267 /* 268 * User was stopped at pc, e.g. the instruction 269 * at pc was not executed. 270 */ 271 inst = db_get_value(pc, sizeof(int), FALSE); 272 if (inst_branch(inst) || inst_call(inst)) { 273 brpc = branch_taken(inst, pc); 274 if (brpc != pc) { /* self-branches are hopeless */ 275 db_taken_bkpt = db_set_temp_breakpoint(brpc); 276 } 277 pc = next_instr_address(pc, 1); 278 } 279 pc = next_instr_address(pc, 0); 280 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 281 } 282 283 void 284 db_clear_single_step(void) 285 { 286 287 if (db_not_taken_bkpt != 0) { 288 db_delete_temp_breakpoint(db_not_taken_bkpt); 289 db_not_taken_bkpt = 0; 290 } 291 if (db_taken_bkpt != 0) { 292 db_delete_temp_breakpoint(db_taken_bkpt); 293 db_taken_bkpt = 0; 294 } 295 } 296 297 #endif /* SOFTWARE_SSTEP */ 298 299 extern int db_cmd_loop_done; 300 301 /* single-step */ 302 /*ARGSUSED*/ 303 void 304 db_single_step_cmd(addr, have_addr, count, modif) 305 db_expr_t addr; 306 boolean_t have_addr; 307 db_expr_t count; 308 char * modif; 309 { 310 boolean_t print = FALSE; 311 312 if (count == -1) 313 count = 1; 314 315 if (modif[0] == 'p') 316 print = TRUE; 317 318 db_run_mode = STEP_ONCE; 319 db_loop_count = count; 320 db_sstep_print = print; 321 db_inst_count = 0; 322 db_load_count = 0; 323 db_store_count = 0; 324 325 db_cmd_loop_done = 1; 326 } 327 328 /* trace and print until call/return */ 329 /*ARGSUSED*/ 330 void 331 db_trace_until_call_cmd(addr, have_addr, count, modif) 332 db_expr_t addr; 333 boolean_t have_addr; 334 db_expr_t count; 335 char * modif; 336 { 337 boolean_t print = FALSE; 338 339 if (modif[0] == 'p') 340 print = TRUE; 341 342 db_run_mode = STEP_CALLT; 343 db_sstep_print = print; 344 db_inst_count = 0; 345 db_load_count = 0; 346 db_store_count = 0; 347 348 db_cmd_loop_done = 1; 349 } 350 351 /*ARGSUSED*/ 352 void 353 db_trace_until_matching_cmd(addr, have_addr, count, modif) 354 db_expr_t addr; 355 boolean_t have_addr; 356 db_expr_t count; 357 char * modif; 358 { 359 boolean_t print = FALSE; 360 361 if (modif[0] == 'p') 362 print = TRUE; 363 364 db_run_mode = STEP_RETURN; 365 db_call_depth = 1; 366 db_sstep_print = print; 367 db_inst_count = 0; 368 db_load_count = 0; 369 db_store_count = 0; 370 371 db_cmd_loop_done = 1; 372 } 373 374 /* continue */ 375 /*ARGSUSED*/ 376 void 377 db_continue_cmd(addr, have_addr, count, modif) 378 db_expr_t addr; 379 boolean_t have_addr; 380 db_expr_t count; 381 char * modif; 382 { 383 if (modif[0] == 'c') 384 db_run_mode = STEP_COUNT; 385 else 386 db_run_mode = STEP_CONTINUE; 387 db_inst_count = 0; 388 db_load_count = 0; 389 db_store_count = 0; 390 391 db_cmd_loop_done = 1; 392 } 393