1/*- 2 * SPDX-License-Identifier: MIT-CMU 3 * 4 * Mach Operating System 5 * Copyright (c) 1991,1990 Carnegie Mellon University 6 * All Rights Reserved. 7 * 8 * Permission to use, copy, modify and distribute this software and its 9 * documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation. 13 * 14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 17 * 18 * Carnegie Mellon requests users of this software to return to 19 * 20 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 21 * School of Computer Science 22 * Carnegie Mellon University 23 * Pittsburgh PA 15213-3890 24 * 25 * any improvements or extensions that they make and grant Carnegie the 26 * rights to redistribute these changes. 27 */ 28/* 29 * Author: David B. Golub, Carnegie Mellon University 30 * Date: 7/90 31 */ 32 33/* 34 * Commands to run process. 35 */ 36 37#include <sys/cdefs.h> 38__FBSDID("$FreeBSD$"); 39 40#include <sys/param.h> 41#include <sys/kdb.h> 42#include <sys/proc.h> 43#include <sys/systm.h> 44 45#include <machine/kdb.h> 46#include <machine/pcb.h> 47 48#include <vm/vm.h> 49 50#include <ddb/ddb.h> 51#include <ddb/db_access.h> 52#include <ddb/db_break.h> 53#include <ddb/db_command.h> 54 55#define STEP_ONCE 1 56#define STEP_RETURN 2 57#define STEP_CALLT 3 58#define STEP_CONTINUE 4 59#define STEP_INVISIBLE 5 60#define STEP_COUNT 6 61static int db_run_mode = STEP_CONTINUE; 62 63static bool db_sstep_multiple; 64static bool db_sstep_print; 65static int db_loop_count; 66static int db_call_depth; 67 68int db_inst_count; 69int db_load_count; 70int db_store_count; 71 72#ifdef SOFTWARE_SSTEP 73db_breakpoint_t db_not_taken_bkpt = 0; 74db_breakpoint_t db_taken_bkpt = 0; 75#endif 76 77#ifndef db_set_single_step 78void db_set_single_step(void); 79#endif 80#ifndef db_clear_single_step 81void db_clear_single_step(void); 82#endif 83#ifndef db_pc_is_singlestep 84static bool 85db_pc_is_singlestep(db_addr_t pc) 86{ 87#ifdef SOFTWARE_SSTEP 88 if ((db_not_taken_bkpt != 0 && pc == db_not_taken_bkpt->address) 89 || (db_taken_bkpt != 0 && pc == db_taken_bkpt->address)) 90 return (true); 91#endif 92 return (false); 93} 94#endif 95 96bool 97db_stop_at_pc(int type, int code, bool *is_breakpoint, bool *is_watchpoint) 98{ 99 db_addr_t pc; 100 db_breakpoint_t bkpt; 101 102 *is_breakpoint = IS_BREAKPOINT_TRAP(type, code); 103 *is_watchpoint = IS_WATCHPOINT_TRAP(type, code); 104 pc = PC_REGS(); 105 if (db_pc_is_singlestep(pc)) 106 *is_breakpoint = false; 107 108 db_clear_single_step(); 109 db_clear_breakpoints(); 110 db_clear_watchpoints(); 111 112#ifdef FIXUP_PC_AFTER_BREAK 113 if (*is_breakpoint) { 114 /* 115 * Breakpoint trap. Fix up the PC if the 116 * machine requires it. 117 */ 118 FIXUP_PC_AFTER_BREAK 119 pc = PC_REGS(); 120 } 121#endif 122 123 /* 124 * Now check for a breakpoint at this address. 125 */ 126 bkpt = db_find_breakpoint_here(pc); 127 if (bkpt) { 128 if (--bkpt->count == 0) { 129 bkpt->count = bkpt->init_count; 130 *is_breakpoint = true; 131 return (true); /* stop here */ 132 } 133 return (false); /* continue the countdown */ 134 } else if (*is_breakpoint) { 135#ifdef BKPT_SKIP 136 BKPT_SKIP; 137#endif 138 } 139 140 *is_breakpoint = false; /* might be a breakpoint, but not ours */ 141 142 /* 143 * If not stepping, then silently ignore single-step traps 144 * (except for clearing the single-step-flag above). 145 * 146 * If stepping, then abort if the trap type is unexpected. 147 * Breakpoints owned by us are expected and were handled above. 148 * Single-steps are expected and are handled below. All others 149 * are unexpected. 150 * 151 * Only do either of these if the MD layer claims to classify 152 * single-step traps unambiguously (by defining IS_SSTEP_TRAP). 153 * Otherwise, fall through to the bad historical behaviour 154 * given by turning unexpected traps into expected traps: if not 155 * stepping, then expect only breakpoints and stop, and if 156 * stepping, then expect only single-steps and step. 157 */ 158#ifdef IS_SSTEP_TRAP 159 if (db_run_mode == STEP_CONTINUE && IS_SSTEP_TRAP(type, code)) 160 return (false); 161 if (db_run_mode != STEP_CONTINUE && !IS_SSTEP_TRAP(type, code)) { 162 printf("Stepping aborted\n"); 163 return (true); 164 } 165#endif 166 167 if (db_run_mode == STEP_INVISIBLE) { 168 db_run_mode = STEP_CONTINUE; 169 return (false); /* continue */ 170 } 171 if (db_run_mode == STEP_COUNT) { 172 return (false); /* continue */ 173 } 174 if (db_run_mode == STEP_ONCE) { 175 if (--db_loop_count > 0) { 176 if (db_sstep_print) { 177 db_printf("\t\t"); 178 db_print_loc_and_inst(pc); 179 } 180 return (false); /* continue */ 181 } 182 } 183 if (db_run_mode == STEP_RETURN) { 184 /* continue until matching return */ 185 db_expr_t ins; 186 187 ins = db_get_value(pc, sizeof(int), false); 188 if (!inst_trap_return(ins) && 189 (!inst_return(ins) || --db_call_depth != 0)) { 190 if (db_sstep_print) { 191 if (inst_call(ins) || inst_return(ins)) { 192 int i; 193 194 db_printf("[after %6d] ", db_inst_count); 195 for (i = db_call_depth; --i > 0; ) 196 db_printf(" "); 197 db_print_loc_and_inst(pc); 198 } 199 } 200 if (inst_call(ins)) 201 db_call_depth++; 202 return (false); /* continue */ 203 } 204 } 205 if (db_run_mode == STEP_CALLT) { 206 /* continue until call or return */ 207 db_expr_t ins; 208 209 ins = db_get_value(pc, sizeof(int), false); 210 if (!inst_call(ins) && 211 !inst_return(ins) && 212 !inst_trap_return(ins)) { 213 return (false); /* continue */ 214 } 215 } 216 return (true); 217} 218 219void 220db_restart_at_pc(bool watchpt) 221{ 222 db_addr_t pc = PC_REGS(); 223 224 if ((db_run_mode == STEP_COUNT) || 225 ((db_run_mode == STEP_ONCE) && db_sstep_multiple) || 226 (db_run_mode == STEP_RETURN) || 227 (db_run_mode == STEP_CALLT)) { 228 /* 229 * We are about to execute this instruction, 230 * so count it now. 231 */ 232#ifdef SOFTWARE_SSTEP 233 db_expr_t ins = 234#endif 235 db_get_value(pc, sizeof(int), false); 236 db_inst_count++; 237 db_load_count += inst_load(ins); 238 db_store_count += inst_store(ins); 239#ifdef SOFTWARE_SSTEP 240 /* XXX works on mips, but... */ 241 if (inst_branch(ins) || inst_call(ins)) { 242 ins = db_get_value(next_instr_address(pc,1), 243 sizeof(int), false); 244 db_inst_count++; 245 db_load_count += inst_load(ins); 246 db_store_count += inst_store(ins); 247 } 248#endif /* SOFTWARE_SSTEP */ 249 } 250 251 if (db_run_mode == STEP_CONTINUE) { 252 if (watchpt || db_find_breakpoint_here(pc)) { 253 /* 254 * Step over breakpoint/watchpoint. 255 */ 256 db_run_mode = STEP_INVISIBLE; 257 db_set_single_step(); 258 } else { 259 db_set_breakpoints(); 260 db_set_watchpoints(); 261 } 262 } else { 263 db_set_single_step(); 264 } 265} 266 267#ifdef SOFTWARE_SSTEP 268/* 269 * Software implementation of single-stepping. 270 * If your machine does not have a trace mode 271 * similar to the vax or sun ones you can use 272 * this implementation, done for the mips. 273 * Just define the above conditional and provide 274 * the functions/macros defined below. 275 * 276 * extern bool 277 * inst_branch(), returns true if the instruction might branch 278 * extern unsigned 279 * branch_taken(), return the address the instruction might 280 * branch to 281 * db_getreg_val(); return the value of a user register, 282 * as indicated in the hardware instruction 283 * encoding, e.g. 8 for r8 284 * 285 * next_instr_address(pc,bd) returns the address of the first 286 * instruction following the one at "pc", 287 * which is either in the taken path of 288 * the branch (bd==1) or not. This is 289 * for machines (mips) with branch delays. 290 * 291 * A single-step may involve at most 2 breakpoints - 292 * one for branch-not-taken and one for branch taken. 293 * If one of these addresses does not already have a breakpoint, 294 * we allocate a breakpoint and save it here. 295 * These breakpoints are deleted on return. 296 */ 297 298void 299db_set_single_step(void) 300{ 301 db_addr_t pc = PC_REGS(), brpc; 302 unsigned inst; 303 304 /* 305 * User was stopped at pc, e.g. the instruction 306 * at pc was not executed. 307 */ 308 inst = db_get_value(pc, sizeof(int), false); 309 if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) { 310 brpc = branch_taken(inst, pc); 311 if (brpc != pc) { /* self-branches are hopeless */ 312 db_taken_bkpt = db_set_temp_breakpoint(brpc); 313 } 314 pc = next_instr_address(pc, 1); 315 } 316 pc = next_instr_address(pc, 0); 317 db_not_taken_bkpt = db_set_temp_breakpoint(pc); 318} 319 320void 321db_clear_single_step(void) 322{ 323 324 if (db_not_taken_bkpt != 0) { 325 db_delete_temp_breakpoint(db_not_taken_bkpt); 326 db_not_taken_bkpt = 0; 327 } 328 if (db_taken_bkpt != 0) { 329 db_delete_temp_breakpoint(db_taken_bkpt); 330 db_taken_bkpt = 0; 331 } 332} 333 334#endif /* SOFTWARE_SSTEP */ 335 336/* single-step */ 337/*ARGSUSED*/ 338void 339db_single_step_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 340{ 341 bool print = false; 342 343 if (count == -1) 344 count = 1; 345 346 if (modif[0] == 'p') 347 print = true; 348 349 db_run_mode = STEP_ONCE; 350 db_loop_count = count; 351 db_sstep_multiple = (count != 1); 352 db_sstep_print = print; 353 db_inst_count = 0; 354 db_load_count = 0; 355 db_store_count = 0; 356 357 db_cmd_loop_done = 1; 358} 359 360/* trace and print until call/return */ 361/*ARGSUSED*/ 362void 363db_trace_until_call_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 364 char *modif) 365{ 366 bool print = false; 367 368 if (modif[0] == 'p') 369 print = true; 370 371 db_run_mode = STEP_CALLT; 372 db_sstep_print = print; 373 db_inst_count = 0; 374 db_load_count = 0; 375 db_store_count = 0; 376 377 db_cmd_loop_done = 1; 378} 379 380/*ARGSUSED*/ 381void 382db_trace_until_matching_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 383 char *modif) 384{ 385 bool print = false; 386 387 if (modif[0] == 'p') 388 print = true; 389 390 db_run_mode = STEP_RETURN; 391 db_call_depth = 1; 392 db_sstep_print = print; 393 db_inst_count = 0; 394 db_load_count = 0; 395 db_store_count = 0; 396 397 db_cmd_loop_done = 1; 398} 399 400/* continue */ 401/*ARGSUSED*/ 402void 403db_continue_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 404{ 405 if (modif[0] == 'c') 406 db_run_mode = STEP_COUNT; 407 else 408 db_run_mode = STEP_CONTINUE; 409 db_inst_count = 0; 410 db_load_count = 0; 411 db_store_count = 0; 412 413 db_cmd_loop_done = 1; 414} 415