mirror of https://github.com/mgba-emu/mgba.git
GDB Stub: allow to override GDB's handling logic of write watchpoint
Also allow to break on nonmodifying writes.
This commit is contained in:
parent
5159d389a3
commit
6f697744d1
|
@ -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*);
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue