mirror of https://github.com/PCSX2/pcsx2.git
Debugger: Copy as CSV. Breakpoint import from CSV
This commit is contained in:
parent
ade2b4baea
commit
4f825641ce
|
@ -282,6 +282,18 @@ void CpuWidget::onBPListContextMenu(QPoint pos)
|
|||
contextMenu->addAction(deleteAction);
|
||||
}
|
||||
|
||||
contextMenu->addSeparator();
|
||||
QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.breakpointList);
|
||||
connect(actionExport, &QAction::triggered, [this]() {
|
||||
// It's important to use the User Role here to allow pasting to be translation agnostic
|
||||
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.breakpointList->model(), Qt::UserRole));
|
||||
});
|
||||
contextMenu->addAction(actionExport);
|
||||
|
||||
QAction* actionImport = new QAction(tr("Paste from CSV"), m_ui.breakpointList);
|
||||
connect(actionImport, &QAction::triggered, this, &CpuWidget::contextBPListPasteCSV);
|
||||
contextMenu->addAction(actionImport);
|
||||
|
||||
contextMenu->popup(m_ui.breakpointList->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
|
@ -335,6 +347,109 @@ void CpuWidget::contextBPListEdit()
|
|||
bpDialog->show();
|
||||
}
|
||||
|
||||
void CpuWidget::contextBPListPasteCSV()
|
||||
{
|
||||
QString csv = QGuiApplication::clipboard()->text();
|
||||
// Skip header
|
||||
csv = csv.mid(csv.indexOf('\n') + 1);
|
||||
|
||||
for (const QString& line : csv.split('\n'))
|
||||
{
|
||||
const QStringList fields = line.split(',');
|
||||
if (fields.size() != BreakpointModel::BreakpointColumns::COLUMN_COUNT)
|
||||
{
|
||||
Console.WriteLn("Debugger CSV Import: Invalid number of columns, skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
int type = fields[0].toUInt(&ok);
|
||||
if (!ok)
|
||||
{
|
||||
Console.WriteLn("Debugger CSV Import: Failed to parse type '%s', skipping", fields[0].toUtf8().constData());
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is how we differentiate between breakpoints and memchecks
|
||||
if (type == MEMCHECK_INVALID)
|
||||
{
|
||||
BreakPoint bp;
|
||||
|
||||
// Address
|
||||
bp.addr = fields[1].toUInt(&ok, 16);
|
||||
if (!ok)
|
||||
{
|
||||
Console.WriteLn("Debugger CSV Import: Failed to parse address '%s', skipping", fields[1].toUtf8().constData());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Condition
|
||||
if (fields[4] != "No Condition")
|
||||
{
|
||||
PostfixExpression expr;
|
||||
bp.hasCond = true;
|
||||
bp.cond.debug = &m_cpu;
|
||||
|
||||
if (!m_cpu.initExpression(fields[4].toUtf8().constData(), expr))
|
||||
{
|
||||
Console.WriteLn("Debugger CSV Import: Failed to parse cond '%s', skipping", fields[4].toUtf8().constData());
|
||||
continue;
|
||||
}
|
||||
bp.cond.expression = expr;
|
||||
strncpy(&bp.cond.expressionString[0], fields[4].toUtf8().constData(), sizeof(bp.cond.expressionString));
|
||||
}
|
||||
|
||||
// Enabled
|
||||
bp.enabled = fields[6].toUInt(&ok);
|
||||
if (!ok)
|
||||
{
|
||||
Console.WriteLn("Debugger CSV Import: Failed to parse enable flag '%s', skipping", fields[1].toUtf8().constData());
|
||||
continue;
|
||||
}
|
||||
|
||||
m_bpModel.insertBreakpointRows(0, 1, {bp});
|
||||
}
|
||||
else
|
||||
{
|
||||
MemCheck mc;
|
||||
// Mode
|
||||
if (type >= MEMCHECK_INVALID)
|
||||
{
|
||||
Console.WriteLn("Debugger CSV Import: Failed to parse cond type '%s', skipping", fields[0].toUtf8().constData());
|
||||
continue;
|
||||
}
|
||||
mc.cond = static_cast<MemCheckCondition>(type);
|
||||
|
||||
// Address
|
||||
mc.start = fields[1].toUInt(&ok, 16);
|
||||
if (!ok)
|
||||
{
|
||||
Console.WriteLn("Debugger CSV Import: Failed to parse address '%s', skipping", fields[1].toUtf8().constData());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Size
|
||||
mc.end = fields[2].toUInt(&ok, 16) + mc.start;
|
||||
if (!ok)
|
||||
{
|
||||
Console.WriteLn("Debugger CSV Import: Failed to parse length '%s', skipping", fields[1].toUtf8().constData());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Result
|
||||
int result = fields[6].toUInt(&ok);
|
||||
if (!ok)
|
||||
{
|
||||
Console.WriteLn("Debugger CSV Import: Failed to parse result flag '%s', skipping", fields[1].toUtf8().constData());
|
||||
continue;
|
||||
}
|
||||
mc.result = static_cast<MemCheckResult>(result);
|
||||
|
||||
m_bpModel.insertBreakpointRows(0, 1, {mc});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CpuWidget::updateFunctionList(bool whenEmpty)
|
||||
{
|
||||
if (!m_cpu.isAlive())
|
||||
|
@ -396,6 +511,14 @@ void CpuWidget::onThreadListContextMenu(QPoint pos)
|
|||
});
|
||||
contextMenu->addAction(actionCopy);
|
||||
|
||||
contextMenu->addSeparator();
|
||||
|
||||
QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.threadList);
|
||||
connect(actionExport, &QAction::triggered, [this]() {
|
||||
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.threadList->model()));
|
||||
});
|
||||
contextMenu->addAction(actionExport);
|
||||
|
||||
contextMenu->popup(m_ui.threadList->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
|
@ -499,6 +622,14 @@ void CpuWidget::onStackListContextMenu(QPoint pos)
|
|||
});
|
||||
contextMenu->addAction(actionCopy);
|
||||
|
||||
contextMenu->addSeparator();
|
||||
|
||||
QAction* actionExport = new QAction(tr("Copy all as CSV"), m_ui.stackList);
|
||||
connect(actionExport, &QAction::triggered, [this]() {
|
||||
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.stackList->model()));
|
||||
});
|
||||
contextMenu->addAction(actionExport);
|
||||
|
||||
contextMenu->popup(m_ui.stackList->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ public slots:
|
|||
void contextBPListDelete();
|
||||
void contextBPListNew();
|
||||
void contextBPListEdit();
|
||||
void contextBPListPasteCSV();
|
||||
|
||||
void updateThreads();
|
||||
void onThreadListDoubleClick(const QModelIndex& index);
|
||||
|
|
|
@ -107,7 +107,7 @@ QVariant BreakpointModel::data(const QModelIndex& index, int role) const
|
|||
switch (index.column())
|
||||
{
|
||||
case BreakpointColumns::TYPE:
|
||||
return 0;
|
||||
return MEMCHECK_INVALID;
|
||||
case BreakpointColumns::OFFSET:
|
||||
return bp->addr;
|
||||
case BreakpointColumns::SIZE_LABEL:
|
||||
|
@ -120,7 +120,7 @@ QVariant BreakpointModel::data(const QModelIndex& index, int role) const
|
|||
case BreakpointColumns::HITS:
|
||||
return 0;
|
||||
case BreakpointColumns::ENABLED:
|
||||
return bp->enabled;
|
||||
return static_cast<int>(bp->enabled);
|
||||
}
|
||||
}
|
||||
else if (const auto* mc = std::get_if<MemCheck>(&bp_mc))
|
||||
|
@ -330,7 +330,7 @@ bool BreakpointModel::insertBreakpointRows(int row, int count, std::vector<Break
|
|||
if (const auto* bp = std::get_if<BreakPoint>(&bp_mc))
|
||||
{
|
||||
Host::RunOnCPUThread([cpu = m_cpu.getCpuType(), bp = *bp] {
|
||||
CBreakPoints::AddBreakPoint(cpu, bp.addr);
|
||||
CBreakPoints::AddBreakPoint(cpu, bp.addr, false, bp.enabled);
|
||||
|
||||
if (bp.hasCond)
|
||||
{
|
||||
|
|
|
@ -95,8 +95,8 @@ namespace QtUtils
|
|||
const int min_column_width = header->minimumSectionSize();
|
||||
const int scrollbar_width = ((view->verticalScrollBar() && view->verticalScrollBar()->isVisible()) ||
|
||||
view->verticalScrollBarPolicy() == Qt::ScrollBarAlwaysOn) ?
|
||||
view->verticalScrollBar()->width() :
|
||||
0;
|
||||
view->verticalScrollBar()->width() :
|
||||
0;
|
||||
int num_flex_items = 0;
|
||||
int total_width = 0;
|
||||
int column_index = 0;
|
||||
|
@ -115,8 +115,8 @@ namespace QtUtils
|
|||
|
||||
const int flex_width =
|
||||
(num_flex_items > 0) ?
|
||||
std::max((view->contentsRect().width() - total_width - scrollbar_width) / num_flex_items, 1) :
|
||||
0;
|
||||
std::max((view->contentsRect().width() - total_width - scrollbar_width) / num_flex_items, 1) :
|
||||
0;
|
||||
|
||||
column_index = 0;
|
||||
for (const int spec_width : widths)
|
||||
|
@ -269,4 +269,40 @@ namespace QtUtils
|
|||
return wi;
|
||||
}
|
||||
|
||||
QString AbstractItemModelToCSV(QAbstractItemModel* model, int role)
|
||||
{
|
||||
QString csv;
|
||||
// Header
|
||||
for (int col = 0; col < model->columnCount(); col++)
|
||||
{
|
||||
csv += model->headerData(col, Qt::Horizontal, Qt::DisplayRole).toString();
|
||||
if (col < model->columnCount() - 1)
|
||||
csv += ",";
|
||||
}
|
||||
|
||||
csv += "\n";
|
||||
|
||||
// Data
|
||||
for (int row = 0; row < model->rowCount(); row++)
|
||||
{
|
||||
for (int col = 0; col < model->columnCount(); col++)
|
||||
{
|
||||
switch(model->data(model->index(row, col), role).metaType().id())
|
||||
{
|
||||
case QMetaType::Int:
|
||||
case QMetaType::UInt:
|
||||
csv += QString::number(model->data(model->index(row, col), role).toUInt(nullptr), 16);
|
||||
break;
|
||||
default:
|
||||
csv += model->data(model->index(row, col), role).toString();
|
||||
break;
|
||||
}
|
||||
|
||||
if (col < model->columnCount() - 1)
|
||||
csv += ",";
|
||||
}
|
||||
csv += "\n";
|
||||
}
|
||||
return csv;
|
||||
}
|
||||
} // namespace QtUtils
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QMetaType>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QAbstractItemModel>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <string_view>
|
||||
|
@ -93,4 +94,7 @@ namespace QtUtils
|
|||
{
|
||||
return QString("%1").arg(QString::number(val, base), sizeof(val) * 2, '0').toUpper();
|
||||
};
|
||||
|
||||
/// Converts an abstract item model to a CSV string.
|
||||
QString AbstractItemModelToCSV(QAbstractItemModel* model, int role = Qt::DisplayRole);
|
||||
} // namespace QtUtils
|
||||
|
|
|
@ -185,13 +185,13 @@ bool CBreakPoints::IsTempBreakPoint(BreakPointCpu cpu, u32 addr)
|
|||
return bp != INVALID_BREAKPOINT;
|
||||
}
|
||||
|
||||
void CBreakPoints::AddBreakPoint(BreakPointCpu cpu, u32 addr, bool temp)
|
||||
void CBreakPoints::AddBreakPoint(BreakPointCpu cpu, u32 addr, bool temp, bool enabled)
|
||||
{
|
||||
size_t bp = FindBreakpoint(cpu, addr, true, temp);
|
||||
if (bp == INVALID_BREAKPOINT)
|
||||
{
|
||||
BreakPoint pt;
|
||||
pt.enabled = true;
|
||||
pt.enabled = enabled;
|
||||
pt.temporary = temp;
|
||||
pt.addr = addr;
|
||||
pt.cpu = cpu;
|
||||
|
|
|
@ -69,6 +69,7 @@ enum MemCheckCondition
|
|||
MEMCHECK_WRITE_ONCHANGE = 0x04,
|
||||
|
||||
MEMCHECK_READWRITE = 0x03,
|
||||
MEMCHECK_INVALID = 0x08, // Invalid condition, used by the CSV parser to know if the line is for a memcheck
|
||||
};
|
||||
|
||||
enum MemCheckResult
|
||||
|
@ -119,7 +120,7 @@ public:
|
|||
static bool IsAddressBreakPoint(BreakPointCpu cpu, u32 addr);
|
||||
static bool IsAddressBreakPoint(BreakPointCpu cpu, u32 addr, bool* enabled);
|
||||
static bool IsTempBreakPoint(BreakPointCpu cpu, u32 addr);
|
||||
static void AddBreakPoint(BreakPointCpu cpu, u32 addr, bool temp = false);
|
||||
static void AddBreakPoint(BreakPointCpu cpu, u32 addr, bool temp = false, bool enabled = true);
|
||||
static void RemoveBreakPoint(BreakPointCpu cpu, u32 addr);
|
||||
static void ChangeBreakPoint(BreakPointCpu cpu, u32 addr, bool enable);
|
||||
static void ClearAllBreakPoints();
|
||||
|
|
Loading…
Reference in New Issue