debugger: display iop module list

This commit is contained in:
Ziemas 2025-05-20 19:31:02 +02:00 committed by Ty
parent 643c83c2a0
commit e50fe50daf
11 changed files with 387 additions and 1 deletions

View File

@ -173,6 +173,10 @@ target_sources(pcsx2-qt PRIVATE
Debugger/DisassemblyView.h
Debugger/DisassemblyView.ui
Debugger/JsonValueWrapper.h
Debugger/ModuleModel.cpp
Debugger/ModuleModel.h
Debugger/ModuleView.cpp
Debugger/ModuleView.h
Debugger/RegisterView.cpp
Debugger/RegisterView.h
Debugger/RegisterView.ui

View File

@ -5,6 +5,7 @@
#include "Debugger/DebuggerEvents.h"
#include "Debugger/DisassemblyView.h"
#include "Debugger/ModuleView.h"
#include "Debugger/RegisterView.h"
#include "Debugger/StackView.h"
#include "Debugger/ThreadView.h"
@ -49,6 +50,7 @@ const std::map<std::string, DockTables::DebuggerViewDescription> DockTables::DEB
DEBUGGER_VIEW(SavedAddressesView, QT_TRANSLATE_NOOP("DebuggerView", "Saved Addresses"), BOTTOM_MIDDLE),
DEBUGGER_VIEW(StackView, QT_TRANSLATE_NOOP("DebuggerView", "Stack"), BOTTOM_MIDDLE),
DEBUGGER_VIEW(ThreadView, QT_TRANSLATE_NOOP("DebuggerView", "Threads"), BOTTOM_MIDDLE),
DEBUGGER_VIEW(ModuleView, QT_TRANSLATE_NOOP("DebuggerView", "Modules"), BOTTOM_MIDDLE),
};
#undef DEBUGGER_VIEW
@ -99,6 +101,7 @@ const std::vector<DockTables::DefaultDockLayout> DockTables::DEFAULT_DOCK_LAYOUT
{"MemoryView", DefaultDockGroup::BOTTOM},
{"BreakpointView", DefaultDockGroup::BOTTOM},
{"ThreadView", DefaultDockGroup::BOTTOM},
{"ModuleView", DefaultDockGroup::BOTTOM},
{"StackView", DefaultDockGroup::BOTTOM},
{"SavedAddressesView", DefaultDockGroup::BOTTOM},
{"GlobalVariableTreeView", DefaultDockGroup::BOTTOM},

View File

