GDB Stub: allow to override GDB's handling logic of write watchpoint

Also allow to break on nonmodifying writes.
This commit is contained in:
aldelaro5 2021-12-30 16:54:24 -05:00 committed by Vicki Pfau
parent 5159d389a3
commit 6f697744d1
7 changed files with 64 additions and 12 deletions

View File

@ -24,6 +24,12 @@ enum GDBStubAckState {
GDB_ACK_OFF
};
enum GDBWatchpointsBehvaior {
GDB_WATCHPOINT_STANDARD_LOGIC = 0,
GDB_WATCHPOINT_OVERRIDE_LOGIC,
GDB_WATCHPOINT_OVERRIDE_LOGIC_ANY_WRITE,
};
struct GDBStub {
struct mDebugger d;
@ -40,10 +46,12 @@ struct GDBStub {
bool supportsSwbreak;
bool supportsHwbreak;
enum GDBWatchpointsBehvaior watchpointsBehavior;
};
void GDBStubCreate(struct GDBStub*);
bool GDBStubListen(struct GDBStub*, int port, const struct Address* bindAddress);
bool GDBStubListen(struct GDBStub*, int port, const struct Address* bindAddress, enum GDBWatchpointsBehvaior watchpointsBehavior);
void GDBStubHangup(struct GDBStub*);
void GDBStubShutdown(struct GDBStub*);

View File

@ -53,7 +53,7 @@ struct mDebugger* mDebuggerCreate(enum mDebuggerType type, struct mCore* core) {
case DEBUGGER_GDB:
#ifdef USE_GDB_STUB
GDBStubCreate(&debugger->gdb);
GDBStubListen(&debugger->gdb, 2345, 0);
GDBStubListen(&debugger->gdb, 2345, 0, GDB_WATCHPOINT_STANDARD_LOGIC);
break;
#endif
case DEBUGGER_NONE:

View File

@ -79,15 +79,18 @@ static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReaso
case DEBUGGER_ENTER_WATCHPOINT:
if (info) {
const char* type = 0;
if (stub->watchpointsBehavior != GDB_WATCHPOINT_STANDARD_LOGIC && info->type.wp.watchType & WATCHPOINT_WRITE) {
// We send S05 instead of T05watch because it bypasses GDB's internal logic to check if
// the value changed and to bypass a step into by GDB. This allows to control the change
// logic even when using savestates which we already handle in the core debugger logic
snprintf(stub->outgoing, GDB_STUB_MAX_LINE - 4, "S%02x", SIGTRAP);
_sendMessage(stub);
return;
}
switch (info->type.wp.watchType) {
case WATCHPOINT_WRITE:
if (info->type.wp.newValue == info->type.wp.oldValue) {
if (stub->d.state == DEBUGGER_PAUSED) {
stub->d.state = DEBUGGER_RUNNING;
}
return;
}
// Fall through
case WATCHPOINT_WRITE_CHANGE:
type = "watch";
break;
@ -575,7 +578,7 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
stub->d.platform->setBreakpoint(stub->d.platform, &breakpoint);
break;
case '2':
watchpoint.type = WATCHPOINT_WRITE_CHANGE;
watchpoint.type = stub->watchpointsBehavior == GDB_WATCHPOINT_OVERRIDE_LOGIC_ANY_WRITE ? WATCHPOINT_WRITE : WATCHPOINT_WRITE_CHANGE;
stub->d.platform->setWatchpoint(stub->d.platform, &watchpoint);
break;
case '3':
@ -763,7 +766,7 @@ void GDBStubCreate(struct GDBStub* stub) {
stub->shouldBlock = false;
}
bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress) {
bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAddress, enum GDBWatchpointsBehvaior watchpointsBehavior) {
if (!SOCKET_FAILED(stub->socket)) {
GDBStubShutdown(stub);
}
@ -780,6 +783,7 @@ bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAdd
goto cleanup;
}
stub->watchpointsBehavior = watchpointsBehavior;
memset(stub->memoryMapXml, 0, GDB_STUB_MAX_LINE);
return true;

View File

@ -32,8 +32,12 @@ void GDBController::setBindAddress(const Address& address) {
m_bindAddress = address;
}
void GDBController::setWatchpointsBehavior(int watchpointsBehaviorId) {
m_watchpointsBehavior = static_cast<GDBWatchpointsBehvaior>(watchpointsBehaviorId);
}
void GDBController::listen() {
if (GDBStubListen(&m_gdbStub, m_port, &m_bindAddress)) {
if (GDBStubListen(&m_gdbStub, m_port, &m_bindAddress, m_watchpointsBehavior)) {
if (!isAttached()) {
attach();
}

View File

@ -28,6 +28,7 @@ public:
public slots:
void setPort(ushort port);
void setBindAddress(const Address&);
void setWatchpointsBehavior(int watchpointsBehaviorId);
void listen();
signals:
@ -41,6 +42,7 @@ private:
ushort m_port = 2345;
Address m_bindAddress;
enum GDBWatchpointsBehvaior m_watchpointsBehavior = GDB_WATCHPOINT_STANDARD_LOGIC;
};
}

View File

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GDBWindow.h"
#include <QButtonGroup>
#include <QGridLayout>
#include <QGroupBox>
#include <QLabel>
@ -12,6 +13,7 @@
#include <QMessageBox>
#include <QPushButton>
#include <QHBoxLayout>
#include <QRadioButton>
#include <QVBoxLayout>
#include "GDBController.h"
@ -47,6 +49,28 @@ GDBWindow::GDBWindow(GDBController* controller, QWidget* parent)
connect(m_bindAddressEdit, &QLineEdit::textChanged, this, &GDBWindow::bindAddressChanged);
settingsGrid->addWidget(m_bindAddressEdit, 1, 1, Qt::AlignLeft);
QGroupBox* watchpointsSettings = new QGroupBox(tr("Write watchpoints behavior"));
mainSegment->addWidget(watchpointsSettings);
QVBoxLayout* watchpointsSettingsLayout = new QVBoxLayout;
watchpointsSettings->setLayout(watchpointsSettingsLayout);
QButtonGroup* watchpointsButtonGroup = new QButtonGroup(watchpointsSettings);
connect(watchpointsButtonGroup, &QButtonGroup::idClicked, controller, &GDBController::setWatchpointsBehavior);
m_watchpointsStandardRadio = new QRadioButton(tr("Standard GDB"), watchpointsSettings);
m_watchpointsStandardRadio->setChecked(true);
watchpointsButtonGroup->addButton(m_watchpointsStandardRadio, GDB_WATCHPOINT_STANDARD_LOGIC);
watchpointsSettingsLayout->addWidget(m_watchpointsStandardRadio);
m_watchpointsOverrideLogicRadio = new QRadioButton(tr("Internal change detection"), watchpointsSettings);
watchpointsButtonGroup->addButton(m_watchpointsOverrideLogicRadio, GDB_WATCHPOINT_OVERRIDE_LOGIC);
watchpointsSettingsLayout->addWidget(m_watchpointsOverrideLogicRadio);
m_watchpointsOverrideLogicAnyWriteRadio = new QRadioButton(tr("Break on all writes"), watchpointsSettings);
watchpointsButtonGroup->addButton(m_watchpointsOverrideLogicAnyWriteRadio, GDB_WATCHPOINT_OVERRIDE_LOGIC_ANY_WRITE);
watchpointsSettingsLayout->addWidget(m_watchpointsOverrideLogicAnyWriteRadio);
QHBoxLayout* buttons = new QHBoxLayout;
m_startStopButton = new QPushButton;
@ -92,6 +116,9 @@ void GDBWindow::bindAddressChanged(const QString& bindAddressString) {
void GDBWindow::started() {
m_portEdit->setEnabled(false);
m_bindAddressEdit->setEnabled(false);
m_watchpointsStandardRadio->setEnabled(false);
m_watchpointsOverrideLogicRadio->setEnabled(false);
m_watchpointsOverrideLogicAnyWriteRadio->setEnabled(false);
m_startStopButton->setText(tr("Stop"));
m_breakButton->setEnabled(true);
disconnect(m_startStopButton, &QAbstractButton::clicked, m_gdbController, &GDBController::listen);
@ -102,6 +129,9 @@ void GDBWindow::started() {
void GDBWindow::stopped() {
m_portEdit->setEnabled(true);
m_bindAddressEdit->setEnabled(true);
m_watchpointsStandardRadio->setEnabled(true);
m_watchpointsOverrideLogicRadio->setEnabled(true);
m_watchpointsOverrideLogicAnyWriteRadio->setEnabled(true);
m_startStopButton->setText(tr("Start"));
m_breakButton->setEnabled(false);
disconnect(m_startStopButton, &QAbstractButton::clicked, m_gdbController, &DebuggerController::detach);

View File

@ -9,6 +9,7 @@
class QLineEdit;
class QPushButton;
class QRadioButton;
namespace QGBA {
@ -34,6 +35,9 @@ private:
QLineEdit* m_portEdit;
QLineEdit* m_bindAddressEdit;
QRadioButton* m_watchpointsStandardRadio;
QRadioButton* m_watchpointsOverrideLogicRadio;
QRadioButton* m_watchpointsOverrideLogicAnyWriteRadio;
QPushButton* m_startStopButton;
QPushButton* m_breakButton;
};