Skip to content

Commit

Permalink
Debugger: IOP Function Tree
Browse files Browse the repository at this point in the history
Co-authored-by: Ziemas <[email protected]>
  • Loading branch information
F0bes and Ziemas committed Oct 26, 2023
1 parent a0ba441 commit 6b45c98
Show file tree
Hide file tree
Showing 9 changed files with 449 additions and 55 deletions.
216 changes: 182 additions & 34 deletions pcsx2-qt/Debugger/CpuWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
connect(m_ui.tabWidgetRegFunc, &QTabWidget::currentChanged, [this](int i) {if(i == 1){updateFunctionList(true);} });
connect(m_ui.listFunctions, &QListWidget::customContextMenuRequested, this, &CpuWidget::onFuncListContextMenu);
connect(m_ui.listFunctions, &QListWidget::itemDoubleClicked, this, &CpuWidget::onFuncListDoubleClick);
connect(m_ui.treeModules, &QTreeWidget::customContextMenuRequested, this, &CpuWidget::onModuleTreeContextMenu);
connect(m_ui.treeModules, &QTreeWidget::itemDoubleClicked, this, &CpuWidget::onModuleTreeDoubleClick);
connect(m_ui.btnRefreshFunctions, &QPushButton::clicked, [this] { updateFunctionList(); });
connect(m_ui.txtFuncSearch, &QLineEdit::textChanged, [this] { updateFunctionList(); });

Expand All @@ -109,6 +111,15 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
m_ui.registerWidget->SetCpu(&cpu);
m_ui.memoryviewWidget->SetCpu(&cpu);

if (cpu.getCpuType() == BREAKPOINT_EE)
{
m_ui.treeModules->setVisible(false);
}
else
{
m_ui.treeModules->header()->setSectionResizeMode(0, QHeaderView::ResizeMode::ResizeToContents);
m_ui.listFunctions->setVisible(false);
}
this->repaint();
}

Expand Down Expand Up @@ -467,36 +478,82 @@ void CpuWidget::updateFunctionList(bool whenEmpty)
if (!m_cpu.isAlive())
return;

if (whenEmpty && m_ui.listFunctions->count())
return;
if (m_cpu.getCpuType() == BREAKPOINT_EE || !m_moduleView)
{
if (whenEmpty && m_ui.listFunctions->count())
return;

m_ui.listFunctions->clear();
m_ui.listFunctions->clear();

const auto demangler = demangler::CDemangler::createGcc();
const QString filter = m_ui.txtFuncSearch->text().toLower();
for (const auto& symbol : m_cpu.GetSymbolMap().GetAllSymbols(SymbolType::ST_FUNCTION))
{
QString symbolName = symbol.name.c_str();
if (m_demangleFunctions)
const auto demangler = demangler::CDemangler::createGcc();
const QString filter = m_ui.txtFuncSearch->text().toLower();
for (const auto& symbol : m_cpu.GetSymbolMap().GetAllSymbols(SymbolType::ST_FUNCTION))
{
symbolName = QString(demangler->demangleToString(symbol.name).c_str());
QString symbolName = symbol.name.c_str();
if (m_demangleFunctions)
{
symbolName = QString(demangler->demangleToString(symbol.name).c_str());

// If the name isn't mangled, or it doesn't understand, it'll return an empty string
// Fall back to the original name if this is the case
if (symbolName.isEmpty())
symbolName = symbol.name.c_str();
}
// If the name isn't mangled, or it doesn't understand, it'll return an empty string
// Fall back to the original name if this is the case
if (symbolName.isEmpty())
symbolName = symbol.name.c_str();
}

if (filter.size() && !symbolName.toLower().contains(filter))
continue;
if (filter.size() && !symbolName.toLower().contains(filter))
continue;

QListWidgetItem* item = new QListWidgetItem();
QListWidgetItem* item = new QListWidgetItem();

item->setText(QString("%0 %1").arg(FilledQStringFromValue(symbol.address, 16)).arg(symbolName));
item->setText(QString("%0 %1").arg(FilledQStringFromValue(symbol.address, 16)).arg(symbolName));

item->setData(256, symbol.address);
item->setData(256, symbol.address);

m_ui.listFunctions->addItem(item);
m_ui.listFunctions->addItem(item);
}
}
else
{
const auto demangler = demangler::CDemangler::createGcc();
const QString filter = m_ui.txtFuncSearch->text().toLower();

m_ui.treeModules->clear();
for (const auto& module : m_cpu.GetSymbolMap().GetModules())
{
QTreeWidgetItem* moduleItem = new QTreeWidgetItem(m_ui.treeModules, QStringList({QString(module.name.c_str()), QString("%0.%1").arg(module.version.major).arg(module.version.minor)}));
QList<QTreeWidgetItem*> functions;
for (const auto& sym : module.exports)
{
if (!QString(sym.name.c_str()).toLower().contains(filter))
continue;

QString symbolName = QString(sym.name.c_str());
if (m_demangleFunctions)
{
QString demangledName = QString(demangler->demangleToString(sym.name).c_str());
if (!demangledName.isEmpty())
symbolName = demangledName;
}
QTreeWidgetItem* functionItem = new QTreeWidgetItem(moduleItem, QStringList(QString("%0 %1").arg(FilledQStringFromValue(sym.address, 16)).arg(symbolName)));
functionItem->setData(0, 256, sym.address);
functions.append(functionItem);
}
moduleItem->addChildren(functions);

if (!filter.isEmpty() && functions.size())
{
moduleItem->setExpanded(true);
m_ui.treeModules->insertTopLevelItem(0, moduleItem);
}
else if (filter.isEmpty())
{
m_ui.treeModules->insertTopLevelItem(0, moduleItem);
}
else
{
delete moduleItem;
}
}
}
}