@ -0,0 +1,134 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "ModuleModel.h"
#include "QtUtils.h"
#include "fmt/format.h"
ModuleModel::ModuleModel(DebugInterface& cpu, QObject* parent)
: QAbstractTableModel(parent)
, m_cpu(cpu)
{
}
int ModuleModel::rowCount(const QModelIndex&) const
{
return m_cpu.GetModuleList().size();
}
int ModuleModel::columnCount(const QModelIndex&) const
{
return ModuleModel::COLUMN_COUNT;
}
QVariant ModuleModel::data(const QModelIndex& index, int role) const
{
const std::vector<IopMod> Modules = m_cpu.GetModuleList();
size_t row = static_cast<size_t>(index.row());
if (row >= Modules.size())
return QVariant();
const IopMod* mod = &Modules[row];
if (role == Qt::DisplayRole)
{
switch (index.column())
{
case ModuleModel::ModuleColumns::NAME:
return mod->name.c_str();
case ModuleModel::ModuleColumns::VERSION:
return fmt::format("{}.{}", mod->version >> 8, mod->version & 0xff).c_str();
case ModuleModel::ModuleColumns::ENTRY:
return QtUtils::FilledQStringFromValue(mod->entry, 16);
case ModuleModel::ModuleColumns::GP:
return QtUtils::FilledQStringFromValue(mod->gp, 16);
case ModuleModel::ModuleColumns::TEXT_SECTION:
{
return QString("[%1 - %2]").arg(QtUtils::FilledQStringFromValue(mod->text_addr, 16), QtUtils::FilledQStringFromValue(mod->text_addr + mod->text_size - 1, 16));
}
case ModuleModel::ModuleColumns::DATA_SECTION:
{
u32 addr = mod->text_addr + mod->text_size;
return QString("[%1 - %2]").arg(QtUtils::FilledQStringFromValue(addr, 16), QtUtils::FilledQStringFromValue(addr + mod->data_size - 1, 16));
}
case ModuleModel::ModuleColumns::BSS_SECTION:
{
if (mod->bss_size == 0)
{
return "";
}
u32 addr = mod->text_addr + mod->text_size + mod->data_size;
return QString("[%1 - %2]").arg(QtUtils::FilledQStringFromValue(addr, 16), QtUtils::FilledQStringFromValue(addr + mod->bss_size - 1, 16));
}
}
}
else if (role == Qt::UserRole)
{
switch (index.column())
{
case ModuleModel::ModuleColumns::NAME:
return mod->name.c_str();
case ModuleModel::ModuleColumns::VERSION:
return mod->version;
case ModuleModel::ModuleColumns::ENTRY:
return mod->entry;
case ModuleModel::ModuleColumns::GP:
return mod->gp;
case ModuleModel::ModuleColumns::TEXT_SECTION:
return mod->text_addr;
case ModuleModel::ModuleColumns::DATA_SECTION:
return mod->text_addr + mod->text_size;
case ModuleModel::ModuleColumns::BSS_SECTION:
{
if (mod->bss_size == 0)
{
return 0;
}
return mod->text_addr + mod->text_size + mod->data_size;
}
}
}
return QVariant();
}
QVariant ModuleModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
{
switch (section)
{
case ModuleColumns::NAME:
//: Warning: short space limit. Abbreviate if needed.
return tr("NAME");
case ModuleColumns::VERSION:
//: Warning: short space limit. Abbreviate if needed.
return tr("VERSION");
case ModuleColumns::ENTRY:
//: Warning: short space limit. Abbreviate if needed. // Entrypoint of the executable
return tr("ENTRY");
case ModuleColumns::GP:
//: Warning: short space limit. Abbreviate if needed.
return tr("GP");
case ModuleColumns::TEXT_SECTION:
//: Warning: short space limit. Abbreviate if needed. // Text section of the executable
return tr("TEXT");
case ModuleColumns::DATA_SECTION:
//: Warning: short space limit. Abbreviate if needed. // Data section of the executable
return tr("DATA");
case ModuleColumns::BSS_SECTION:
//: Warning: short space limit. Abbreviate if needed. // BSS section of the executable
return tr("BSS");
default:
return QVariant();
}
}
return QVariant();
}
void ModuleModel::refreshData()
{
beginResetModel();
endResetModel();
}

View File

@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include <QtCore/QAbstractTableModel>
#include <QtWidgets/QHeaderView>
#include "DebugTools/DebugInterface.h"
#include "DebugTools/BiosDebugData.h"
class ModuleModel : public QAbstractTableModel
{
Q_OBJECT
public:
enum ModuleColumns : int
{
NAME = 0,
VERSION,
ENTRY,
GP,
TEXT_SECTION,
DATA_SECTION,
BSS_SECTION,
COLUMN_COUNT
};
static constexpr QHeaderView::ResizeMode HeaderResizeModes[ModuleColumns::COLUMN_COUNT] =
{
QHeaderView::ResizeMode::ResizeToContents,
QHeaderView::ResizeMode::Stretch,
QHeaderView::ResizeMode::Stretch,
QHeaderView::ResizeMode::Stretch,
QHeaderView::ResizeMode::ResizeToContents,
QHeaderView::ResizeMode::ResizeToContents,
QHeaderView::ResizeMode::ResizeToContents,
};
explicit ModuleModel(DebugInterface& cpu, QObject* parent = nullptr);
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
void refreshData();
private:
DebugInterface& m_cpu;
};

View File

