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