Merge pull request #13691 from TryTwo/PR_Notes

Debugger Add note-type symbols .
This commit is contained in:
JMC47
2025-06-22 01:05:42 -04:00
committed by GitHub
12 changed files with 519 additions and 65 deletions

View File

@ -51,6 +51,7 @@ void SymbolDB::Clear(const char* prefix)
{ {
// TODO: honor prefix // TODO: honor prefix
m_functions.clear(); m_functions.clear();
m_notes.clear();
m_checksum_to_function.clear(); m_checksum_to_function.clear();
} }

View File

@ -29,6 +29,16 @@ struct SCall
u32 call_address; u32 call_address;
}; };
struct Note
{
std::string name;
u32 address = 0;
u32 size = 0;
int layer = 0;
Note() = default;
};
struct Symbol struct Symbol
{ {
enum class Type enum class Type
@ -71,6 +81,7 @@ class SymbolDB
{ {
public: public:
using XFuncMap = std::map<u32, Symbol>; using XFuncMap = std::map<u32, Symbol>;
using XNoteMap = std::map<u32, Note>;
using XFuncPtrMap = std::map<u32, std::set<Symbol*>>; using XFuncPtrMap = std::map<u32, std::set<Symbol*>>;
SymbolDB(); SymbolDB();
@ -86,6 +97,7 @@ public:
std::vector<Symbol*> GetSymbolsFromHash(u32 hash); std::vector<Symbol*> GetSymbolsFromHash(u32 hash);
const XFuncMap& Symbols() const { return m_functions; } const XFuncMap& Symbols() const { return m_functions; }
const XNoteMap& Notes() const { return m_notes; }
XFuncMap& AccessSymbols() { return m_functions; } XFuncMap& AccessSymbols() { return m_functions; }
bool IsEmpty() const; bool IsEmpty() const;
void Clear(const char* prefix = ""); void Clear(const char* prefix = "");
@ -94,6 +106,7 @@ public:
protected: protected:
XFuncMap m_functions; XFuncMap m_functions;
XNoteMap m_notes;
XFuncPtrMap m_checksum_to_function; XFuncPtrMap m_checksum_to_function;
}; };
} // namespace Common } // namespace Common

View File

@ -91,6 +91,46 @@ void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAdd
} }
} }
void PPCSymbolDB::AddKnownNote(u32 start_addr, u32 size, const std::string& name)
{
auto iter = m_notes.find(start_addr);
if (iter != m_notes.end())
{
// Already got it, just update the name and size.
Common::Note* tempfunc = &iter->second;
tempfunc->name = name;
tempfunc->size = size;
}
else
{
Common::Note tf;
tf.name = name;
tf.address = start_addr;
tf.size = size;
m_notes[start_addr] = tf;
}
}
void PPCSymbolDB::DetermineNoteLayers()
{
if (m_notes.empty())
return;
for (auto& note : m_notes)
note.second.layer = 0;
for (auto iter = m_notes.begin(); iter != m_notes.end(); ++iter)
{
const u32 range = iter->second.address + iter->second.size;
auto search = m_notes.lower_bound(range);
while (--search != iter)
search->second.layer += 1;
}
}
Common::Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr) Common::Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr)
{ {
auto it = m_functions.lower_bound(addr); auto it = m_functions.lower_bound(addr);
@ -112,6 +152,46 @@ Common::Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr)
return nullptr; return nullptr;
} }
Common::Note* PPCSymbolDB::GetNoteFromAddr(u32 addr)
{
if (m_notes.empty())
return nullptr;
auto itn = m_notes.lower_bound(addr);
// If the address is exactly the start address of a symbol, we're done.
if (itn != m_notes.end() && itn->second.address == addr)
return &itn->second;
// Otherwise, check whether the address is within the bounds of a symbol.
if (itn == m_notes.begin())
return nullptr;
do
{
--itn;
// If itn's range reaches the address.
if (addr < itn->second.address + itn->second.size)
return &itn->second;
// If layer is 0, it's the last note that could possibly reach the address, as there are no more
// underlying notes.
} while (itn != m_notes.begin() && itn->second.layer != 0);
return nullptr;
}
void PPCSymbolDB::DeleteFunction(u32 start_address)
{
m_functions.erase(start_address);
}
void PPCSymbolDB::DeleteNote(u32 start_address)
{
m_notes.erase(start_address);
}
std::string_view PPCSymbolDB::GetDescription(u32 addr) std::string_view PPCSymbolDB::GetDescription(u32 addr)
{ {
if (const Common::Symbol* const symbol = GetSymbolFromAddr(addr)) if (const Common::Symbol* const symbol = GetSymbolFromAddr(addr))
@ -406,6 +486,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
if (strlen(name) > 0) if (strlen(name) > 0)
{ {
bool good; bool good;
// Notes will be treated the same as Data.
const Common::Symbol::Type type = section_name == ".text" || section_name == ".init" ? const Common::Symbol::Type type = section_name == ".text" || section_name == ".init" ?
Common::Symbol::Type::Function : Common::Symbol::Type::Function :
Common::Symbol::Type::Data; Common::Symbol::Type::Data;
@ -438,7 +519,11 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
if (good) if (good)
{ {
++good_count; ++good_count;
AddKnownSymbol(guard, vaddress, size, name_string, object_filename_string, type);
if (section_name == ".note")
AddKnownNote(vaddress, size, name);
else
AddKnownSymbol(guard, vaddress, size, name_string, object_filename_string, type);
} }
else else
{ {
@ -448,6 +533,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
} }
Index(); Index();
DetermineNoteLayers();
NOTICE_LOG_FMT(SYMBOLS, "{} symbols loaded, {} symbols ignored.", good_count, bad_count); NOTICE_LOG_FMT(SYMBOLS, "{} symbols loaded, {} symbols ignored.", good_count, bad_count);
return true; return true;
} }
@ -495,6 +581,17 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
file.WriteString(line); file.WriteString(line);
} }
// Write .note section
auto note_symbols = m_notes | std::views::transform([](auto f) { return f.second; });
file.WriteString("\n.note section layout\n");
for (const auto& symbol : note_symbols)
{
// Write symbol address, size, virtual address, alignment, name
const std::string line = fmt::format("{:08x} {:06x} {:08x} {} {}\n", symbol.address,
symbol.size, symbol.address, 0, symbol.name);
file.WriteString(line);
}
return true; return true;
} }

View File

@ -25,13 +25,18 @@ public:
void AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size, void AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
const std::string& name, const std::string& object_name, const std::string& name, const std::string& object_name,
Common::Symbol::Type type = Common::Symbol::Type::Function); Common::Symbol::Type type = Common::Symbol::Type::Function);
void AddKnownNote(u32 start_addr, u32 size, const std::string& name);
Common::Symbol* GetSymbolFromAddr(u32 addr) override; Common::Symbol* GetSymbolFromAddr(u32 addr) override;
bool NoteExists() const { return !m_notes.empty(); }
Common::Note* GetNoteFromAddr(u32 addr);
void DetermineNoteLayers();
void DeleteFunction(u32 start_address);
void DeleteNote(u32 start_address);
std::string_view GetDescription(u32 addr); std::string_view GetDescription(u32 addr);
void FillInCallers(); void FillInCallers();
bool LoadMap(const Core::CPUThreadGuard& guard, const std::string& filename, bool bad = false); bool LoadMap(const Core::CPUThreadGuard& guard, const std::string& filename, bool bad = false);
bool SaveSymbolMap(const std::string& filename) const; bool SaveSymbolMap(const std::string& filename) const;
bool SaveCodeMap(const Core::CPUThreadGuard& guard, const std::string& filename) const; bool SaveCodeMap(const Core::CPUThreadGuard& guard, const std::string& filename) const;

View File