Expand Down Expand Up @@ -555,19 +612,6 @@ void CpuWidget::onFuncListContextMenu(QPoint pos)
else
m_funclistContextMenu->clear();

//: "Demangling" is the opposite of "Name mangling", which is a process where a compiler takes function names and combines them with other characteristics of the function (e.g. what types of data it accepts) to ensure they stay unique even when multiple functions exist with the same name (but different inputs / const-ness). See here: https://en.wikipedia.org/wiki/Name_mangling#C++
QAction* demangleAction = new QAction(tr("Demangle Symbols"), m_ui.listFunctions);
demangleAction->setCheckable(true);
demangleAction->setChecked(m_demangleFunctions);

connect(demangleAction, &QAction::triggered, [this] {
m_demangleFunctions = !m_demangleFunctions;
m_ui.disassemblyWidget->setDemangle(m_demangleFunctions);
updateFunctionList();
});

m_funclistContextMenu->addAction(demangleAction);

QAction* copyName = new QAction(tr("Copy Function Name"), m_ui.listFunctions);
connect(copyName, &QAction::triggered, [this] {
// We only store the address in the widget item
Expand Down Expand Up @@ -601,9 +645,38 @@ void CpuWidget::onFuncListContextMenu(QPoint pos)
m_ui.memoryviewWidget->gotoAddress(m_ui.listFunctions->selectedItems().first()->data(256).toUInt());
});

m_funclistContextMenu->addSeparator();

m_funclistContextMenu->addAction(gotoMemory);

//: "Demangling" is the opposite of "Name mangling", which is a process where a compiler takes function names and combines them with other characteristics of the function (e.g. what types of data it accepts) to ensure they stay unique even when multiple functions exist with the same name (but different inputs / const-ness). See here: https://en.wikipedia.org/wiki/Name_mangling#C++
QAction* demangleAction = new QAction(tr("Demangle Symbols"), m_ui.listFunctions);
demangleAction->setCheckable(true);
demangleAction->setChecked(m_demangleFunctions);

connect(demangleAction, &QAction::triggered, [this] {
m_demangleFunctions = !m_demangleFunctions;
m_ui.disassemblyWidget->setDemangle(m_demangleFunctions);
updateFunctionList();
});

m_funclistContextMenu->addAction(demangleAction);

if (m_cpu.getCpuType() == BREAKPOINT_IOP)
{
QAction* moduleViewAction = new QAction(tr("Module Tree"), m_ui.listFunctions);
moduleViewAction->setCheckable(true);
moduleViewAction->setChecked(m_moduleView);

connect(moduleViewAction, &QAction::triggered, [this] {
m_moduleView = !m_moduleView;
m_ui.treeModules->setVisible(m_moduleView);
m_ui.listFunctions->setVisible(!m_moduleView);
updateFunctionList();
});

m_funclistContextMenu->addAction(moduleViewAction);
}
m_funclistContextMenu->popup(m_ui.listFunctions->viewport()->mapToGlobal(pos));
}

Expand All @@ -612,6 +685,81 @@ void CpuWidget::onFuncListDoubleClick(QListWidgetItem* item)
m_ui.disassemblyWidget->gotoAddress(item->data(256).toUInt());
}

