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