@ -0,0 +1,99 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#include "ModuleView.h"
#include "QtUtils.h"
#include <QtGui/QClipboard>
#include <QtWidgets/QMenu>
ModuleView::ModuleView(const DebuggerViewParameters& parameters)
: DebuggerView(parameters, MONOSPACE_FONT)
, m_model(new ModuleModel(cpu()))
{
m_ui.setupUi(this);
m_ui.moduleList->setModel(m_model);
m_ui.moduleList->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_ui.moduleList, &QTableView::customContextMenuRequested, this, &ModuleView::openContextMenu);
connect(m_ui.moduleList, &QTableView::doubleClicked, this, &ModuleView::onDoubleClick);
for (std::size_t i = 0; auto mode : ModuleModel::HeaderResizeModes)
{
m_ui.moduleList->horizontalHeader()->setSectionResizeMode(i, mode);
i++;
}
receiveEvent<DebuggerEvents::VMUpdate>([this](const DebuggerEvents::VMUpdate& event) -> bool {
m_model->refreshData();
return true;
});
}
void ModuleView::openContextMenu(QPoint pos)
{
if (!m_ui.moduleList->selectionModel()->hasSelection())
return;
QMenu* menu = new QMenu(m_ui.moduleList);
menu->setAttribute(Qt::WA_DeleteOnClose);
QAction* copy = menu->addAction(tr("Copy"));
connect(copy, &QAction::triggered, [this]() {
const QItemSelectionModel* selection_model = m_ui.moduleList->selectionModel();
if (!selection_model->hasSelection())
return;
QGuiApplication::clipboard()->setText(m_model->data(selection_model->currentIndex()).toString());
});
menu->addSeparator();
QAction* copy_all_as_csv = menu->addAction(tr("Copy all as CSV"));
connect(copy_all_as_csv, &QAction::triggered, [this]() {
QGuiApplication::clipboard()->setText(QtUtils::AbstractItemModelToCSV(m_ui.moduleList->model()));
});
menu->popup(m_ui.moduleList->viewport()->mapToGlobal(pos));
}
void ModuleView::onDoubleClick(const QModelIndex& index)
{
switch (index.column())
{
case ModuleModel::ModuleColumns::ENTRY:
{
goToInDisassembler(m_model->data(index, Qt::UserRole).toUInt(), true);
break;
}
case ModuleModel::ModuleColumns::GP:
{
goToInMemoryView(m_model->data(index, Qt::UserRole).toUInt(), true);
break;
}
case ModuleModel::ModuleColumns::TEXT_SECTION:
{
goToInDisassembler(m_model->data(index, Qt::UserRole).toUInt(), true);
break;
}
case ModuleModel::ModuleColumns::DATA_SECTION:
{
goToInMemoryView(m_model->data(index, Qt::UserRole).toUInt(), true);
break;
}
case ModuleModel::ModuleColumns::BSS_SECTION:
{
auto data = m_model->data(index, Qt::UserRole).toUInt();
if (data)
{
goToInMemoryView(data, true);
}
break;
}
default:
{
break;
}
}
}

