misc: Generalize GDB single stepping.

The new single stepping implementation for x86 doesn't rely on any ISA
specific properties or functionality. This change pulls out the per ISA
implementation of those functions and promotes the X86 implementation to the
base class.

One drawback of that implementation is that the CPU might stop on an
instruction twice if it's affected by both breakpoints and single stepping.
While that might be a little surprising, it's harmless and would only happen
under somewhat unlikely circumstances.
This commit is contained in:
Gabe Black 2014-12-05 22:37:03 -08:00
parent fb07d43b1a
commit 4a8a0a0798
13 changed files with 52 additions and 234 deletions

View file

@ -255,46 +255,6 @@ RemoteGDB::setregs()
context->pcState(gdbregs.regs64[KGDB_REG_PC]);
}
void
RemoteGDB::clearSingleStep()
{
DPRINTF(GDBMisc, "clearSingleStep bt_addr=%#x nt_addr=%#x\n",
takenBkpt, notTakenBkpt);
if (takenBkpt != 0)
clearTempBreakpoint(takenBkpt);
if (notTakenBkpt != 0)
clearTempBreakpoint(notTakenBkpt);
}
void
RemoteGDB::setSingleStep()
{
PCState pc = context->pcState();
PCState bpc;
bool set_bt = false;
// User was stopped at pc, e.g. the instruction at pc was not
// executed.
MachInst inst = read<MachInst>(pc.pc());
StaticInstPtr si = context->getDecoderPtr()->decode(inst, pc.pc());
if (si->hasBranchTarget(pc, context, bpc)) {
// Don't bother setting a breakpoint on the taken branch if it
// is the same as the next pc
if (bpc.pc() != pc.npc())
set_bt = true;
}
DPRINTF(GDBMisc, "setSingleStep bt_addr=%#x nt_addr=%#x\n",
takenBkpt, notTakenBkpt);
setTempBreakpoint(notTakenBkpt = pc.npc());
if (set_bt)
setTempBreakpoint(takenBkpt = bpc.pc());
}
// Write bytes to kernel address space for debugger.
bool
RemoteGDB::write(Addr vaddr, size_t size, const char *data)

View file

@ -47,22 +47,15 @@ namespace AlphaISA {
class RemoteGDB : public BaseRemoteGDB
{
protected:
Addr notTakenBkpt;
Addr takenBkpt;
protected:
void getregs();
void setregs();
void clearSingleStep();
void setSingleStep();
// Machine memory
bool acc(Addr addr, size_t len);
bool write(Addr addr, size_t size, const char *data);
virtual bool insertHardBreak(Addr addr, size_t len);
bool insertHardBreak(Addr addr, size_t len);
public:
RemoteGDB(System *system, ThreadContext *context);

View file

@ -161,8 +161,7 @@ using namespace std;
using namespace ArmISA;
RemoteGDB::RemoteGDB(System *_system, ThreadContext *tc)
: BaseRemoteGDB(_system, tc, GDB_REG_BYTES),
notTakenBkpt(0), takenBkpt(0)
: BaseRemoteGDB(_system, tc, GDB_REG_BYTES)
{
}
@ -314,46 +313,6 @@ RemoteGDB::setregs()
}
}
void
RemoteGDB::clearSingleStep()
{
DPRINTF(GDBMisc, "clearSingleStep bt_addr=%#x nt_addr=%#x\n",
takenBkpt, notTakenBkpt);
if (takenBkpt != 0)
clearTempBreakpoint(takenBkpt);
if (notTakenBkpt != 0)
clearTempBreakpoint(notTakenBkpt);
}
void
RemoteGDB::setSingleStep()
{
PCState pc = context->pcState();
PCState bpc;
bool set_bt = false;
// User was stopped at pc, e.g. the instruction at pc was not
// executed.
MachInst inst = read<MachInst>(pc.pc());
StaticInstPtr si = context->getDecoderPtr()->decode(inst, pc.pc());
if (si->hasBranchTarget(pc, context, bpc)) {
// Don't bother setting a breakpoint on the taken branch if it
// is the same as the next pc
if (bpc.pc() != pc.npc())
set_bt = true;
}
DPRINTF(GDBMisc, "setSingleStep bt_addr=%#x nt_addr=%#x\n",
takenBkpt, notTakenBkpt);
setTempBreakpoint(notTakenBkpt = pc.npc());
if (set_bt)
setTempBreakpoint(takenBkpt = bpc.pc());
}
// Write bytes to kernel address space for debugger.
bool
RemoteGDB::write(Addr vaddr, size_t size, const char *data)