@ -221,6 +221,8 @@ add_executable(dolphin-emu
Debugger/CodeViewWidget.h Debugger/CodeViewWidget.h
Debugger/CodeWidget.cpp Debugger/CodeWidget.cpp
Debugger/CodeWidget.h Debugger/CodeWidget.h
Debugger/EditSymbolDialog.cpp
Debugger/EditSymbolDialog.h
Debugger/GekkoSyntaxHighlight.cpp Debugger/GekkoSyntaxHighlight.cpp
Debugger/GekkoSyntaxHighlight.h Debugger/GekkoSyntaxHighlight.h
Debugger/JitBlockTableModel.cpp Debugger/JitBlockTableModel.cpp

View File

@ -37,6 +37,7 @@
#include "Core/PowerPC/PowerPC.h" #include "Core/PowerPC/PowerPC.h"
#include "Core/System.h" #include "Core/System.h"
#include "DolphinQt/Debugger/AssembleInstructionDialog.h" #include "DolphinQt/Debugger/AssembleInstructionDialog.h"
#include "DolphinQt/Debugger/EditSymbolDialog.h"
#include "DolphinQt/Debugger/PatchInstructionDialog.h" #include "DolphinQt/Debugger/PatchInstructionDialog.h"
#include "DolphinQt/Host.h" #include "DolphinQt/Host.h"
#include "DolphinQt/QtUtils/FromStdString.h" #include "DolphinQt/QtUtils/FromStdString.h"
@ -136,8 +137,9 @@ constexpr int CODE_VIEW_COLUMN_ADDRESS = 1;
constexpr int CODE_VIEW_COLUMN_INSTRUCTION = 2; constexpr int CODE_VIEW_COLUMN_INSTRUCTION = 2;
constexpr int CODE_VIEW_COLUMN_PARAMETERS = 3; constexpr int CODE_VIEW_COLUMN_PARAMETERS = 3;
constexpr int CODE_VIEW_COLUMN_DESCRIPTION = 4; constexpr int CODE_VIEW_COLUMN_DESCRIPTION = 4;
constexpr int CODE_VIEW_COLUMN_BRANCH_ARROWS = 5; constexpr int CODE_VIEW_COLUMN_NOTE = 5;
constexpr int CODE_VIEW_COLUMNCOUNT = 6; constexpr int CODE_VIEW_COLUMN_BRANCH_ARROWS = 6;
constexpr int CODE_VIEW_COLUMNCOUNT = 7;
CodeViewWidget::CodeViewWidget() CodeViewWidget::CodeViewWidget()
: m_system(Core::System::GetInstance()), m_ppc_symbol_db(m_system.GetPPCSymbolDB()) : m_system(Core::System::GetInstance()), m_ppc_symbol_db(m_system.GetPPCSymbolDB())
@ -160,6 +162,7 @@ CodeViewWidget::CodeViewWidget()
setHorizontalHeaderItem(CODE_VIEW_COLUMN_INSTRUCTION, new QTableWidgetItem(tr("Instr."))); setHorizontalHeaderItem(CODE_VIEW_COLUMN_INSTRUCTION, new QTableWidgetItem(tr("Instr.")));
setHorizontalHeaderItem(CODE_VIEW_COLUMN_PARAMETERS, new QTableWidgetItem(tr("Parameters"))); setHorizontalHeaderItem(CODE_VIEW_COLUMN_PARAMETERS, new QTableWidgetItem(tr("Parameters")));
setHorizontalHeaderItem(CODE_VIEW_COLUMN_DESCRIPTION, new QTableWidgetItem(tr("Symbols"))); setHorizontalHeaderItem(CODE_VIEW_COLUMN_DESCRIPTION, new QTableWidgetItem(tr("Symbols")));
setHorizontalHeaderItem(CODE_VIEW_COLUMN_NOTE, new QTableWidgetItem(tr("Notes")));
setHorizontalHeaderItem(CODE_VIEW_COLUMN_BRANCH_ARROWS, new QTableWidgetItem(tr("Branches"))); setHorizontalHeaderItem(CODE_VIEW_COLUMN_BRANCH_ARROWS, new QTableWidgetItem(tr("Branches")));
setFont(Settings::Instance().GetDebugFont()); setFont(Settings::Instance().GetDebugFont());
@ -332,6 +335,11 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
std::string param = (split == std::string::npos ? "" : disas.substr(split + 1)); std::string param = (split == std::string::npos ? "" : disas.substr(split + 1));
const std::string_view desc = debug_interface.GetDescription(addr); const std::string_view desc = debug_interface.GetDescription(addr);
const Common::Note* note = m_ppc_symbol_db.GetNoteFromAddr(addr);
std::string note_string;
if (note != nullptr)
note_string = note->name;
// Adds whitespace and a minimum size to ins and param. Helps to prevent frequent resizing while // Adds whitespace and a minimum size to ins and param. Helps to prevent frequent resizing while
// scrolling. // scrolling.
const QString ins_formatted = const QString ins_formatted =
@ -343,9 +351,11 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
auto* ins_item = new QTableWidgetItem(ins_formatted); auto* ins_item = new QTableWidgetItem(ins_formatted);
auto* param_item = new QTableWidgetItem(param_formatted); auto* param_item = new QTableWidgetItem(param_formatted);
auto* description_item = new QTableWidgetItem(desc_formatted); auto* description_item = new QTableWidgetItem(desc_formatted);
auto* note_item = new QTableWidgetItem(QString::fromStdString(note_string));
auto* branch_item = new QTableWidgetItem(); auto* branch_item = new QTableWidgetItem();
for (auto* item : {bp_item, addr_item, ins_item, param_item, description_item, branch_item}) for (auto* item :
{bp_item, addr_item, ins_item, param_item, description_item, note_item, branch_item})
{ {
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
item->setData(Qt::UserRole, addr); item->setData(Qt::UserRole, addr);
@ -407,6 +417,7 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
setItem(i, CODE_VIEW_COLUMN_INSTRUCTION, ins_item); setItem(i, CODE_VIEW_COLUMN_INSTRUCTION, ins_item);
setItem(i, CODE_VIEW_COLUMN_PARAMETERS, param_item); setItem(i, CODE_VIEW_COLUMN_PARAMETERS, param_item);
setItem(i, CODE_VIEW_COLUMN_DESCRIPTION, description_item); setItem(i, CODE_VIEW_COLUMN_DESCRIPTION, description_item);
setItem(i, CODE_VIEW_COLUMN_NOTE, note_item);
setItem(i, CODE_VIEW_COLUMN_BRANCH_ARROWS, branch_item); setItem(i, CODE_VIEW_COLUMN_BRANCH_ARROWS, branch_item);
if (addr == GetAddress()) if (addr == GetAddress())
@ -415,6 +426,9 @@ void CodeViewWidget::Update(const Core::CPUThreadGuard* guard)
} }
} }
m_ppc_symbol_db.NoteExists() ? showColumn(CODE_VIEW_COLUMN_NOTE) :
hideColumn(CODE_VIEW_COLUMN_NOTE);
CalculateBranchIndentation(); CalculateBranchIndentation();
m_ppc_symbol_db.FillInCallers(); m_ppc_symbol_db.FillInCallers();
@ -565,8 +579,6 @@ void CodeViewWidget::OnContextMenu()
const u32 addr = GetContextAddress(); const u32 addr = GetContextAddress();
const bool has_symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
auto* follow_branch_action = auto* follow_branch_action =
menu->addAction(tr("Follow &Branch"), this, &CodeViewWidget::OnFollowBranch); menu->addAction(tr("Follow &Branch"), this, &CodeViewWidget::OnFollowBranch);
@ -586,17 +598,17 @@ void CodeViewWidget::OnContextMenu()
menu->addAction(tr("Copy Tar&get Address"), this, &CodeViewWidget::OnCopyTargetAddress); menu->addAction(tr("Copy Tar&get Address"), this, &CodeViewWidget::OnCopyTargetAddress);
menu->addSeparator(); menu->addSeparator();
auto* symbol_rename_action = auto* symbol_add_action =
menu->addAction(tr("&Rename Symbol"), this, &CodeViewWidget::OnRenameSymbol); menu->addAction(tr("&Add function symbol"), this, &CodeViewWidget::OnAddFunction);
auto* symbol_size_action = auto* symbol_edit_action =
menu->addAction(tr("Set Symbol &Size"), this, &CodeViewWidget::OnSetSymbolSize); menu->addAction(tr("&Edit function symbol"), this, &CodeViewWidget::OnEditSymbol);
auto* symbol_end_action =
menu->addAction(tr("Set Symbol &End Address"), this, &CodeViewWidget::OnSetSymbolEndAddress); auto* note_add_action = menu->addAction(tr("Add Note"), this, &CodeViewWidget::OnAddNote);
auto* note_edit_action = menu->addAction(tr("Edit Note"), this, &CodeViewWidget::OnEditNote);
menu->addSeparator(); menu->addSeparator();
auto* run_to_action = menu->addAction(tr("Run &to Here"), this, &CodeViewWidget::OnRunToHere); auto* run_to_action = menu->addAction(tr("Run &to Here"), this, &CodeViewWidget::OnRunToHere);
auto* function_action =
menu->addAction(tr("&Add Function"), this, &CodeViewWidget::OnAddFunction);
auto* ppc_action = menu->addAction(tr("PPC vs Host"), this, &CodeViewWidget::OnPPCComparison); auto* ppc_action = menu->addAction(tr("PPC vs Host"), this, &CodeViewWidget::OnPPCComparison);
auto* insert_blr_action = menu->addAction(tr("&Insert BLR"), this, &CodeViewWidget::OnInsertBLR); auto* insert_blr_action = menu->addAction(tr("&Insert BLR"), this, &CodeViewWidget::OnInsertBLR);
auto* insert_nop_action = menu->addAction(tr("Insert &NOP"), this, &CodeViewWidget::OnInsertNOP); auto* insert_nop_action = menu->addAction(tr("Insert &NOP"), this, &CodeViewWidget::OnInsertNOP);
@ -645,20 +657,24 @@ void CodeViewWidget::OnContextMenu()
follow_branch_action->setEnabled(follow_branch_enabled); follow_branch_action->setEnabled(follow_branch_enabled);
for (auto* action : for (auto* action :
{copy_address_action, copy_line_action, copy_hex_action, function_action, run_to_action, {copy_address_action, copy_line_action, copy_hex_action, symbol_add_action,
ppc_action, insert_blr_action, insert_nop_action, replace_action, assemble_action}) symbol_edit_action, note_add_action, note_edit_action, run_to_action, ppc_action,
insert_blr_action, insert_nop_action, replace_action, assemble_action})
{ {
action->setEnabled(running); action->setEnabled(running);
} }
for (auto* action : {symbol_rename_action, symbol_size_action, symbol_end_action})
action->setEnabled(has_symbol);
for (auto* action : {copy_target_memory, show_target_memory}) for (auto* action : {copy_target_memory, show_target_memory})
{ {
action->setEnabled(valid_load_store); action->setEnabled(valid_load_store);
} }
auto* note = m_ppc_symbol_db.GetNoteFromAddr(addr);
note_edit_action->setEnabled(note != nullptr);
// A note cannot be added ontop of the starting address of another note.
if (note != nullptr && note->address == addr)
note_add_action->setEnabled(false);
restore_action->setEnabled(running && restore_action->setEnabled(running &&
m_system.GetPowerPC().GetDebugInterface().HasEnabledPatch(addr)); m_system.GetPowerPC().GetDebugInterface().HasEnabledPatch(addr));
@ -881,6 +897,13 @@ void CodeViewWidget::OnPPCComparison()
void CodeViewWidget::OnAddFunction() void CodeViewWidget::OnAddFunction()
{ {
const u32 addr = GetContextAddress(); const u32 addr = GetContextAddress();
const int confirm =
QMessageBox::warning(this, tr("Add Function Symbol"),
tr("Force new function symbol to be made at %1?").arg(addr, 0, 16),
QMessageBox::Ok | QMessageBox::Cancel);
if (confirm != QMessageBox::Ok)
return;
Core::CPUThreadGuard guard(m_system); Core::CPUThreadGuard guard(m_system);
@ -917,25 +940,80 @@ void CodeViewWidget::OnFollowBranch()
SetAddress(branch_addr, SetAddressUpdate::WithDetailedUpdate); SetAddress(branch_addr, SetAddressUpdate::WithDetailedUpdate);
} }
void CodeViewWidget::OnRenameSymbol() void CodeViewWidget::OnEditSymbol()
{ {
const u32 addr = GetContextAddress(); const u32 addr = GetContextAddress();
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr); Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
if (!symbol) if (symbol == nullptr)
{
OnAddFunction();
return;
}
std::string name = symbol->name;
u32 size = symbol->size;
const u32 symbol_address = symbol->address;
EditSymbolDialog dialog(this, symbol_address, &size, &name);
if (dialog.exec() != QDialog::Accepted)
return; return;
bool good; if (dialog.DeleteRequested())
const QString name =
QInputDialog::getText(this, tr("Rename Symbol"), tr("Symbol Name:"), QLineEdit::Normal,
QString::fromStdString(symbol->name), &good, Qt::WindowCloseButtonHint);
if (good && !name.isEmpty())
{ {
symbol->Rename(name.toStdString()); OnDeleteSymbol();
emit Host::GetInstance()->PPCSymbolsChanged(); return;
} }
if (symbol->name != name)
symbol->Rename(name);
if (symbol->size != size)
{
Core::CPUThreadGuard guard(m_system);
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, size);
}
emit Host::GetInstance()->PPCSymbolsChanged();
}
void CodeViewWidget::OnDeleteSymbol()
{
const u32 addr = GetContextAddress();
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr);
if (symbol == nullptr)
return;
const int confirm = QMessageBox::warning(this, tr("Delete Function Symbol"),
tr("Delete function symbol: %1\nat %2?")
.arg(QString::fromStdString(symbol->name))
.arg(addr, 0, 16),
QMessageBox::Ok | QMessageBox::Cancel);
if (confirm != QMessageBox::Ok)
return;
m_ppc_symbol_db.DeleteFunction(symbol->address);
emit Host::GetInstance()->PPCSymbolsChanged();
}
void CodeViewWidget::OnAddNote()
{
const u32 note_address = GetContextAddress();
std::string name = "";
u32 size = 4;
EditSymbolDialog dialog(this, note_address, &size, &name, EditSymbolDialog::Type::Note);
if (dialog.exec() != QDialog::Accepted || dialog.DeleteRequested())
return;
m_ppc_symbol_db.AddKnownNote(note_address, size, name);
m_ppc_symbol_db.DetermineNoteLayers();
emit Host::GetInstance()->PPCSymbolsChanged();
} }
void CodeViewWidget::OnSelectionChanged() void CodeViewWidget::OnSelectionChanged()
@ -951,53 +1029,57 @@ void CodeViewWidget::OnSelectionChanged()
} }
} }
void CodeViewWidget::OnSetSymbolSize() void CodeViewWidget::OnEditNote()
{ {
const u32 addr = GetContextAddress(); const u32 context_address = GetContextAddress();
Common::Note* const note = m_ppc_symbol_db.GetNoteFromAddr(context_address);
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr); if (note == nullptr)
if (!symbol)
return; return;
bool good; std::string name = note->name;
const int size = QInputDialog::getInt( u32 size = note->size;
this, tr("Rename Symbol"), tr("Symbol Size (%1):").arg(QString::fromStdString(symbol->name)), const u32 note_address = note->address;
symbol->size, 1, 0xFFFF, 1, &good, Qt::WindowCloseButtonHint);
if (!good) EditSymbolDialog dialog(this, note_address, &size, &name, EditSymbolDialog::Type::Note);
if (dialog.exec() != QDialog::Accepted)
return; return;
Core::CPUThreadGuard guard(m_system); if (dialog.DeleteRequested())
{
OnDeleteNote();
return;
}
if (note->name != name || note->size != size)
{
m_ppc_symbol_db.AddKnownNote(note_address, size, name);
m_ppc_symbol_db.DetermineNoteLayers();
}
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, size);
emit Host::GetInstance()->PPCSymbolsChanged(); emit Host::GetInstance()->PPCSymbolsChanged();
} }
void CodeViewWidget::OnSetSymbolEndAddress() void CodeViewWidget::OnDeleteNote()
{ {
const u32 addr = GetContextAddress(); const u32 context_address = GetContextAddress();
Common::Note* const note = m_ppc_symbol_db.GetNoteFromAddr(context_address);
Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(addr); if (note == nullptr)
if (!symbol)
return; return;
bool good; const int confirm = QMessageBox::warning(this, tr("Delete Note"),
const QString name = QInputDialog::getText( tr("Delete Note: %1\nat %2?")
this, tr("Set Symbol End Address"), .arg(QString::fromStdString(note->name))
tr("Symbol End Address (%1):").arg(QString::fromStdString(symbol->name)), QLineEdit::Normal, .arg(context_address, 0, 16),
QStringLiteral("%1").arg(addr + symbol->size, 8, 16, QLatin1Char('0')), &good, QMessageBox::Ok | QMessageBox::Cancel);
Qt::WindowCloseButtonHint);
const u32 address = name.toUInt(&good, 16); if (confirm != QMessageBox::Ok)
if (!good)
return; return;
Core::CPUThreadGuard guard(m_system); m_ppc_symbol_db.DeleteNote(note->address);
PPCAnalyst::ReanalyzeFunction(guard, symbol->address, *symbol, address - symbol->address);
emit Host::GetInstance()->PPCSymbolsChanged(); emit Host::GetInstance()->PPCSymbolsChanged();
} }

