Files
freebsd/sys/ddb/db_run.c
T
Konstantin Belousov aaadc92d9c Merge bde improvements for ddb on x86, mostly for single-stepping and
vm86 mode.

MFC r304085 (by bde):
Fix the variables $esp, $ds, $es, $fs, $gs and $ss in vm86 mode.  Fix
PC_REGS() so that printing of instructions works in some useful cases.

MFC r304962 (by bde):
Expand error messages: print symbol names, parentheses and shift tokens,
and negative shift counts.  Fix error messages.

MFC r305612 (by bde):
Fix single-stepping of instructions emulated by vm86.

MFC r305661 (by bde):
Give the full syntax of the 'count' arg for all commmands that support
it. Give the full syntax of the 'addr' arg for these commands and some
others.  Rename it from 'address' for the generic command. Fix
description of how 'count' is supposed to work for the 'break'
command.

Don't (mis)describe the syntax of the comma for the 'step' command.

Expand the description for the generic command.

Give the full syntax for the 'examine' command.  It was also missing
the possible values for the modifier.

MFC r305663 (by bde):
Fix stopping when the specified breakpoint count is reached.

MFC r305665 (by bde):
Pass the trap type and code down from db_trap() to db_stop_at_pc() so
that the latter can easily determine what the trap type actually is
after callers are fixed to encode the type unambigously.

MFC r305807 (by bde):
Use the MI macro TRAPF_USERMODE() instead of open-coded checks for
SEL_UPL and sometimes PSL_VM.  Fix logic errors in treating vm86
bioscall mode as kernel mode.  The main place checked all the
necessary flags, but put the necessary parentheses for the PSL_VM and
PCB_VM86CALL checks in the wrong place.

MFC r305811 (by bz):
Try to fix LINT builds after r305807.

MFC r305840 (by bde):
Abort single stepping in ddb if the trap is not for single-stepping.

MFC r305862 (by bde):
Ifdef the new dr6 variable for KDB.

MFC r305864 (by bde):
Statically initialize the run mode to the one that will become current
on first entry. Don't reset to the run mode to STEP_NONE when
stopping, and remove STEP_NONE.

MFC r305865 (by bde):
Fix decoding of tf_rsp on amd64, and move TF_HAS_STACKREGS() to the
i386-only section, and fix a comment about the amd64 kernel trapframe
not having stackregs.

MFC r305897 (by bde):
Silently ignore unexpected single-step traps.

MFC r306311 (by bde):
Determine the operand/address size of %cs in a new function
db_segsize().  Use db_segsize() to set the default operand/address
size for disassembling.

Fix db_print_loc_and_inst() to ask for the normal format and not the
alternate in normal operation. Use db_segsize() to avoid trying to
print a garbage stack trace if %cs is 16 bits.
2016-11-07 12:10:17 +00:00

415 lines
9.7 KiB
C

/*-
* Mach Operating System
* Copyright (c) 1991,1990 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or [email protected]
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
/*
* Author: David B. Golub, Carnegie Mellon University
* Date: 7/90
*/
/*
* Commands to run process.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kdb.h>
#include <sys/proc.h>
#include <machine/kdb.h>
#include <machine/pcb.h>
#include <vm/vm.h>
#include <ddb/ddb.h>
#include <ddb/db_break.h>
#include <ddb/db_access.h>
#define STEP_ONCE 1
#define STEP_RETURN 2
#define STEP_CALLT 3
#define STEP_CONTINUE 4
#define STEP_INVISIBLE 5
#define STEP_COUNT 6
static int db_run_mode = STEP_CONTINUE;
static bool db_sstep_multiple;
static bool db_sstep_print;
static int db_loop_count;
static int db_call_depth;
int db_inst_count;
int db_load_count;
int db_store_count;
#ifdef SOFTWARE_SSTEP
db_breakpoint_t db_not_taken_bkpt = 0;
db_breakpoint_t db_taken_bkpt = 0;
#endif
#ifndef db_set_single_step
void db_set_single_step(void);
#endif
#ifndef db_clear_single_step
void db_clear_single_step(void);
#endif
#ifndef db_pc_is_singlestep
static bool
db_pc_is_singlestep(db_addr_t pc)
{
#ifdef SOFTWARE_SSTEP
if ((db_not_taken_bkpt != 0 && pc == db_not_taken_bkpt->address)
|| (db_taken_bkpt != 0 && pc == db_taken_bkpt->address))
return (true);
#endif
return (false);
}
#endif
bool
db_stop_at_pc(int type, int code, bool *is_breakpoint, bool *is_watchpoint)
{
db_addr_t pc;
db_breakpoint_t bkpt;
*is_breakpoint = IS_BREAKPOINT_TRAP(type, code);
*is_watchpoint = IS_WATCHPOINT_TRAP(type, code);
pc = PC_REGS();
if (db_pc_is_singlestep(pc))
*is_breakpoint = false;
db_clear_single_step();
db_clear_breakpoints();
db_clear_watchpoints();
#ifdef FIXUP_PC_AFTER_BREAK
if (*is_breakpoint) {
/*
* Breakpoint trap. Fix up the PC if the
* machine requires it.
*/
FIXUP_PC_AFTER_BREAK
pc = PC_REGS();
}
#endif
/*
* Now check for a breakpoint at this address.
*/
bkpt = db_find_breakpoint_here(pc);
if (bkpt) {
if (--bkpt->count == 0) {
bkpt->count = bkpt->init_count;
*is_breakpoint = true;
return (true); /* stop here */
}
return (false); /* continue the countdown */
} else if (*is_breakpoint) {
#ifdef BKPT_SKIP
BKPT_SKIP;
#endif
}
*is_breakpoint = false; /* might be a breakpoint, but not ours */
/*
* If not stepping, then silently ignore single-step traps
* (except for clearing the single-step-flag above).
*
* If stepping, then abort if the trap type is unexpected.
* Breakpoints owned by us are expected and were handled above.
* Single-steps are expected and are handled below. All others
* are unexpected.
*
* Only do either of these if the MD layer claims to classify
* single-step traps unambiguously (by defining IS_SSTEP_TRAP).
* Otherwise, fall through to the bad historical behaviour
* given by turning unexpected traps into expected traps: if not
* stepping, then expect only breakpoints and stop, and if
* stepping, then expect only single-steps and step.
*/
#ifdef IS_SSTEP_TRAP
if (db_run_mode == STEP_CONTINUE && IS_SSTEP_TRAP(type, code))
return (false);
if (db_run_mode != STEP_CONTINUE && !IS_SSTEP_TRAP(type, code)) {
printf("Stepping aborted\n");
return (true);
}
#endif
if (db_run_mode == STEP_INVISIBLE) {
db_run_mode = STEP_CONTINUE;
return (false); /* continue */
}
if (db_run_mode == STEP_COUNT) {
return (false); /* continue */
}
if (db_run_mode == STEP_ONCE) {
if (--db_loop_count > 0) {
if (db_sstep_print) {
db_printf("\t\t");
db_print_loc_and_inst(pc);
db_printf("\n");
}
return (false); /* continue */
}
}
if (db_run_mode == STEP_RETURN) {
/* continue until matching return */
db_expr_t ins;
ins = db_get_value(pc, sizeof(int), false);
if (!inst_trap_return(ins) &&
(!inst_return(ins) || --db_call_depth != 0)) {
if (db_sstep_print) {
if (inst_call(ins) || inst_return(ins)) {
int i;
db_printf("[after %6d] ", db_inst_count);
for (i = db_call_depth; --i > 0; )
db_printf(" ");
db_print_loc_and_inst(pc);
db_printf("\n");
}
}
if (inst_call(ins))
db_call_depth++;
return (false); /* continue */
}
}
if (db_run_mode == STEP_CALLT) {
/* continue until call or return */
db_expr_t ins;
ins = db_get_value(pc, sizeof(int), false);
if (!inst_call(ins) &&
!inst_return(ins) &&
!inst_trap_return(ins)) {
return (false); /* continue */
}
}
return (true);
}
void
db_restart_at_pc(bool watchpt)
{
db_addr_t pc = PC_REGS();
if ((db_run_mode == STEP_COUNT) ||
((db_run_mode == STEP_ONCE) && db_sstep_multiple) ||
(db_run_mode == STEP_RETURN) ||
(db_run_mode == STEP_CALLT)) {
/*
* We are about to execute this instruction,
* so count it now.
*/
#ifdef SOFTWARE_SSTEP
db_expr_t ins =
#endif
db_get_value(pc, sizeof(int), false);
db_inst_count++;
db_load_count += inst_load(ins);
db_store_count += inst_store(ins);
#ifdef SOFTWARE_SSTEP
/* XXX works on mips, but... */
if (inst_branch(ins) || inst_call(ins)) {
ins = db_get_value(next_instr_address(pc,1),
sizeof(int), false);
db_inst_count++;
db_load_count += inst_load(ins);
db_store_count += inst_store(ins);
}
#endif /* SOFTWARE_SSTEP */
}
if (db_run_mode == STEP_CONTINUE) {
if (watchpt || db_find_breakpoint_here(pc)) {
/*
* Step over breakpoint/watchpoint.
*/
db_run_mode = STEP_INVISIBLE;
db_set_single_step();
} else {
db_set_breakpoints();
db_set_watchpoints();
}
} else {
db_set_single_step();
}
}
#ifdef SOFTWARE_SSTEP
/*
* Software implementation of single-stepping.
* If your machine does not have a trace mode
* similar to the vax or sun ones you can use
* this implementation, done for the mips.
* Just define the above conditional and provide
* the functions/macros defined below.
*
* extern bool
* inst_branch(), returns true if the instruction might branch
* extern unsigned
* branch_taken(), return the address the instruction might
* branch to
* db_getreg_val(); return the value of a user register,
* as indicated in the hardware instruction
* encoding, e.g. 8 for r8
*
* next_instr_address(pc,bd) returns the address of the first
* instruction following the one at "pc",
* which is either in the taken path of
* the branch (bd==1) or not. This is
* for machines (mips) with branch delays.
*
* A single-step may involve at most 2 breakpoints -
* one for branch-not-taken and one for branch taken.
* If one of these addresses does not already have a breakpoint,
* we allocate a breakpoint and save it here.
* These breakpoints are deleted on return.
*/
void
db_set_single_step(void)
{
db_addr_t pc = PC_REGS(), brpc;
unsigned inst;
/*
* User was stopped at pc, e.g. the instruction
* at pc was not executed.
*/
inst = db_get_value(pc, sizeof(int), false);
if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) {
brpc = branch_taken(inst, pc);
if (brpc != pc) { /* self-branches are hopeless */
db_taken_bkpt = db_set_temp_breakpoint(brpc);
}
pc = next_instr_address(pc, 1);
}
pc = next_instr_address(pc, 0);
db_not_taken_bkpt = db_set_temp_breakpoint(pc);
}
void
db_clear_single_step(void)
{
if (db_not_taken_bkpt != 0) {
db_delete_temp_breakpoint(db_not_taken_bkpt);
db_not_taken_bkpt = 0;
}
if (db_taken_bkpt != 0) {
db_delete_temp_breakpoint(db_taken_bkpt);
db_taken_bkpt = 0;
}
}
#endif /* SOFTWARE_SSTEP */
extern int db_cmd_loop_done;
/* single-step */
/*ARGSUSED*/
void
db_single_step_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
{
bool print = false;
if (count == -1)
count = 1;
if (modif[0] == 'p')
print = true;
db_run_mode = STEP_ONCE;
db_loop_count = count;
db_sstep_multiple = (count != 1);
db_sstep_print = print;
db_inst_count = 0;
db_load_count = 0;
db_store_count = 0;
db_cmd_loop_done = 1;
}
/* trace and print until call/return */
/*ARGSUSED*/
void
db_trace_until_call_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
char *modif)
{
bool print = false;
if (modif[0] == 'p')
print = true;
db_run_mode = STEP_CALLT;
db_sstep_print = print;
db_inst_count = 0;
db_load_count = 0;
db_store_count = 0;
db_cmd_loop_done = 1;
}
/*ARGSUSED*/
void
db_trace_until_matching_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
char *modif)
{
bool print = false;
if (modif[0] == 'p')
print = true;
db_run_mode = STEP_RETURN;
db_call_depth = 1;
db_sstep_print = print;
db_inst_count = 0;
db_load_count = 0;
db_store_count = 0;
db_cmd_loop_done = 1;
}
/* continue */
/*ARGSUSED*/
void
db_continue_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif)
{
if (modif[0] == 'c')
db_run_mode = STEP_COUNT;
else
db_run_mode = STEP_CONTINUE;
db_inst_count = 0;
db_load_count = 0;
db_store_count = 0;
db_cmd_loop_done = 1;
}