View file

@ -80,23 +80,15 @@ const int GDB_REG_BYTES = std::max(GDB64_NUMREGS * sizeof(uint64_t),
class RemoteGDB : public BaseRemoteGDB
{
protected:
bool acc(Addr addr, size_t len);
bool write(Addr addr, size_t size, const char *data);
protected:
Addr notTakenBkpt;
Addr takenBkpt;
void getregs();
void setregs();
protected:
bool acc(Addr addr, size_t len);
bool write(Addr addr, size_t size, const char *data);
void getregs();
void setregs();
void clearSingleStep();
void setSingleStep();
public:
RemoteGDB(System *_system, ThreadContext *tc);
public:
RemoteGDB(System *_system, ThreadContext *tc);
};
} // namespace ArmISA

View file

@ -235,47 +235,3 @@ RemoteGDB::setregs()
context->setFloatRegBits(FLOATREG_FIR,
gdbregs.regs32[GdbIntRegs + GdbFloatArchRegs + 1]);
}
void
RemoteGDB::clearSingleStep()
{
DPRINTF(GDBMisc, "clearSingleStep bt_addr=%#x nt_addr=%#x\n",
takenBkpt, notTakenBkpt);
if (takenBkpt != 0)
clearTempBreakpoint(takenBkpt);
if (notTakenBkpt != 0)
clearTempBreakpoint(notTakenBkpt);
}
void
RemoteGDB::setSingleStep()
{
PCState pc = context->pcState();
PCState bpc;
bool set_bt = false;
// User was stopped at pc, e.g. the instruction at pc was not
// executed.
MachInst inst = read<MachInst>(pc.pc());
StaticInstPtr si = context->getDecoderPtr()->decode(inst, pc.pc());
if (si->hasBranchTarget(pc, context, bpc)) {
// Don't bother setting a breakpoint on the taken branch if it
// is the same as the next npc
if (bpc.npc() != pc.nnpc())
set_bt = true;
}
DPRINTF(GDBMisc, "setSingleStep bt_addr=%#x nt_addr=%#x\n",
takenBkpt, notTakenBkpt);
notTakenBkpt = pc.nnpc();
setTempBreakpoint(notTakenBkpt);
if (set_bt) {
takenBkpt = bpc.npc();
setTempBreakpoint(takenBkpt);
}
}

View file

@ -54,10 +54,6 @@ namespace MipsISA
class RemoteGDB : public BaseRemoteGDB
{
protected:
Addr notTakenBkpt;
Addr takenBkpt;
public:
RemoteGDB(System *_system, ThreadContext *tc);
@ -66,9 +62,6 @@ namespace MipsISA
void getregs();
void setregs();
void clearSingleStep();
void setSingleStep();
};
}

View file

@ -65,18 +65,6 @@ class RemoteGDB : public BaseRemoteGDB
{
panic("setregs not implemented for POWER!");
}
void
clearSingleStep()
{
panic("clearSingleStep not implemented for POWER!");
}
void
setSingleStep()
{
panic("setSingleStep not implemented for POWER!");
}
};
} // namespace PowerISA

View file

@ -144,7 +144,7 @@ using namespace std;
using namespace SparcISA;
RemoteGDB::RemoteGDB(System *_system, ThreadContext *c)
: BaseRemoteGDB(_system, c, NumGDBRegs * sizeof(uint64_t)), nextBkpt(0)
: BaseRemoteGDB(_system, c, NumGDBRegs * sizeof(uint64_t))
{}
///////////////////////////////////////////////////////////
@ -241,17 +241,3 @@ RemoteGDB::setregs()
context->setIntReg(x - RegG0, gdbregs.regs64[x]);
// Only the integer registers, pc and npc are set in netbsd
}
void
RemoteGDB::clearSingleStep()
{
if (nextBkpt)
clearTempBreakpoint(nextBkpt);
}
void
RemoteGDB::setSingleStep()
{
nextBkpt = context->pcState().npc();
setTempBreakpoint(nextBkpt);
}