View File

@ -87,13 +87,15 @@ private:
void OnCopyFunction(); void OnCopyFunction();
void OnCopyCode(); void OnCopyCode();
void OnCopyHex(); void OnCopyHex();
void OnRenameSymbol();
void OnSelectionChanged(); void OnSelectionChanged();
void OnSetSymbolSize();
void OnSetSymbolEndAddress();
void OnRunToHere(); void OnRunToHere();
void OnAddFunction(); void OnAddFunction();
void OnEditSymbol();
void OnDeleteSymbol();
void OnAddNote();
void OnPPCComparison(); void OnPPCComparison();
void OnEditNote();
void OnDeleteNote();
void OnInsertBLR(); void OnInsertBLR();
void OnInsertNOP(); void OnInsertNOP();
void OnReplaceInstruction(); void OnReplaceInstruction();

View File

@ -16,7 +16,9 @@
#include <QPushButton> #include <QPushButton>
#include <QSplitter> #include <QSplitter>
#include <QStyleHints> #include <QStyleHints>
#include <QTabWidget>
#include <QTableWidget> #include <QTableWidget>
#include <QVBoxLayout>
#include <QWidget> #include <QWidget>
#include "Common/Event.h" #include "Common/Event.h"
@ -119,7 +121,7 @@ void CodeWidget::CreateWidgets()
m_box_splitter = new QSplitter(Qt::Vertical); m_box_splitter = new QSplitter(Qt::Vertical);
m_box_splitter->setStyleSheet(BOX_SPLITTER_STYLESHEET); m_box_splitter->setStyleSheet(BOX_SPLITTER_STYLESHEET);
auto add_search_line_edit = [this](const QString& name, QListWidget* list_widget) { auto add_search_line_edit = [this](const QString& name, QWidget* list_widget) {
auto* widget = new QWidget; auto* widget = new QWidget;
auto* line_layout = new QGridLayout; auto* line_layout = new QGridLayout;
auto* label = new QLabel(name); auto* label = new QLabel(name);
@ -138,8 +140,12 @@ void CodeWidget::CreateWidgets()
m_search_callstack = add_search_line_edit(tr("Callstack"), m_callstack_list); m_search_callstack = add_search_line_edit(tr("Callstack"), m_callstack_list);
// Symbols // Symbols
auto* symbols_tab = new QTabWidget;
m_symbols_list = new QListWidget; m_symbols_list = new QListWidget;
m_search_symbols = add_search_line_edit(tr("Symbols"), m_symbols_list); m_note_list = new QListWidget;
symbols_tab->addTab(m_symbols_list, tr("Symbols"));
symbols_tab->addTab(m_note_list, tr("Notes"));
m_search_symbols = add_search_line_edit(tr("Symbols"), symbols_tab);
// Function calls // Function calls
m_function_calls_list = new QListWidget; m_function_calls_list = new QListWidget;
@ -197,7 +203,7 @@ void CodeWidget::ConnectWidgets()
connect(m_search_callstack, &QLineEdit::textChanged, this, &CodeWidget::UpdateCallstack); connect(m_search_callstack, &QLineEdit::textChanged, this, &CodeWidget::UpdateCallstack);
connect(m_branch_watch, &QPushButton::clicked, this, &CodeWidget::OnBranchWatchDialog); connect(m_branch_watch, &QPushButton::clicked, this, &CodeWidget::OnBranchWatchDialog);
connect(m_note_list, &QListWidget::itemPressed, this, &CodeWidget::OnSelectNote);
connect(m_symbols_list, &QListWidget::itemPressed, this, &CodeWidget::OnSelectSymbol); connect(m_symbols_list, &QListWidget::itemPressed, this, &CodeWidget::OnSelectSymbol);
connect(m_callstack_list, &QListWidget::itemPressed, this, &CodeWidget::OnSelectCallstack); connect(m_callstack_list, &QListWidget::itemPressed, this, &CodeWidget::OnSelectCallstack);
connect(m_function_calls_list, &QListWidget::itemPressed, this, connect(m_function_calls_list, &QListWidget::itemPressed, this,
@ -234,6 +240,7 @@ void CodeWidget::OnSetCodeAddress(u32 address)
void CodeWidget::OnPPCSymbolsChanged() void CodeWidget::OnPPCSymbolsChanged()
{ {
UpdateSymbols(); UpdateSymbols();
UpdateNotes();
UpdateCallstack(); UpdateCallstack();
if (const Common::Symbol* symbol = m_ppc_symbol_db.GetSymbolFromAddr(m_code_view->GetAddress())) if (const Common::Symbol* symbol = m_ppc_symbol_db.GetSymbolFromAddr(m_code_view->GetAddress()))
{ {
@ -277,6 +284,7 @@ void CodeWidget::OnSearchSymbols()
{ {
m_symbol_filter = m_search_symbols->text(); m_symbol_filter = m_search_symbols->text();
UpdateSymbols(); UpdateSymbols();
UpdateNotes();
} }
void CodeWidget::OnSelectSymbol() void CodeWidget::OnSelectSymbol()
@ -296,6 +304,17 @@ void CodeWidget::OnSelectSymbol()
m_code_view->setFocus(); m_code_view->setFocus();
} }
void CodeWidget::OnSelectNote()
{
const auto items = m_note_list->selectedItems();
if (items.isEmpty())
return;
const u32 address = items[0]->data(Qt::UserRole).toUInt();
m_code_view->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithUpdate);
}
void CodeWidget::OnSelectCallstack() void CodeWidget::OnSelectCallstack()
{ {
const auto items = m_callstack_list->selectedItems(); const auto items = m_callstack_list->selectedItems();
@ -424,6 +443,30 @@ void CodeWidget::UpdateSymbols()
m_symbols_list->sortItems(); m_symbols_list->sortItems();
} }
void CodeWidget::UpdateNotes()
{
const QString selection = m_note_list->selectedItems().isEmpty() ?
QStringLiteral("") :
m_note_list->selectedItems()[0]->text();
m_note_list->clear();
for (const auto& note : m_ppc_symbol_db.Notes())
{
const QString name = QString::fromStdString(note.second.name);
auto* item = new QListWidgetItem(name);
if (name == selection)
item->setSelected(true);
item->setData(Qt::UserRole, note.second.address);
if (name.toUpper().indexOf(m_symbol_filter.toUpper()) != -1)
m_note_list->addItem(item);
}
m_note_list->sortItems();
}
void CodeWidget::UpdateFunctionCalls(const Common::Symbol* symbol) void CodeWidget::UpdateFunctionCalls(const Common::Symbol* symbol)
{ {
m_function_calls_list->clear(); m_function_calls_list->clear();

View File

@ -61,11 +61,13 @@ private:
void UpdateCallstack(); void UpdateCallstack();
void UpdateFunctionCalls(const Common::Symbol* symbol); void UpdateFunctionCalls(const Common::Symbol* symbol);
void UpdateFunctionCallers(const Common::Symbol* symbol); void UpdateFunctionCallers(const Common::Symbol* symbol);
void UpdateNotes();
void OnPPCSymbolsChanged(); void OnPPCSymbolsChanged();
void OnSearchAddress(); void OnSearchAddress();
void OnSearchSymbols(); void OnSearchSymbols();
void OnSelectSymbol(); void OnSelectSymbol();
void OnSelectNote();
void OnSelectCallstack(); void OnSelectCallstack();
void OnSelectFunctionCallers(); void OnSelectFunctionCallers();
void OnSelectFunctionCalls(); void OnSelectFunctionCalls();
@ -84,6 +86,7 @@ private:
QListWidget* m_callstack_list; QListWidget* m_callstack_list;
QLineEdit* m_search_symbols; QLineEdit* m_search_symbols;
QListWidget* m_symbols_list; QListWidget* m_symbols_list;
QListWidget* m_note_list;
QLineEdit* m_search_calls; QLineEdit* m_search_calls;
QListWidget* m_function_calls_list; QListWidget* m_function_calls_list;
QLineEdit* m_search_callers; QLineEdit* m_search_callers;

View File

@ -0,0 +1,153 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinQt/Debugger/EditSymbolDialog.h"
#include <QAbstractButton>
#include <QDialogButtonBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QRegularExpression>
#include <QSpinBox>
#include <QVBoxLayout>
EditSymbolDialog::EditSymbolDialog(QWidget* parent, const u32 symbol_address, u32* symbol_size,
std::string* symbol_name, Type type)
: QDialog(parent), m_symbol_name(symbol_name), m_symbol_size(symbol_size),
m_symbol_address(symbol_address)
{
m_type = type == Type::Symbol ? tr("Symbol") : tr("Note");
setWindowTitle(tr("Edit %1").arg(m_type));
CreateWidgets();
ConnectWidgets();
}
void EditSymbolDialog::CreateWidgets()
{
m_buttons = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
m_buttons->addButton(tr("Reset"), QDialogButtonBox::ResetRole);
m_buttons->addButton(tr("Delete"), QDialogButtonBox::DestructiveRole);
QLabel* info_label = new QLabel(tr("Editing %1 starting at: %2\nWarning: Must save the symbol "
"map for changes to be kept.")
.arg(m_type)
.arg(QString::number(m_symbol_address, 16)));
m_name_edit = new QLineEdit();
m_name_edit->setPlaceholderText(tr("%1 name").arg(m_type));
auto* size_layout = new QHBoxLayout;
QLabel* address_end_label = new QLabel(tr("End Address"));
QLabel* size_lines_label = new QLabel(tr("Lines"));
QLabel* size_hex_label = new QLabel(tr("Size: 0x"));
m_address_end_edit = new QLineEdit();
m_size_lines_spin = new QSpinBox();
m_size_hex_edit = new QLineEdit();
size_hex_label->setAlignment(Qt::AlignCenter | Qt::AlignRight);
size_lines_label->setAlignment(Qt::AlignCenter | Qt::AlignRight);
// Get system font and use to size boxes.
QFont font;
QFontMetrics fm(font);
const int width = fm.horizontalAdvance(QLatin1Char('0')) * 2;
m_address_end_edit->setFixedWidth(width * 6);
m_size_hex_edit->setFixedWidth(width * 5);
m_size_lines_spin->setFixedWidth(width * 5);
m_size_hex_edit->setMaxLength(7);
m_size_lines_spin->setRange(0, 99999);
// Accept hex input only
QRegularExpression rx(QStringLiteral("[0-9a-fA-F]{0,8}"));
QValidator* validator = new QRegularExpressionValidator(rx, this);
m_address_end_edit->setValidator(validator);
m_size_hex_edit->setValidator(validator);
size_layout->addWidget(address_end_label);
size_layout->addWidget(m_address_end_edit);
size_layout->addWidget(size_hex_label);
size_layout->addWidget(m_size_hex_edit);
size_layout->addWidget(size_lines_label);
size_layout->addWidget(m_size_lines_spin);
auto* layout = new QVBoxLayout();
layout->addWidget(info_label);
layout->addWidget(m_name_edit);
layout->addLayout(size_layout);
layout->addWidget(m_buttons);
setLayout(layout);
FillFunctionData();
}
void EditSymbolDialog::FillFunctionData()
{
m_name_edit->setText(QString::fromStdString(*m_symbol_name));
m_size_lines_spin->setValue(*m_symbol_size / 4);
m_size_hex_edit->setText(QString::number(*m_symbol_size, 16));
m_address_end_edit->setText(
QStringLiteral("%1").arg(m_symbol_address + *m_symbol_size, 8, 16, QLatin1Char('0')));
}
void EditSymbolDialog::UpdateAddressData(u32 size)
{
// Not sure what the max size should be. Definitely not a full 8, so set to 7.
size = size & 0xFFFFFFF;
m_size_lines_spin->setValue(size / 4);
m_size_hex_edit->setText(QString::number(size, 16));
m_address_end_edit->setText(
QStringLiteral("%1").arg(m_symbol_address + size, 8, 16, QLatin1Char('0')));
}
void EditSymbolDialog::ConnectWidgets()
{
connect(m_size_lines_spin, QOverload<int>::of(&QSpinBox::valueChanged), this,
[this](int value) { UpdateAddressData(value * 4); });
connect(m_size_hex_edit, &QLineEdit::editingFinished, this, [this] {
bool good;
const u32 size = m_size_hex_edit->text().toUInt(&good, 16);
if (good)
UpdateAddressData(size);
});
connect(m_address_end_edit, &QLineEdit::textEdited, this, [this] {
bool good;
const u32 end = m_address_end_edit->text().toUInt(&good, 16);
if (good && end > m_symbol_address)
UpdateAddressData(end - m_symbol_address);
});
connect(m_buttons, &QDialogButtonBox::accepted, this, &EditSymbolDialog::Accepted);
connect(m_buttons, &QDialogButtonBox::rejected, this, &EditSymbolDialog::reject);
connect(m_buttons, &QDialogButtonBox::clicked, this, [this](QAbstractButton* btn) {
const auto role = m_buttons->buttonRole(btn);
if (role == QDialogButtonBox::ButtonRole::ResetRole)
{
FillFunctionData();
}
else if (role == QDialogButtonBox::ButtonRole::DestructiveRole)
{
m_delete_chosen = true;
QDialog::accept();
}
});
}
void EditSymbolDialog::Accepted()
{
const std::string name = m_name_edit->text().toStdString();
if (*m_symbol_name != name)
*m_symbol_name = name;
bool good;
const u32 size = m_size_hex_edit->text().toUInt(&good, 16);
if (good && *m_symbol_size != size)
*m_symbol_size = size;
QDialog::accept();
}

View File

@ -0,0 +1,51 @@
// Copyright 2025 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <string>
#include <QDialog>
#include <QString>
#include "Common/CommonTypes.h"
class QLineEdit;
class QDialogButtonBox;
class QSpinBox;
class EditSymbolDialog : public QDialog
{
Q_OBJECT
public:
enum class Type
{
Symbol,
Note,
};
explicit EditSymbolDialog(QWidget* parent, const u32 symbol_address, u32* symbol_size,
std::string* symbol_name, Type type = Type::Symbol);
bool DeleteRequested() const { return m_delete_chosen; }
private:
void CreateWidgets();
void ConnectWidgets();
void FillFunctionData();
void UpdateAddressData(u32 size);
void Accepted();
QLineEdit* m_name_edit;
QSpinBox* m_size_lines_spin;
QLineEdit* m_size_hex_edit;
QLineEdit* m_address_end_edit;
QDialogButtonBox* m_buttons;
QString m_type;
std::string* m_symbol_name;
u32* m_symbol_size;
const u32 m_symbol_address;
bool m_delete_chosen = false;
};

View File

@ -144,6 +144,7 @@
<ClCompile Include="Debugger\BreakpointWidget.cpp" /> <ClCompile Include="Debugger\BreakpointWidget.cpp" />
<ClCompile Include="Debugger\CodeViewWidget.cpp" /> <ClCompile Include="Debugger\CodeViewWidget.cpp" />
<ClCompile Include="Debugger\CodeWidget.cpp" /> <ClCompile Include="Debugger\CodeWidget.cpp" />
<ClCompile Include="Debugger\EditSymbolDialog.cpp" />
<ClCompile Include="Debugger\GekkoSyntaxHighlight.cpp" /> <ClCompile Include="Debugger\GekkoSyntaxHighlight.cpp" />
<ClCompile Include="Debugger\JitBlockTableModel.cpp" /> <ClCompile Include="Debugger\JitBlockTableModel.cpp" />
<ClCompile Include="Debugger\JITWidget.cpp" /> <ClCompile Include="Debugger\JITWidget.cpp" />
@ -365,6 +366,7 @@
<QtMoc Include="Debugger\BreakpointWidget.h" /> <QtMoc Include="Debugger\BreakpointWidget.h" />
<QtMoc Include="Debugger\CodeViewWidget.h" /> <QtMoc Include="Debugger\CodeViewWidget.h" />
<QtMoc Include="Debugger\CodeWidget.h" /> <QtMoc Include="Debugger\CodeWidget.h" />
<QtMoc Include="Debugger\EditSymbolDialog.h" />
<QtMoc Include="Debugger\GekkoSyntaxHighlight.h" /> <QtMoc Include="Debugger\GekkoSyntaxHighlight.h" />
<QtMoc Include="Debugger\JitBlockTableModel.h" /> <QtMoc Include="Debugger\JitBlockTableModel.h" />
<QtMoc Include="Debugger\JITWidget.h" /> <QtMoc Include="Debugger\JITWidget.h" />