void CpuWidget::onModuleTreeContextMenu(QPoint pos)
{
if (!m_moduleTreeContextMenu)
m_moduleTreeContextMenu = new QMenu(m_ui.treeModules);
else
m_moduleTreeContextMenu->clear();

if (m_ui.treeModules->selectedItems().count() && m_ui.treeModules->selectedItems().first()->data(0, 256).isValid())
{
QAction* copyName = new QAction(tr("Copy Function Name"), m_ui.treeModules);
connect(copyName, &QAction::triggered, [this] {
QApplication::clipboard()->setText(m_cpu.GetSymbolMap().GetLabelName(m_ui.treeModules->selectedItems().first()->data(0, 256).toUInt()).c_str());
});
m_moduleTreeContextMenu->addAction(copyName);

QAction* copyAddress = new QAction(tr("Copy Function Address"), m_ui.treeModules);
connect(copyAddress, &QAction::triggered, [this] {
const QString addressString = FilledQStringFromValue(m_ui.treeModules->selectedItems().first()->data(0, 256).toUInt(), 16);
QApplication::clipboard()->setText(addressString);
});
m_moduleTreeContextMenu->addAction(copyAddress);

m_moduleTreeContextMenu->addSeparator();

QAction* gotoDisasm = new QAction(tr("Go to in Disassembly"), m_ui.treeModules);
connect(gotoDisasm, &QAction::triggered, [this] {
m_ui.disassemblyWidget->gotoAddress(m_ui.treeModules->selectedItems().first()->data(0, 256).toUInt());
});
m_moduleTreeContextMenu->addAction(gotoDisasm);

QAction* gotoMemory = new QAction(tr("Go to in Memory View"), m_ui.treeModules);
connect(gotoMemory, &QAction::triggered, [this] {
m_ui.memoryviewWidget->gotoAddress(m_ui.treeModules->selectedItems().first()->data(0, 256).toUInt());
});
m_moduleTreeContextMenu->addAction(gotoMemory);
}

//: "Demangling" is the opposite of "Name mangling", which is a process where a compiler takes function names and combines them with other characteristics of the function (e.g. what types of data it accepts) to ensure they stay unique even when multiple functions exist with the same name (but different inputs / const-ness). See here: https://en.wikipedia.org/wiki/Name_mangling#C++
QAction* demangleAction = new QAction(tr("Demangle Symbols"), m_ui.treeModules);
demangleAction->setCheckable(true);
demangleAction->setChecked(m_demangleFunctions);

connect(demangleAction, &QAction::triggered, [this] {
m_demangleFunctions = !m_demangleFunctions;
m_ui.disassemblyWidget->setDemangle(m_demangleFunctions);
updateFunctionList();
});

m_moduleTreeContextMenu->addSeparator();

m_moduleTreeContextMenu->addAction(demangleAction);

QAction* moduleViewAction = new QAction(tr("Module Tree"), m_ui.treeModules);
moduleViewAction->setCheckable(true);
moduleViewAction->setChecked(m_moduleView);

connect(moduleViewAction, &QAction::triggered, [this] {
m_moduleView = !m_moduleView;
m_ui.treeModules->setVisible(m_moduleView);
m_ui.listFunctions->setVisible(!m_moduleView);
updateFunctionList();
});

m_moduleTreeContextMenu->addAction(moduleViewAction);

m_moduleTreeContextMenu->popup(m_ui.treeModules->viewport()->mapToGlobal(pos));
}

void CpuWidget::onModuleTreeDoubleClick(QTreeWidgetItem* item)
{
if (item->data(0, 256).isValid())
{
m_ui.disassemblyWidget->gotoAddress(item->data(0, 256).toUInt());
}
}
void CpuWidget::updateStackFrames()
{
m_stackModel.refreshData();
Expand Down
5 changes: 4 additions & 1 deletion pcsx2-qt/Debugger/CpuWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ public slots:
void onFuncListContextMenu(QPoint pos);
void onFuncListDoubleClick(QListWidgetItem* item);
bool getDemangleFunctions() const { return m_demangleFunctions; }

void onModuleTreeContextMenu(QPoint pos);
void onModuleTreeDoubleClick(QTreeWidgetItem* item);
void reloadCPUWidgets()
{
if (!QtHost::IsOnUIThread())
Expand All @@ -99,6 +100,7 @@ public slots:

QMenu* m_stacklistContextMenu = 0;
QMenu* m_funclistContextMenu = 0;
QMenu* m_moduleTreeContextMenu = 0;

Ui::CpuWidget m_ui;

Expand All @@ -110,4 +112,5 @@ public slots:
StackModel m_stackModel;

bool m_demangleFunctions = true;
bool m_moduleView = true;
};
29 changes: 29 additions & 0 deletions pcsx2-qt/Debugger/CpuWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,35 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTreeWidget" name="treeModules">
<property name="enabled">
<bool>true</bool>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="indentation">
<number>8</number>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<property name="columnCount">
<number>2</number>
</property>
<column>
<property name="text">
<string>Module</string>
</property>
</column>
<column>
<property name="text">
<string>Version</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QListWidget" name="listFunctions">
<property name="contextMenuPolicy">
Expand Down
Loading

0 comments on commit 6b45c98

Please sign in to comment.