View file

@ -66,11 +66,6 @@ class RemoteGDB : public BaseRemoteGDB
protected:
void getregs();
void setregs();
void clearSingleStep();
void setSingleStep();
Addr nextBkpt;
};
}

View file

@ -61,7 +61,7 @@ using namespace std;
using namespace X86ISA;
RemoteGDB::RemoteGDB(System *_system, ThreadContext *c) :
BaseRemoteGDB(_system, c, GDB_REG_BYTES), singleStepEvent(this)
BaseRemoteGDB(_system, c, GDB_REG_BYTES)
{}
bool
@ -88,14 +88,6 @@ RemoteGDB::acc(Addr va, size_t len)
}
}
void
RemoteGDB::SingleStepEvent::process()
{
if (!gdb->singleStepEvent.scheduled())
gdb->scheduleInstCommitEvent(&gdb->singleStepEvent, 1);
gdb->trap(SIGTRAP);
}
void
RemoteGDB::getregs()
{
@ -231,16 +223,3 @@ RemoteGDB::setregs()
}
}
}
void
RemoteGDB::clearSingleStep()
{
descheduleInstCommitEvent(&singleStepEvent);
}
void
RemoteGDB::setSingleStep()
{
if (!singleStepEvent.scheduled())
scheduleInstCommitEvent(&singleStepEvent, 1);
}

View file

@ -116,26 +116,9 @@ class RemoteGDB : public BaseRemoteGDB
bool acc(Addr addr, size_t len);
protected:
class SingleStepEvent : public Event
{
protected:
RemoteGDB *gdb;
public:
SingleStepEvent(RemoteGDB *g) : gdb(g)
{}
void process();
};
SingleStepEvent singleStepEvent;
void getregs();
void setregs();
void clearSingleStep();
void setSingleStep();
bool checkBpLen(size_t len) { return len == 1; }
};

View file

@ -262,11 +262,18 @@ BaseRemoteGDB::TrapEvent::process()
gdb->trap(_type);
}
BaseRemoteGDB::BaseRemoteGDB(System *_system, ThreadContext *c, size_t cacheSize)
: inputEvent(NULL), trapEvent(this), listener(NULL), number(-1), fd(-1),
active(false), attached(false),
system(_system), context(c),
gdbregs(cacheSize)
void
BaseRemoteGDB::SingleStepEvent::process()
{
if (!gdb->singleStepEvent.scheduled())
gdb->scheduleInstCommitEvent(&gdb->singleStepEvent, 1);
gdb->trap(SIGTRAP);
}
BaseRemoteGDB::BaseRemoteGDB(System *_system, ThreadContext *c,
size_t cacheSize) : inputEvent(NULL), trapEvent(this), listener(NULL),
number(-1), fd(-1), active(false), attached(false), system(_system),
context(c), gdbregs(cacheSize), singleStepEvent(this)
{
memset(gdbregs.regs, 0, gdbregs.bytes());
}
@ -525,6 +532,19 @@ BaseRemoteGDB::write(Addr vaddr, size_t size, const char *data)
return true;
}
void
BaseRemoteGDB::clearSingleStep()
{
descheduleInstCommitEvent(&singleStepEvent);
}
void
BaseRemoteGDB::setSingleStep()
{
if (!singleStepEvent.scheduled())
scheduleInstCommitEvent(&singleStepEvent, 1);
}
PCEventQueue *BaseRemoteGDB::getPcEventQueue()
{
return &system->pcEventQueue;

View file

@ -209,11 +209,25 @@ class BaseRemoteGDB
}
protected:
class SingleStepEvent : public Event
{
protected:
BaseRemoteGDB *gdb;
public:
SingleStepEvent(BaseRemoteGDB *g) : gdb(g)
{}
void process();
};
SingleStepEvent singleStepEvent;
virtual void getregs() = 0;
virtual void setregs() = 0;
virtual void clearSingleStep() = 0;
virtual void setSingleStep() = 0;
void clearSingleStep();
void setSingleStep();
PCEventQueue *getPcEventQueue();
EventQueue *getComInstEventQueue();