View File

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include "ui_ModuleView.h"
#include "DebuggerView.h"
#include "ModuleModel.h"
#include <QtCore/QSortFilterProxyModel>
class ModuleView final : public DebuggerView
{
Q_OBJECT
public:
ModuleView(const DebuggerViewParameters& parameters);
void openContextMenu(QPoint pos);
void onDoubleClick(const QModelIndex& index);
private:
Ui::ModuleView m_ui;
ModuleModel* m_model;
};

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ModuleView</class>
<widget class="QWidget" name="ModuleView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Modules</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTableView" name="moduleList"/>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -121,6 +121,8 @@
<ClCompile Include="Debugger\StackView.cpp" />
<ClCompile Include="Debugger\ThreadModel.cpp" />
<ClCompile Include="Debugger\ThreadView.cpp" />
<ClCompile Include="Debugger\ModuleModel.cpp" />
<ClCompile Include="Debugger\ModuleView.cpp" />
<ClCompile Include="Debugger\Breakpoints\BreakpointDialog.cpp" />
<ClCompile Include="Debugger\Breakpoints\BreakpointModel.cpp" />
<ClCompile Include="Debugger\Breakpoints\BreakpointView.cpp" />
@ -235,6 +237,8 @@
<QtMoc Include="Debugger\RegisterView.h" />
<QtMoc Include="Debugger\StackModel.h" />
<QtMoc Include="Debugger\StackView.h" />
<QtMoc Include="Debugger\ModuleModel.h" />
<QtMoc Include="Debugger\ModuleView.h" />
<QtMoc Include="Debugger\ThreadModel.h" />
<QtMoc Include="Debugger\ThreadView.h" />
<ClInclude Include="Debugger\DebuggerSettingsManager.h" />
@ -309,6 +313,8 @@
<ClCompile Include="$(IntDir)Debugger\moc_RegisterView.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_StackModel.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_StackView.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_ModuleModel.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_ModuleView.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_ThreadModel.cpp" />
<ClCompile Include="$(IntDir)Debugger\moc_ThreadView.cpp" />
<ClCompile Include="$(IntDir)Debugger\Breakpoints\moc_BreakpointDialog.cpp" />
@ -368,6 +374,7 @@
<QtUi Include="Debugger\StackView.ui" />
<QtUi Include="Debugger\SymbolTree\NewSymbolDialog.ui" />
<QtUi Include="Debugger\SymbolTree\SymbolTreeView.ui" />
<QtUi Include="Debugger\ModuleView.ui" />
<QtUi Include="Debugger\ThreadView.ui" />
<QtUi Include="GameList\EmptyGameListWidget.ui" />
<QtUi Include="GameList\GameListWidget.ui" />

View File

@ -79,7 +79,16 @@ std::vector<IopMod> getIOPModules()
{
IopMod mod;
mod.name = iopMemReadString(iopMemRead32(maddr + 4));
u32 nstr = iopMemRead32(maddr + 4);
if (nstr)
{
mod.name = iopMemReadString(iopMemRead32(maddr + 4));
}
else
{
mod.name = "(NULL)";
}
mod.version = iopMemRead16(maddr + 8);
mod.entry = iopMemRead32(maddr + 0x10);
mod.gp = iopMemRead32(maddr + 0x14);

View File

@ -734,6 +734,11 @@ std::vector<std::unique_ptr<BiosThread>> R5900DebugInterface::GetThreadList() co
return getEEThreads();
}
std::vector<IopMod> R5900DebugInterface::GetModuleList() const
{
return {};
}
//
// R3000DebugInterface
//
@ -1062,6 +1067,11 @@ std::vector<std::unique_ptr<BiosThread>> R3000DebugInterface::GetThreadList() co
return getIOPThreads();
}
std::vector<IopMod> R3000DebugInterface::GetModuleList() const
{
return getIOPModules();
}
ElfMemoryReader::ElfMemoryReader(const ccc::ElfFile& elf)
: m_elf(elf)
{

View File

@ -90,6 +90,7 @@ public:
virtual SymbolGuardian& GetSymbolGuardian() const = 0;
virtual SymbolImporter* GetSymbolImporter() const = 0;
virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
virtual std::vector<IopMod> GetModuleList() const = 0;
bool isAlive();
bool isCpuPaused();
@ -151,6 +152,7 @@ public:
SymbolGuardian& GetSymbolGuardian() const override;
SymbolImporter* GetSymbolImporter() const override;
std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::vector<IopMod> GetModuleList() const override;
std::string disasm(u32 address, bool simplify) override;
bool isValidAddress(u32 address) override;
@ -194,6 +196,7 @@ public:
SymbolGuardian& GetSymbolGuardian() const override;
SymbolImporter* GetSymbolImporter() const override;
std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::vector<IopMod> GetModuleList() const override;
std::string disasm(u32 address, bool simplify) override;
bool isValidAddress(u32 address) override;