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 GDB_ACK_OFF
}; };
enum GDBWatchpointsBehvaior {
GDB_WATCHPOINT_STANDARD_LOGIC = 0,
GDB_WATCHPOINT_OVERRIDE_LOGIC,
GDB_WATCHPOINT_OVERRIDE_LOGIC_ANY_WRITE,
};
struct GDBStub { struct GDBStub {
struct mDebugger d; struct mDebugger d;
@ -40,10 +46,12 @@ struct GDBStub {
bool supportsSwbreak; bool supportsSwbreak;
bool supportsHwbreak; bool supportsHwbreak;
enum GDBWatchpointsBehvaior watchpointsBehavior;
}; };
void GDBStubCreate(struct GDBStub*); 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 GDBStubHangup(struct GDBStub*);
void GDBStubShutdown(struct GDBStub*); void GDBStubShutdown(struct GDBStub*);

View File

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

View File

@ -79,15 +79,18 @@ static void _gdbStubEntered(struct mDebugger* debugger, enum mDebuggerEntryReaso
case DEBUGGER_ENTER_WATCHPOINT: case DEBUGGER_ENTER_WATCHPOINT:
if (info) { if (info) {
const char* type = 0; 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) { switch (info->type.wp.watchType) {
case WATCHPOINT_WRITE: 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: case WATCHPOINT_WRITE_CHANGE:
type = "watch"; type = "watch";
break; break;
@ -575,7 +578,7 @@ static void _setBreakpoint(struct GDBStub* stub, const char* message) {
stub->d.platform->setBreakpoint(stub->d.platform, &breakpoint); stub->d.platform->setBreakpoint(stub->d.platform, &breakpoint);
break; break;
case '2': 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); stub->d.platform->setWatchpoint(stub->d.platform, &watchpoint);
break; break;
case '3': case '3':
@ -763,7 +766,7 @@ void GDBStubCreate(struct GDBStub* stub) {
stub->shouldBlock = false; 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)) { if (!SOCKET_FAILED(stub->socket)) {
GDBStubShutdown(stub); GDBStubShutdown(stub);
} }
@ -780,6 +783,7 @@ bool GDBStubListen(struct GDBStub* stub, int port, const struct Address* bindAdd
goto cleanup; goto cleanup;
} }
stub->watchpointsBehavior = watchpointsBehavior;
memset(stub->memoryMapXml, 0, GDB_STUB_MAX_LINE); memset(stub->memoryMapXml, 0, GDB_STUB_MAX_LINE);
return true; return true;

View File

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

View File

@ -28,6 +28,7 @@ public:
public slots: public slots:
void setPort(ushort port); void setPort(ushort port);
void setBindAddress(const Address&); void setBindAddress(const Address&);
void setWatchpointsBehavior(int watchpointsBehaviorId);
void listen(); void listen();
signals: signals:
@ -41,6 +42,7 @@ private:
ushort m_port = 2345; ushort m_port = 2345;
Address m_bindAddress; 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GDBWindow.h" #include "GDBWindow.h"
#include <QButtonGroup>
#include <QGridLayout> #include <QGridLayout>
#include <QGroupBox> #include <QGroupBox>
#include <QLabel> #include <QLabel>
@ -12,6 +13,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton> #include <QPushButton>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QRadioButton>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "GDBController.h" #include "GDBController.h"
@ -47,6 +49,28 @@ GDBWindow::GDBWindow(GDBController* controller, QWidget* parent)
connect(m_bindAddressEdit, &QLineEdit::textChanged, this, &GDBWindow::bindAddressChanged); connect(m_bindAddressEdit, &QLineEdit::textChanged, this, &GDBWindow::bindAddressChanged);
settingsGrid->addWidget(m_bindAddressEdit, 1, 1, Qt::AlignLeft); 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; QHBoxLayout* buttons = new QHBoxLayout;
m_startStopButton = new QPushButton; m_startStopButton = new QPushButton;
@ -92,6 +116,9 @@ void GDBWindow::bindAddressChanged(const QString& bindAddressString) {
void GDBWindow::started() { void GDBWindow::started() {
m_portEdit->setEnabled(false); m_portEdit->setEnabled(false);
m_bindAddressEdit->setEnabled(false); m_bindAddressEdit->setEnabled(false);
m_watchpointsStandardRadio->setEnabled(false);
m_watchpointsOverrideLogicRadio->setEnabled(false);
m_watchpointsOverrideLogicAnyWriteRadio->setEnabled(false);
m_startStopButton->setText(tr("Stop")); m_startStopButton->setText(tr("Stop"));
m_breakButton->setEnabled(true); m_breakButton->setEnabled(true);
disconnect(m_startStopButton, &QAbstractButton::clicked, m_gdbController, &GDBController::listen); disconnect(m_startStopButton, &QAbstractButton::clicked, m_gdbController, &GDBController::listen);
@ -102,6 +129,9 @@ void GDBWindow::started() {
void GDBWindow::stopped() { void GDBWindow::stopped() {
m_portEdit->setEnabled(true); m_portEdit->setEnabled(true);
m_bindAddressEdit->setEnabled(true); m_bindAddressEdit->setEnabled(true);
m_watchpointsStandardRadio->setEnabled(true);
m_watchpointsOverrideLogicRadio->setEnabled(true);
m_watchpointsOverrideLogicAnyWriteRadio->setEnabled(true);
m_startStopButton->setText(tr("Start")); m_startStopButton->setText(tr("Start"));
m_breakButton->setEnabled(false); m_breakButton->setEnabled(false);
disconnect(m_startStopButton, &QAbstractButton::clicked, m_gdbController, &DebuggerController::detach); disconnect(m_startStopButton, &QAbstractButton::clicked, m_gdbController, &DebuggerController::detach);

View File

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