Debugger: Replace SymbolMap class with new SymbolGuardian class

This new class uses the CCC library I added in the last commit and
parses the symbol tables on a worker thread.
This commit is contained in:
chaoticgd
2024-08-27 12:48:40 -04:00
committed by Ty
parent 87b03fdc28
commit 44b50bee26
30 changed files with 1134 additions and 1319 deletions
+16 -233
View File
@@ -12,7 +12,6 @@
#include "DebugTools/DebugInterface.h"
#include "DebugTools/Breakpoints.h"
#include "DebugTools/BiosDebugData.h"
#include "DebugTools/MipsStackWalk.h"
#include "QtUtils.h"
@@ -100,27 +99,10 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
i++;
}
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(); });
m_ui.disassemblyWidget->SetCpu(&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();
m_ui.savedAddressesList->setModel(&m_savedAddressesModel);
@@ -139,9 +121,11 @@ CpuWidget::CpuWidget(QWidget* parent, DebugInterface& cpu)
DebuggerSettingsManager::loadGameSettings(&m_savedAddressesModel);
connect(m_ui.memorySearchWidget, &MemorySearchWidget::addAddressToSavedAddressesList, this, &CpuWidget::addAddressToSavedAddressesList);
connect(m_ui.memorySearchWidget, &MemorySearchWidget::goToAddressInDisassemblyView, [this](u32 address) { m_ui.disassemblyWidget->gotoAddress(address); });
connect(m_ui.memorySearchWidget, &MemorySearchWidget::goToAddressInDisassemblyView,
[this](u32 address) { m_ui.disassemblyWidget->gotoAddress(address, true); });
connect(m_ui.memorySearchWidget, &MemorySearchWidget::goToAddressInMemoryView, m_ui.memoryviewWidget, &MemoryViewWidget::gotoAddress);
connect(m_ui.memorySearchWidget, &MemorySearchWidget::switchToMemoryViewTab, [this]() { m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); });
connect(m_ui.memorySearchWidget, &MemorySearchWidget::switchToMemoryViewTab,
[this]() { m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory); });
m_ui.memorySearchWidget->setCpu(&m_cpu);
m_refreshDebuggerTimer.setInterval(1000);
@@ -153,13 +137,13 @@ CpuWidget::~CpuWidget() = default;
void CpuWidget::refreshDebugger()
{
if (m_cpu.isAlive())
{
m_ui.registerWidget->update();
m_ui.disassemblyWidget->update();
m_ui.memoryviewWidget->update();
m_ui.memorySearchWidget->update();
}
if (!m_cpu.isAlive())
return;
m_ui.registerWidget->update();
m_ui.disassemblyWidget->update();
m_ui.memoryviewWidget->update();
m_ui.memorySearchWidget->update();
}
void CpuWidget::reloadCPUWidgets()
@@ -317,7 +301,7 @@ void CpuWidget::onBPListDoubleClicked(const QModelIndex& index)
{
if (index.column() == BreakpointModel::OFFSET)
{
m_ui.disassemblyWidget->gotoAddress(m_bpModel.data(index, BreakpointModel::DataRole).toUInt());
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_bpModel.data(index, BreakpointModel::DataRole).toUInt());
}
}
}
@@ -491,7 +475,7 @@ void CpuWidget::onSavedAddressesListContextMenu(QPoint pos)
QAction* goToAddressDisassemblyAction = new QAction(tr("Go to in Disassembly"), m_ui.savedAddressesList);
connect(goToAddressDisassemblyAction, &QAction::triggered, this, [this, indexAtPos]() {
const QModelIndex rowAddressIndex = m_ui.savedAddressesList->model()->index(indexAtPos.row(), 0, QModelIndex());
m_ui.disassemblyWidget->gotoAddress(m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt());
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.savedAddressesList->model()->data(rowAddressIndex, Qt::UserRole).toUInt());
});
contextMenu->addAction(goToAddressDisassemblyAction);
}
@@ -584,73 +568,6 @@ void CpuWidget::addAddressToSavedAddressesList(u32 address)
m_ui.savedAddressesList->edit(m_ui.savedAddressesList->model()->index(rowCount - 1, 1));
}
void CpuWidget::updateFunctionList(bool whenEmpty)
{
if (!m_cpu.isAlive())
return;
if (m_cpu.getCpuType() == BREAKPOINT_EE || !m_moduleView)
{
if (whenEmpty && m_ui.listFunctions->count())
return;
m_ui.listFunctions->clear();
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 (filter.size() && !symbolName.toLower().contains(filter))
continue;
QListWidgetItem* item = new QListWidgetItem();
item->setText(QString("%0 %1").arg(FilledQStringFromValue(symbol.address, 16)).arg(symbolName));
item->setData(Qt::UserRole, symbol.address);
m_ui.listFunctions->addItem(item);
}
}
else
{
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), QString::number(module.exports.size())}));
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());
QTreeWidgetItem* functionItem = new QTreeWidgetItem(moduleItem, QStringList(QString("%0 %1").arg(FilledQStringFromValue(sym.address, 16)).arg(symbolName)));
functionItem->setData(0, Qt::UserRole, 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;
}
}
}
}
void CpuWidget::updateThreads()
{
m_threadModel.refreshData();
@@ -694,145 +611,11 @@ void CpuWidget::onThreadListDoubleClick(const QModelIndex& index)
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
break;
default: // Default to PC
m_ui.disassemblyWidget->gotoAddress(m_ui.threadList->model()->data(m_ui.threadList->model()->index(index.row(), ThreadModel::ThreadColumns::PC), Qt::UserRole).toUInt());
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.threadList->model()->data(m_ui.threadList->model()->index(index.row(), ThreadModel::ThreadColumns::PC), Qt::UserRole).toUInt());
break;
}
}
void CpuWidget::onFuncListContextMenu(QPoint pos)
{
if (!m_funclistContextMenu)
m_funclistContextMenu = new QMenu(m_ui.listFunctions);
else
m_funclistContextMenu->clear();
if (m_ui.listFunctions->selectedItems().count() && m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).isValid())
{
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
// Resolve the function name by fetching the symbolmap and filtering the address
const QListWidgetItem* selectedItem = m_ui.listFunctions->selectedItems().first();
const QString functionName = QString(m_cpu.GetSymbolMap().GetLabelName(selectedItem->data(Qt::UserRole).toUInt()).c_str());
QApplication::clipboard()->setText(functionName);
});
m_funclistContextMenu->addAction(copyName);
QAction* copyAddress = new QAction(tr("Copy Function Address"), m_ui.listFunctions);
connect(copyAddress, &QAction::triggered, [this] {
const QString addressString = FilledQStringFromValue(m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).toUInt(), 16);
QApplication::clipboard()->setText(addressString);
});
m_funclistContextMenu->addAction(copyAddress);
m_funclistContextMenu->addSeparator();
QAction* gotoDisasm = new QAction(tr("Go to in Disassembly"), m_ui.listFunctions);
connect(gotoDisasm, &QAction::triggered, [this] {
m_ui.disassemblyWidget->gotoAddress(m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).toUInt());
});
m_funclistContextMenu->addAction(gotoDisasm);
QAction* gotoMemory = new QAction(tr("Go to in Memory View"), m_ui.listFunctions);
connect(gotoMemory, &QAction::triggered, [this] {
m_ui.memoryviewWidget->gotoAddress(m_ui.listFunctions->selectedItems().first()->data(Qt::UserRole).toUInt());
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
});
m_funclistContextMenu->addAction(gotoMemory);
m_funclistContextMenu->addSeparator();
}
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));
}
void CpuWidget::onFuncListDoubleClick(QListWidgetItem* item)
{
m_ui.disassemblyWidget->gotoAddress(item->data(Qt::UserRole).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, Qt::UserRole).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, Qt::UserRole).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, Qt::UserRole).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, Qt::UserRole).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, Qt::UserRole).toUInt());
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
});
m_moduleTreeContextMenu->addAction(gotoMemory);
}
m_moduleTreeContextMenu->addSeparator();
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, Qt::UserRole).isValid())
{
m_ui.disassemblyWidget->gotoAddress(item->data(0, Qt::UserRole).toUInt());
}
}
void CpuWidget::updateStackFrames()
{
m_stackModel.refreshData();
@@ -873,14 +656,14 @@ void CpuWidget::onStackListDoubleClick(const QModelIndex& index)
{
case StackModel::StackModel::ENTRY:
case StackModel::StackModel::ENTRY_LABEL:
m_ui.disassemblyWidget->gotoAddress(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt());
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::ENTRY), Qt::UserRole).toUInt());
break;
case StackModel::StackModel::SP:
m_ui.memoryviewWidget->gotoAddress(m_ui.stackList->model()->data(index, Qt::UserRole).toUInt());
m_ui.tabWidget->setCurrentWidget(m_ui.tab_memory);
break;
default: // Default to PC
m_ui.disassemblyWidget->gotoAddress(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt());
m_ui.disassemblyWidget->gotoAddressAndSetFocus(m_ui.stackList->model()->data(m_ui.stackList->model()->index(index.row(), StackModel::StackColumns::PC), Qt::UserRole).toUInt());
break;
}
}
-7
View File
@@ -63,11 +63,6 @@ public slots:
void onStackListContextMenu(QPoint pos);
void onStackListDoubleClick(const QModelIndex& index);
void updateFunctionList(bool whenEmpty = false);
void onFuncListContextMenu(QPoint pos);
void onFuncListDoubleClick(QListWidgetItem* item);
void onModuleTreeContextMenu(QPoint pos);
void onModuleTreeDoubleClick(QTreeWidgetItem* item);
void refreshDebugger();
void reloadCPUWidgets();
@@ -91,6 +86,4 @@ private:
QSortFilterProxyModel m_threadProxyModel;
StackModel m_stackModel;
SavedAddressesModel m_savedAddressesModel;
bool m_moduleView = true;
};
+78 -142
View File
@@ -153,9 +153,9 @@ void DisassemblyWidget::contextFollowBranch()
if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO)
{
if (line.info.isBranch)
gotoAddress(line.info.branchTarget);
gotoAddressAndSetFocus(line.info.branchTarget);
else if (line.info.hasRelevantAddress)
gotoAddress(line.info.releventAddress);
gotoAddressAndSetFocus(line.info.releventAddress);
}
}
@@ -176,144 +176,93 @@ void DisassemblyWidget::contextGoToAddress()
return;
}
gotoAddress(targetAddress);
gotoAddressAndSetFocus(targetAddress);
}
void DisassemblyWidget::contextAddFunction()
{
// Get current function
const u32 curAddress = m_selectedAddressStart;
const u32 curFuncAddr = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
QString optionaldlgText;
if (curFuncAddr != SymbolMap::INVALID_ADDRESS)
{
if (curFuncAddr == curAddress) // There is already a function here
{
QMessageBox::warning(this, tr("Add Function Error"), tr("A function entry point already exists here. Consider renaming instead."));
}
else
{
const u32 prevSize = m_cpu->GetSymbolMap().GetFunctionSize(curFuncAddr);
u32 newSize = curAddress - curFuncAddr;
bool ok;
QString funcName = QInputDialog::getText(this, tr("Add Function"),
tr("Function will be (0x%1) instructions long.\nEnter function name").arg(prevSize - newSize, 0, 16), QLineEdit::Normal, "", &ok);
if (!ok)
return;
m_cpu->GetSymbolMap().SetFunctionSize(curFuncAddr, newSize); // End the current function to where we selected
newSize = prevSize - newSize;
m_cpu->GetSymbolMap().AddFunction(funcName.toLocal8Bit().constData(), curAddress, newSize);
m_cpu->GetSymbolMap().SortSymbols();
}
}
else
{
bool ok;
QString funcName = QInputDialog::getText(this, "Add Function",
tr("Function will be (0x%1) instructions long.\nEnter function name").arg(m_selectedAddressEnd + 4 - m_selectedAddressStart, 0, 16), QLineEdit::Normal, "", &ok);
if (!ok)
return;
m_cpu->GetSymbolMap().AddFunction(funcName.toLocal8Bit().constData(), m_selectedAddressStart, m_selectedAddressEnd + 4 - m_selectedAddressStart);
m_cpu->GetSymbolMap().SortSymbols();
}
}
void DisassemblyWidget::contextCopyFunctionName()
{
QGuiApplication::clipboard()->setText(QString::fromStdString(m_cpu->GetSymbolMap().GetLabelName(m_selectedAddressStart)));
std::string name = m_cpu->GetSymbolGuardian().FunctionStartingAtAddress(m_selectedAddressStart).name;
QGuiApplication::clipboard()->setText(QString::fromStdString(name));
}
void DisassemblyWidget::contextRemoveFunction()
{
u32 curFuncAddr = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
m_cpu->GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
ccc::Function* curFunc = database.functions.symbol_overlapping_address(m_selectedAddressStart);
if (!curFunc)
return;
if (curFuncAddr != SymbolMap::INVALID_ADDRESS)
{
u32 previousFuncAddr = m_cpu->GetSymbolMap().GetFunctionStart(curFuncAddr - 4);
if (previousFuncAddr != SymbolMap::INVALID_ADDRESS)
{
// Extend the previous function to replace the spot of the function that is going to be removed
u32 expandedSize = m_cpu->GetSymbolMap().GetFunctionSize(previousFuncAddr) + m_cpu->GetSymbolMap().GetFunctionSize(curFuncAddr);
m_cpu->GetSymbolMap().SetFunctionSize(previousFuncAddr, expandedSize);
}
ccc::Function* previousFunc = database.functions.symbol_overlapping_address(curFunc->address().value - 4);
if (previousFunc)
previousFunc->set_size(curFunc->size() + previousFunc->size());
m_cpu->GetSymbolMap().RemoveFunction(curFuncAddr);
m_cpu->GetSymbolMap().SortSymbols();
}
database.functions.mark_symbol_for_destruction(curFunc->handle(), &database);
database.destroy_marked_symbols();
});
}
void DisassemblyWidget::contextRenameFunction()
{
const u32 curFuncAddress = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
if (curFuncAddress != SymbolMap::INVALID_ADDRESS)
{
bool ok;
QString funcName = QInputDialog::getText(this, tr("Rename Function"), tr("Function name"), QLineEdit::Normal, m_cpu->GetSymbolMap().GetLabelName(curFuncAddress).c_str(), &ok);
if (!ok)
return;
const FunctionInfo curFunc = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart);
if (funcName.isEmpty())
{
QMessageBox::warning(this, tr("Rename Function Error"), tr("Function name cannot be nothing."));
}
else
{
m_cpu->GetSymbolMap().SetLabelName(funcName.toLocal8Bit().constData(), curFuncAddress);
m_cpu->GetSymbolMap().SortSymbols();
this->repaint();
}
}
else
if (!curFunc.address.valid())
{
QMessageBox::warning(this, tr("Rename Function Error"), tr("No function / symbol is currently selected."));
return;
}
QString oldName = QString::fromStdString(curFunc.name);
bool ok;
QString newName = QInputDialog::getText(this, tr("Rename Function"), tr("Function name"), QLineEdit::Normal, oldName, &ok);
if (!ok)
return;
if (newName.isEmpty())
{
QMessageBox::warning(this, tr("Rename Function Error"), tr("Function name cannot be nothing."));
return;
}
m_cpu->GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
database.functions.rename_symbol(curFunc.handle, newName.toStdString());
});
}
void DisassemblyWidget::contextStubFunction()
{
const u32 curFuncAddress = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
if (curFuncAddress != SymbolMap::INVALID_ADDRESS)
{
Host::RunOnCPUThread([this, curFuncAddress, cpu = m_cpu] {
this->m_stubbedFunctions.insert({curFuncAddress, {cpu->read32(curFuncAddress), cpu->read32(curFuncAddress + 4)}});
cpu->write32(curFuncAddress, 0x03E00008); // jr $ra
cpu->write32(curFuncAddress + 4, 0x00000000); // nop
emit VMUpdate();
});
}
else // Stub the current opcode instead
{
Host::RunOnCPUThread([this, cpu = m_cpu] {
this->m_stubbedFunctions.insert({m_selectedAddressStart, {cpu->read32(m_selectedAddressStart), cpu->read32(m_selectedAddressStart + 4)}});
cpu->write32(m_selectedAddressStart, 0x03E00008); // jr $ra
cpu->write32(m_selectedAddressStart + 4, 0x00000000); // nop
emit VMUpdate();
});
}
FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(m_selectedAddressStart);
u32 address = function.address.valid() ? function.address.value : m_selectedAddressStart;
Host::RunOnCPUThread([this, address, cpu = m_cpu] {
this->m_stubbedFunctions.insert({address, {cpu->read32(address), cpu->read32(address + 4)}});
cpu->write32(address, 0x03E00008); // jr ra
cpu->write32(address + 4, 0x00000000); // nop
emit VMUpdate();
});
}
void DisassemblyWidget::contextRestoreFunction()
{
const u32 curFuncAddress = m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart);
if (curFuncAddress != SymbolMap::INVALID_ADDRESS && m_stubbedFunctions.find(curFuncAddress) != m_stubbedFunctions.end())
u32 address = m_selectedAddressStart;
m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) {
const ccc::Function* function = database.functions.symbol_overlapping_address(m_selectedAddressStart);
if (function)
address = function->address().value;
});
auto stub = m_stubbedFunctions.find(address);
if (stub != m_stubbedFunctions.end())
{
Host::RunOnCPUThread([this, curFuncAddress, cpu = m_cpu] {
cpu->write32(curFuncAddress, std::get<0>(this->m_stubbedFunctions[curFuncAddress]));
cpu->write32(curFuncAddress + 4, std::get<1>(this->m_stubbedFunctions[curFuncAddress]));
this->m_stubbedFunctions.erase(curFuncAddress);
emit VMUpdate();
});
}
else if (m_stubbedFunctions.find(m_selectedAddressStart) != m_stubbedFunctions.end())
{
Host::RunOnCPUThread([this, cpu = m_cpu] {
cpu->write32(m_selectedAddressStart, std::get<0>(this->m_stubbedFunctions[m_selectedAddressStart]));
cpu->write32(m_selectedAddressStart + 4, std::get<1>(this->m_stubbedFunctions[m_selectedAddressStart]));
this->m_stubbedFunctions.erase(m_selectedAddressStart);
Host::RunOnCPUThread([this, address, cpu = m_cpu, stub] {
auto [first_instruction, second_instruction] = stub->second;
cpu->write32(address, first_instruction);
cpu->write32(address + 4, second_instruction);
this->m_stubbedFunctions.erase(address);
emit VMUpdate();
});
}
@@ -641,7 +590,7 @@ void DisassemblyWidget::keyPressEvent(QKeyEvent* event)
contextFollowBranch();
break;
case Qt::Key_Left:
gotoAddress(m_cpu->getPC());
gotoAddressAndSetFocus(m_cpu->getPC());
break;
case Qt::Key_O:
m_showInstructionOpcode = !m_showInstructionOpcode;
@@ -666,7 +615,7 @@ void DisassemblyWidget::customMenuRequested(QPoint pos)
contextMenu->addAction(action = new QAction(tr("&Copy Instruction Text"), this));
action->setShortcut(QKeySequence(Qt::Key_C));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyInstructionText);
if (m_selectedAddressStart == m_cpu->GetSymbolMap().GetFunctionStart(m_selectedAddressStart))
if (m_cpu->GetSymbolGuardian().FunctionExistsWithStartingAddress(m_selectedAddressStart))
{
contextMenu->addAction(action = new QAction(tr("Copy Function Name"), this));
connect(action, &QAction::triggered, this, &DisassemblyWidget::contextCopyFunctionName);
@@ -741,13 +690,8 @@ inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFon
const bool isConditionalMet = line.info.conditionMet;
const bool isCurrentPC = m_cpu->getPC() == address;
bool isFunctionNoReturn = false;
const std::string addressSymbol = m_cpu->GetSymbolMap().GetLabelName(address);
if(m_cpu->GetSymbolMap().GetFunctionStart(address) == address)
{
isFunctionNoReturn = m_cpu->GetSymbolMap().GetFunctionNoReturn(address);
}
FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionStartingAtAddress(address);
SymbolInfo symbol = m_cpu->GetSymbolGuardian().SymbolStartingAtAddress(address);
const bool showOpcode = m_showInstructionOpcode && m_cpu->isAlive();
QString lineString;
@@ -760,7 +704,7 @@ inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFon
lineString = QString(" %1 %2 %3 %4 %5 %6");
}
if(isFunctionNoReturn)
if (function.is_no_return)
{
lineString = lineString.arg("NR");
}
@@ -769,13 +713,12 @@ inline QString DisassemblyWidget::DisassemblyStringFromAddress(u32 address, QFon
lineString = lineString.arg(" ");
}
if (addressSymbol.empty()) // The address wont have symbol text if it's the start of a function for example
if (symbol.name.empty())
lineString = lineString.arg(address, 8, 16, QChar('0')).toUpper();
else
{
// We want this text elided
QFontMetrics metric(font);
QString symbolString = QString::fromStdString(addressSymbol);
QString symbolString = QString::fromStdString(symbol.name);
lineString = lineString.arg(metric.elidedText(symbolString, Qt::ElideRight, (selected ? 32 : 7) * font.pointSize()));
}
@@ -829,11 +772,11 @@ QColor DisassemblyWidget::GetAddressFunctionColor(u32 address)
};
}
const auto funNum = m_cpu->GetSymbolMap().GetFunctionNum(address);
if (funNum == -1)
return this->palette().text().color();
ccc::FunctionHandle handle = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(address).handle;
if (!handle.valid())
return palette().text().color();
return colors[funNum % 6];
return colors[handle.value % colors.size()];
}
QString DisassemblyWidget::FetchSelectionInfo(SelectionInfo selInfo)
@@ -861,6 +804,11 @@ QString DisassemblyWidget::FetchSelectionInfo(SelectionInfo selInfo)
return infoBlock;
}
void DisassemblyWidget::gotoAddressAndSetFocus(u32 address)
{
gotoAddress(address, true);
}
void DisassemblyWidget::gotoAddress(u32 address, bool should_set_focus)
{
const u32 destAddress = address & ~3;
@@ -888,21 +836,9 @@ bool DisassemblyWidget::AddressCanRestore(u32 start, u32 end)
bool DisassemblyWidget::FunctionCanRestore(u32 address)
{
u32 funcStartAddress = m_cpu->GetSymbolMap().GetFunctionStart(address);
FunctionInfo function = m_cpu->GetSymbolGuardian().FunctionOverlappingAddress(address);
if (function.address.valid())
address = function.address.value;
if (funcStartAddress != SymbolMap::INVALID_ADDRESS)
{
if (m_stubbedFunctions.find(funcStartAddress) != this->m_stubbedFunctions.end())
{
return true;
}
}
else
{
if (m_stubbedFunctions.find(address) != this->m_stubbedFunctions.end())
{
return true;
}
}
return false;
return m_stubbedFunctions.find(address) != m_stubbedFunctions.end();
}
+2 -1
View File
@@ -58,7 +58,8 @@ public slots:
void contextRestoreFunction();
void contextShowOpcode();
void gotoAddress(u32 address, bool should_set_focus = true);
void gotoAddressAndSetFocus(u32 address);
void gotoAddress(u32 address, bool should_set_focus);
void setDemangle(bool demangle) { m_demangleFunctions = demangle; };
signals:
+3 -3
View File
@@ -47,7 +47,7 @@ QVariant BreakpointModel::data(const QModelIndex& index, int role) const
case BreakpointColumns::OFFSET:
return QtUtils::FilledQStringFromValue(bp->addr, 16);
case BreakpointColumns::SIZE_LABEL:
return m_cpu.GetSymbolMap().GetLabelName(bp->addr).c_str();
return QString::fromStdString(m_cpu.GetSymbolGuardian().FunctionStartingAtAddress(bp->addr).name);
case BreakpointColumns::OPCODE:
// Note: Fix up the disassemblymanager so we can use it here, instead of calling a function through the disassemblyview (yuck)
return m_cpu.disasm(bp->addr, true).c_str();
@@ -100,7 +100,7 @@ QVariant BreakpointModel::data(const QModelIndex& index, int role) const
case BreakpointColumns::OFFSET:
return bp->addr;
case BreakpointColumns::SIZE_LABEL:
return m_cpu.GetSymbolMap().GetLabelName(bp->addr).c_str();
return QString::fromStdString(m_cpu.GetSymbolGuardian().FunctionStartingAtAddress(bp->addr).name);
case BreakpointColumns::OPCODE:
// Note: Fix up the disassemblymanager so we can use it here, instead of calling a function through the disassemblyview (yuck)
return m_cpu.disasm(bp->addr, false).c_str();
@@ -146,7 +146,7 @@ QVariant BreakpointModel::data(const QModelIndex& index, int role) const
case BreakpointColumns::OFFSET:
return QtUtils::FilledQStringFromValue(bp->addr, 16);
case BreakpointColumns::SIZE_LABEL:
return m_cpu.GetSymbolMap().GetLabelName(bp->addr).c_str();
return QString::fromStdString(m_cpu.GetSymbolGuardian().FunctionStartingAtAddress(bp->addr).name);
case BreakpointColumns::OPCODE:
// Note: Fix up the disassemblymanager so we can use it here, instead of calling a function through the disassemblyview (yuck)
return m_cpu.disasm(bp->addr, false).c_str();
+2 -2
View File
@@ -33,7 +33,7 @@ QVariant StackModel::data(const QModelIndex& index, int role) const
case StackModel::ENTRY:
return QtUtils::FilledQStringFromValue(stackFrame.entry, 16);
case StackModel::ENTRY_LABEL:
return m_cpu.GetSymbolMap().GetLabelName(stackFrame.entry).c_str();
return QString::fromStdString(m_cpu.GetSymbolGuardian().FunctionStartingAtAddress(stackFrame.entry).name);
case StackModel::PC:
return QtUtils::FilledQStringFromValue(stackFrame.pc, 16);
case StackModel::PC_OPCODE:
@@ -52,7 +52,7 @@ QVariant StackModel::data(const QModelIndex& index, int role) const
case StackModel::ENTRY:
return stackFrame.entry;
case StackModel::ENTRY_LABEL:
return m_cpu.GetSymbolMap().GetLabelName(stackFrame.entry).c_str();
return QString::fromStdString(m_cpu.GetSymbolGuardian().FunctionStartingAtAddress(stackFrame.entry).name);
case StackModel::PC:
return stackFrame.pc;
case StackModel::PC_OPCODE:
+1 -1
View File
@@ -28,7 +28,7 @@ public:
static constexpr QHeaderView::ResizeMode HeaderResizeModes[StackColumns::COLUMN_COUNT] =
{
QHeaderView::ResizeMode::ResizeToContents,
QHeaderView::ResizeMode::ResizeToContents,
QHeaderView::ResizeMode::Stretch,
QHeaderView::ResizeMode::ResizeToContents,
QHeaderView::ResizeMode::Stretch,
QHeaderView::ResizeMode::ResizeToContents,
-15
View File
@@ -4,7 +4,6 @@
#include "CDVD/CDVDcommon.h"
#include "CDVD/IsoReader.h"
#include "CDVD/IsoFileFormats.h"
#include "DebugTools/SymbolMap.h"
#include "Config.h"
#include "Host.h"
#include "IconsFontAwesome5.h"
@@ -287,20 +286,6 @@ void CDVDsys_SetFile(CDVD_SourceType srctype, std::string newfile)
#endif
m_SourceFilename[enum_cast(srctype)] = std::move(newfile);
// look for symbol file
if (R5900SymbolMap.IsEmpty())
{
std::string symName;
std::string::size_type n = m_SourceFilename[enum_cast(srctype)].rfind('.');
if (n == std::string::npos)
symName = m_SourceFilename[enum_cast(srctype)] + ".sym";
else
symName = m_SourceFilename[enum_cast(srctype)].substr(0, n) + ".sym";
R5900SymbolMap.LoadNocashSym(symName.c_str());
R5900SymbolMap.SortSymbols();
}
}
const std::string& CDVDsys_GetFile(CDVD_SourceType srctype)
+2 -2
View File
@@ -786,7 +786,7 @@ set(pcsx2DebugToolsSources
DebugTools/MipsAssemblerTables.cpp
DebugTools/MipsStackWalk.cpp
DebugTools/Breakpoints.cpp
DebugTools/SymbolMap.cpp
DebugTools/SymbolGuardian.cpp
DebugTools/DisR3000A.cpp
DebugTools/DisR5900asm.cpp
DebugTools/DisVU0Micro.cpp
@@ -803,7 +803,7 @@ set(pcsx2DebugToolsHeaders
DebugTools/MipsAssemblerTables.h
DebugTools/MipsStackWalk.h
DebugTools/Breakpoints.h
DebugTools/SymbolMap.h
DebugTools/SymbolGuardian.h
DebugTools/Debug.h
DebugTools/DisASM.h
DebugTools/DisVUmicro.h
+1 -1
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+
#include "Breakpoints.h"
#include "SymbolMap.h"
#include "SymbolGuardian.h"
#include "MIPSAnalyst.h"
#include <cstdio>
#include "R5900.h"
+112 -21
View File
@@ -12,7 +12,6 @@
#include "R3000A.h"
#include "IopMem.h"
#include "SymbolMap.h"
#include "VMManager.h"
#include "common/StringUtil.h"
@@ -27,16 +26,16 @@ R3000DebugInterface r3000Debug;
enum ReferenceIndexType
{
REF_INDEX_PC = 32,
REF_INDEX_HI = 33,
REF_INDEX_LO = 34,
REF_INDEX_PC = 32,
REF_INDEX_HI = 33,
REF_INDEX_LO = 34,
REF_INDEX_OPTARGET = 0x800,
REF_INDEX_OPSTORE = 0x1000,
REF_INDEX_OPLOAD = 0x2000,
REF_INDEX_IS_OPSL = REF_INDEX_OPTARGET | REF_INDEX_OPSTORE | REF_INDEX_OPLOAD,
REF_INDEX_FPU = 0x4000,
REF_INDEX_FPU_INT = 0x8000,
REF_INDEX_VFPU = 0x10000,
REF_INDEX_OPSTORE = 0x1000,
REF_INDEX_OPLOAD = 0x2000,
REF_INDEX_IS_OPSL = REF_INDEX_OPTARGET | REF_INDEX_OPSTORE | REF_INDEX_OPLOAD,
REF_INDEX_FPU = 0x4000,
REF_INDEX_FPU_INT = 0x8000,
REF_INDEX_VFPU = 0x10000,
REF_INDEX_VFPU_INT = 0x20000,
REF_INDEX_IS_FLOAT = REF_INDEX_FPU | REF_INDEX_VFPU,
@@ -109,10 +108,12 @@ public:
virtual bool parseSymbol(char* str, u64& symbolValue)
{
u32 value;
bool result = cpu->GetSymbolMap().GetLabelValue(str, value);
symbolValue = value;
return result;
SymbolInfo symbol = cpu->GetSymbolGuardian().SymbolWithName(std::string(str));
if (!symbol.address.valid())
return false;
symbolValue = symbol.address.value;
return true;
}
virtual u64 getReferenceValue(u64 referenceIndex)
@@ -131,7 +132,7 @@ public:
const R5900::OPCODE& opcode = R5900::GetInstruction(OP);
if (opcode.flags & IS_MEMORY)
{
// Fetch the address in the base register
// Fetch the address in the base register
u32 target = cpuRegs.GPR.r[(OP >> 21) & 0x1F].UD[0];
// Add the offset (lower 16 bits)
target += static_cast<u16>(OP);
@@ -238,7 +239,7 @@ void DebugInterface::resumeCpu()
char* DebugInterface::stringFromPointer(u32 p)
{
const int BUFFER_LEN = 25;
const int BUFFER_LEN = 64;
static char buf[BUFFER_LEN] = {0};
if (!isValidAddress(p))
@@ -267,6 +268,46 @@ char* DebugInterface::stringFromPointer(u32 p)
return buf;
}
std::optional<u32> DebugInterface::getCallerStackPointer(const ccc::Function& currentFunction)
{
u32 sp = getRegister(EECAT_GPR, 29);
u32 pc = getPC();
if (pc != currentFunction.address().value)
{
std::optional<u32> stack_frame_size = getStackFrameSize(currentFunction);
if (!stack_frame_size.has_value())
return std::nullopt;
sp += *stack_frame_size;
}
return sp;
}
std::optional<u32> DebugInterface::getStackFrameSize(const ccc::Function& function)
{
s32 stack_frame_size = function.stack_frame_size;
if (stack_frame_size < 0)
{
// The stack frame size isn't stored in the symbol table, so we try
// to extract it from the code by checking for an instruction at the
// start of the current function that is in the form of
// "addui $sp, $sp, frame_size" instead.
u32 instruction = read32(function.address().value);
if ((instruction & 0xffff0000) == 0x27bd0000)
stack_frame_size = -(instruction & 0xffff);
if (stack_frame_size < 0)
return std::nullopt;
}
return (u32)stack_frame_size;
}
bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest)
{
MipsExpressionFunctions funcs(this);
@@ -375,6 +416,14 @@ void R5900DebugInterface::write8(u32 address, u8 value)
memWrite8(address, value);
}
void R5900DebugInterface::write16(u32 address, u16 value)
{
if (!isValidAddress(address))
return;
memWrite16(address, value);
}
void R5900DebugInterface::write32(u32 address, u32 value)
{
if (!isValidAddress(address))
@@ -383,6 +432,21 @@ void R5900DebugInterface::write32(u32 address, u32 value)
memWrite32(address, value);
}
void R5900DebugInterface::write64(u32 address, u64 value)
{
if (!isValidAddress(address))
return;
memWrite64(address, value);
}
void R5900DebugInterface::write128(u32 address, u128 value)
{
if (!isValidAddress(address))
return;
memWrite128(address, value);
}
int R5900DebugInterface::getRegisterCategoryCount()
{
@@ -727,9 +791,9 @@ u32 R5900DebugInterface::getCycles()
return cpuRegs.cycle;
}
SymbolMap& R5900DebugInterface::GetSymbolMap() const
SymbolGuardian& R5900DebugInterface::GetSymbolGuardian() const
{
return R5900SymbolMap;
return R5900SymbolGuardian;
}
std::vector<std::unique_ptr<BiosThread>> R5900DebugInterface::GetThreadList() const
@@ -788,7 +852,6 @@ u32 R3000DebugInterface::read32(u32 address, bool& valid)
if (!(valid = isValidAddress(address)))
return -1;
return iopMemRead32(address);
}
u64 R3000DebugInterface::read64(u32 address)
@@ -815,6 +878,14 @@ void R3000DebugInterface::write8(u32 address, u8 value)
iopMemWrite8(address, value);
}
void R3000DebugInterface::write16(u32 address, u16 value)
{
if (!isValidAddress(address))
return;
iopMemWrite16(address, value);
}
void R3000DebugInterface::write32(u32 address, u32 value)
{
if (!isValidAddress(address))
@@ -823,6 +894,26 @@ void R3000DebugInterface::write32(u32 address, u32 value)
iopMemWrite32(address, value);
}
void R3000DebugInterface::write64(u32 address, u64 value)
{
if (!isValidAddress(address))
return;
iopMemWrite32(address + 0, value);
iopMemWrite32(address + 4, value >> 32);
}
void R3000DebugInterface::write128(u32 address, u128 value)
{
if (!isValidAddress(address))
return;
iopMemWrite32(address + 0x0, value._u32[0]);
iopMemWrite32(address + 0x4, value._u32[1]);
iopMemWrite32(address + 0x8, value._u32[2]);
iopMemWrite32(address + 0xc, value._u32[3]);
}
int R3000DebugInterface::getRegisterCategoryCount()
{
return IOPCAT_COUNT;
@@ -1019,9 +1110,9 @@ u32 R3000DebugInterface::getCycles()
return psxRegs.cycle;
}
SymbolMap& R3000DebugInterface::GetSymbolMap() const
SymbolGuardian& R3000DebugInterface::GetSymbolGuardian() const
{
return R3000SymbolMap;
return R3000SymbolGuardian;
}
std::vector<std::unique_ptr<BiosThread>> R3000DebugInterface::GetThreadList() const
+16 -4
View File
@@ -5,7 +5,7 @@
#include "DebugTools/BiosDebugData.h"
#include "MemoryTypes.h"
#include "ExpressionParser.h"
#include "SymbolMap.h"
#include "SymbolGuardian.h"
#include <string>
@@ -51,7 +51,10 @@ public:
virtual u64 read64(u32 address, bool& valid) = 0;
virtual u128 read128(u32 address) = 0;
virtual void write8(u32 address, u8 value) = 0;
virtual void write16(u32 address, u16 value) = 0;
virtual void write32(u32 address, u32 value) = 0;
virtual void write64(u32 address, u64 value) = 0;
virtual void write128(u32 address, u128 value) = 0;
// register stuff
virtual int getRegisterCategoryCount() = 0;
@@ -73,7 +76,7 @@ public:
virtual bool isValidAddress(u32 address) = 0;
virtual u32 getCycles() = 0;
virtual BreakPointCpu getCpuType() = 0;
[[nodiscard]] virtual SymbolMap& GetSymbolMap() const = 0;
[[nodiscard]] virtual SymbolGuardian& GetSymbolGuardian() const = 0;
[[nodiscard]] virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
bool initExpression(const char* exp, PostfixExpression& dest);
@@ -84,6 +87,9 @@ public:
void resumeCpu();
char* stringFromPointer(u32 p);
std::optional<u32> getCallerStackPointer(const ccc::Function& currentFunction);
std::optional<u32> getStackFrameSize(const ccc::Function& currentFunction);
static void setPauseOnEntry(bool pauseOnEntry) { m_pause_on_entry = pauseOnEntry; };
static bool getPauseOnEntry() { return m_pause_on_entry; }
@@ -104,7 +110,10 @@ public:
u64 read64(u32 address, bool& valid) override;
u128 read128(u32 address) override;
void write8(u32 address, u8 value) override;
void write16(u32 address, u16 value) override;
void write32(u32 address, u32 value) override;
void write64(u32 address, u64 value) override;
void write128(u32 address, u128 value) override;
// register stuff
int getRegisterCategoryCount() override;
@@ -121,7 +130,7 @@ public:
bool getCPCOND0() override;
void setPc(u32 newPc) override;
void setRegister(int cat, int num, u128 newValue) override;
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
[[nodiscard]] SymbolGuardian& GetSymbolGuardian() const override;
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::string disasm(u32 address, bool simplify) override;
@@ -144,7 +153,10 @@ public:
u64 read64(u32 address, bool& valid) override;
u128 read128(u32 address) override;
void write8(u32 address, u8 value) override;
void write16(u32 address, u16 value) override;
void write32(u32 address, u32 value) override;
void write64(u32 address, u64 value) override;
void write128(u32 address, u128 value) override;
// register stuff
int getRegisterCategoryCount() override;
@@ -161,7 +173,7 @@ public:
bool getCPCOND0() override;
void setPc(u32 newPc) override;
void setRegister(int cat, int num, u128 newValue) override;
[[nodiscard]] SymbolMap& GetSymbolMap() const override;
[[nodiscard]] SymbolGuardian& GetSymbolGuardian() const override;
[[nodiscard]] std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
std::string disasm(u32 address, bool simplify) override;
+49 -36
View File
@@ -30,7 +30,7 @@ static u32 computeHash(u32 address, u32 size)
}
static void parseDisasm(SymbolMap& map, const char* disasm, char* opcode, char* arguments, size_t arguments_size, bool insertSymbols)
static void parseDisasm(SymbolGuardian& guardian, const char* disasm, char* opcode, char* arguments, size_t arguments_size, bool insertSymbols)
{
if (*disasm == '(')
{
@@ -64,7 +64,7 @@ static void parseDisasm(SymbolMap& map, const char* disasm, char* opcode, char*
u32 branchTarget;
sscanf(disasm+3,"0x%08x",&branchTarget);
const std::string addressSymbol = map.GetLabelName(branchTarget);
const std::string addressSymbol = guardian.SymbolStartingAtAddress(branchTarget).name;
if (!addressSymbol.empty() && insertSymbols)
{
arguments += std::snprintf(arguments, arguments_size - (arguments - arguments_start), "%s",addressSymbol.c_str());
@@ -147,19 +147,50 @@ void DisassemblyManager::analyze(u32 address, u32 size = 1024)
continue;
}
SymbolInfo info;
if (!cpu->GetSymbolMap().GetSymbolInfo(&info,address,ST_ALL))
SymbolInfo info = cpu->GetSymbolGuardian().SymbolOverlappingAddress(
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
if (info.descriptor.has_value())
{
switch (*info.descriptor)
{
case ccc::SymbolDescriptor::FUNCTION:
{
DisassemblyFunction* function = new DisassemblyFunction(cpu,info.address.value,info.size);
entries[info.address.value] = function;
address = info.address.value + info.size;
break;
}
case ccc::SymbolDescriptor::GLOBAL_VARIABLE:
{
DisassemblyData* data = new DisassemblyData(cpu,info.address.value,info.size,DATATYPE_WORD);
entries[info.address.value] = data;
address = info.address.value+info.size;
break;
}
case ccc::SymbolDescriptor::LOCAL_VARIABLE:
{
DisassemblyData* data = new DisassemblyData(cpu,info.address.value,info.size,DATATYPE_WORD);
entries[info.address.value] = data;
address = info.address.value+info.size;
break;
}
default:
break;
}
} else {
if (address % 4)
{
u32 next = std::min<u32>((address+3) & ~3,cpu->GetSymbolMap().GetNextSymbolAddress(address,ST_ALL));
u32 next = std::min<u32>((address+3) & ~3,cpu->GetSymbolGuardian().SymbolAfterAddress(
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE).address.value);
DisassemblyData* data = new DisassemblyData(cpu,address,next-address,DATATYPE_BYTE);
entries[address] = data;
address = next;
continue;
}
u32 next = cpu->GetSymbolMap().GetNextSymbolAddress(address,ST_ALL);
u32 next = cpu->GetSymbolGuardian().SymbolAfterAddress(
address, ccc::FUNCTION | ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE).address.value;
if ((next % 4) && next != 0xFFFFFFFF)
{
@@ -181,26 +212,6 @@ void DisassemblyManager::analyze(u32 address, u32 size = 1024)
address = next;
continue;
}
switch (info.type)
{
case ST_FUNCTION:
{
DisassemblyFunction* function = new DisassemblyFunction(cpu,info.address,info.size);
entries[info.address] = function;
address = info.address+info.size;
}
break;
case ST_DATA:
{
DisassemblyData* data = new DisassemblyData(cpu,info.address,info.size,cpu->GetSymbolMap().GetDataType(info.address));
entries[info.address] = data;
address = info.address+info.size;
}
break;
default:
break;
}
}
}
@@ -401,7 +412,7 @@ bool DisassemblyFunction::disassemble(u32 address, DisassemblyLineInfo& dest, bo
if (it == entries.end())
return false;
return it->second->disassemble(address,dest,simplify, simplify);
return it->second->disassemble(address,dest,insertSymbols,simplify);
}
void DisassemblyFunction::getBranchLines(u32 start, u32 size, std::vector<BranchLine>& dest)
@@ -529,21 +540,23 @@ void DisassemblyFunction::load()
u32 funcPos = address;
u32 funcEnd = address+size;
u32 nextData = cpu->GetSymbolMap().GetNextSymbolAddress(funcPos-1,ST_DATA);
SymbolInfo nextData = cpu->GetSymbolGuardian().SymbolAfterAddress(
funcPos-1, ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
u32 opcodeSequenceStart = funcPos;
while (funcPos < funcEnd)
{
if (funcPos == nextData)
if (funcPos == nextData.address.value && nextData.size > 0)
{
if (opcodeSequenceStart != funcPos)
addOpcodeSequence(opcodeSequenceStart,funcPos);
DisassemblyData* data = new DisassemblyData(cpu,funcPos,cpu->GetSymbolMap().GetDataSize(funcPos),cpu->GetSymbolMap().GetDataType(funcPos));
DisassemblyData* data = new DisassemblyData(cpu,funcPos,nextData.size,DATATYPE_WORD);
entries[funcPos] = data;
lineAddresses.push_back(funcPos);
funcPos += data->getTotalSize();
nextData = cpu->GetSymbolMap().GetNextSymbolAddress(funcPos-1,ST_DATA);
nextData = cpu->GetSymbolGuardian().SymbolAfterAddress(funcPos-1,
ccc::GLOBAL_VARIABLE | ccc::LOCAL_VARIABLE);
opcodeSequenceStart = funcPos;
continue;
}
@@ -593,7 +606,7 @@ void DisassemblyFunction::load()
*/
#if 0
// lui
if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData)
if (MIPS_GET_OP(opInfo.encodedOpcode) == 0x0F && funcPos < funcEnd && funcPos != nextData.address.value)
{
u32 next = cpu->read32(funcPos);
@@ -706,7 +719,7 @@ bool DisassemblyOpcode::disassemble(u32 address, DisassemblyLineInfo& dest, bool
char opcode[64],arguments[256];
std::string dis = cpu->disasm(address,simplify);
parseDisasm(cpu->GetSymbolMap(),dis.c_str(),opcode,arguments,std::size(arguments),insertSymbols);
parseDisasm(cpu->GetSymbolGuardian(),dis.c_str(),opcode,arguments,std::size(arguments),insertSymbols);
dest.type = DISTYPE_OPCODE;
dest.name = opcode;
dest.params = arguments;
@@ -783,7 +796,7 @@ bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo& dest, bool
case MACRO_LI:
dest.name = name;
addressSymbol = cpu->GetSymbolMap().GetLabelName(immediate);
addressSymbol = cpu->GetSymbolGuardian().SymbolStartingAtAddress(immediate).name;
if (!addressSymbol.empty() && insertSymbols)
{
std::snprintf(buffer,std::size(buffer),"%s,%s",cpu->getRegisterName(0,rt),addressSymbol.c_str());
@@ -799,7 +812,7 @@ bool DisassemblyMacro::disassemble(u32 address, DisassemblyLineInfo& dest, bool
case MACRO_MEMORYIMM:
dest.name = name;
addressSymbol = cpu->GetSymbolMap().GetLabelName(immediate);
addressSymbol = cpu->GetSymbolGuardian().SymbolStartingAtAddress(immediate).name;
if (!addressSymbol.empty() && insertSymbols)
{
std::snprintf(buffer,std::size(buffer),"%s,%s",cpu->getRegisterName(0,rt),addressSymbol.c_str());
@@ -994,7 +1007,7 @@ void DisassemblyData::createLines()
case DATATYPE_WORD:
{
value = memRead32(pos);
const std::string label = cpu->GetSymbolMap().GetLabelName(value);
const std::string label = cpu->GetSymbolGuardian().SymbolStartingAtAddress(value).name;
if (!label.empty())
std::snprintf(buffer,std::size(buffer),"%s",label.c_str());
else
+9 -2
View File
@@ -3,7 +3,7 @@
#pragma once
#include "SymbolMap.h"
#include "SymbolGuardian.h"
#include "common/Threading.h"
#include "common/Pcsx2Types.h"
#include "DebugInterface.h"
@@ -94,7 +94,6 @@ private:
int num;
};
class DisassemblyMacro: public DisassemblyEntry
{
public:
@@ -124,6 +123,14 @@ private:
int dataSize;
};
enum DataType
{
DATATYPE_NONE,
DATATYPE_BYTE,
DATATYPE_HALFWORD,
DATATYPE_WORD,
DATATYPE_ASCII
};
class DisassemblyData: public DisassemblyEntry
{
+55 -28
View File
@@ -4,13 +4,10 @@
#include "MIPSAnalyst.h"
#include "Debug.h"
#include "DebugInterface.h"
#include "SymbolMap.h"
#include "DebugInterface.h"
#include "R5900.h"
#include "R5900OpcodeTables.h"
static std::vector<MIPSAnalyst::AnalyzedFunction> functions;
#define MIPS_MAKE_J(addr) (0x08000000 | ((addr)>>2))
#define MIPS_MAKE_JAL(addr) (0x0C000000 | ((addr)>>2))
#define MIPS_MAKE_JR_RA() (0x03e00008)
@@ -117,12 +114,6 @@ namespace MIPSAnalyst
return INVALIDTARGET;
}
static const char *DefaultFunctionName(char buffer[256], u32 startAddr) {
std::snprintf(buffer, 256, "z_un_%08x", startAddr);
return buffer;
}
static u32 ScanAheadForJumpback(u32 fromAddr, u32 knownStart, u32 knownEnd) {
static const u32 MAX_AHEAD_SCAN = 0x1000;
// Maybe a bit high... just to make sure we don't get confused by recursive tail recursion.
@@ -183,7 +174,8 @@ namespace MIPSAnalyst
return furthestJumpbackAddr;
}
void ScanForFunctions(SymbolMap& map, u32 startAddr, u32 endAddr, bool insertSymbols) {
void ScanForFunctions(ccc::SymbolDatabase& database, u32 startAddr, u32 endAddr) {
std::vector<MIPSAnalyst::AnalyzedFunction> functions;
AnalyzedFunction currentFunction = {startAddr};
u32 furthestBranch = 0;
@@ -192,19 +184,14 @@ namespace MIPSAnalyst
bool isStraightLeaf = true;
bool suspectedNoReturn = false;
functions.clear();
u32 addr;
for (addr = startAddr; addr <= endAddr; addr += 4) {
// Use pre-existing symbol map info if available. May be more reliable.
SymbolInfo syminfo;
if (map.GetSymbolInfo(&syminfo, addr, ST_FUNCTION)) {
addr = syminfo.address + syminfo.size - 4;
// We still need to insert the func for hashing purposes.
currentFunction.start = syminfo.address;
currentFunction.end = syminfo.address + syminfo.size - 4;
functions.push_back(currentFunction);
ccc::FunctionHandle existing_symbol_handle = database.functions.first_handle_from_starting_address(addr);
const ccc::Function* existing_symbol = database.functions.symbol_from_handle(existing_symbol_handle);
if (existing_symbol && existing_symbol->address().valid() && existing_symbol->size() > 0) {
addr = existing_symbol->address().value + existing_symbol->size() - 4;
currentFunction.start = addr + 4;
furthestBranch = 0;
looking = false;
@@ -289,10 +276,18 @@ namespace MIPSAnalyst
}
}
// Prevent functions from being generated that overlap with existing
// symbols. This is mainly a problem with symbols from SNDLL symbol
// tables as they will have a size of zero.
ccc::FunctionHandle next_symbol_handle = database.functions.first_handle_from_starting_address(addr+8);
const ccc::Function* next_symbol = database.functions.symbol_from_handle(next_symbol_handle);
end |= next_symbol != nullptr;
if (end) {
// most functions are aligned to 8 or 16 bytes
// add the padding to this one
while (((addr+8) % 16) && r5900Debug.read32(addr+8) == 0)
// Most functions are aligned to 8 or 16 bytes, so add padding
// to this one unless a symbol exists implying a new function
// follows immediately.
while (next_symbol == nullptr && ((addr+8) % 16) && r5900Debug.read32(addr+8) == 0)
addr += 4;
currentFunction.end = addr + 4;
@@ -312,13 +307,45 @@ namespace MIPSAnalyst
currentFunction.end = addr + 4;
functions.push_back(currentFunction);
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Analysis");
if(!source->valid())
return;
for (auto iter = functions.begin(); iter != functions.end(); iter++) {
iter->size = iter->end - iter->start + 4;
if (insertSymbols) {
char temp[256];
map.AddFunction(DefaultFunctionName(temp, iter->start), iter->start, iter->end - iter->start + 4, iter->suspectedNoReturn);
for (const AnalyzedFunction& function : functions) {
ccc::FunctionHandle handle = database.functions.first_handle_from_starting_address(function.start);
ccc::Function* symbol = database.functions.symbol_from_handle(handle);
if (!symbol) {
std::string name;
// The SNDLL importer may create label symbols for functions if
// they're not in a section named ".text" since it can't
// otherwise distinguish between functions and globals.
for (auto [address, handle] : database.labels.handles_from_starting_address(function.start)) {
ccc::Label* label = database.labels.symbol_from_handle(handle);
if (label && !label->is_junk) {
name = label->name();
break;
}
}
if (name.empty()) {
name = StringUtil::StdStringFromFormat("z_un_%08x", function.start);
}
ccc::Result<ccc::Function*> symbol_result = database.functions.create_symbol(
std::move(name), function.start, *source, nullptr);
if (!symbol_result.success())
return;
symbol = *symbol_result;
}
if (symbol->size() == 0) {
symbol->set_size(function.end - function.start + 4);
}
symbol->is_no_return = function.suspectedNoReturn;
}
}
+2 -3
View File
@@ -3,7 +3,7 @@
#pragma once
#include "SymbolMap.h"
#include "SymbolGuardian.h"
class DebugInterface;
@@ -22,7 +22,6 @@ namespace MIPSAnalyst
u32 start;
u32 end;
u64 hash;
u32 size;
bool isStraightLeaf;
bool hasHash;
bool suspectedNoReturn;
@@ -30,7 +29,7 @@ namespace MIPSAnalyst
char name[64];
};
void ScanForFunctions(SymbolMap& map, u32 startAddr, u32 endAddr, bool insertSymbols);
void ScanForFunctions(ccc::SymbolDatabase& database, u32 startAddr, u32 endAddr);
enum LoadStoreLRType { LOADSTORE_NORMAL, LOADSTORE_LEFT, LOADSTORE_RIGHT };
+6 -7
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-2.0+
#include "MipsStackWalk.h"
#include "SymbolMap.h"
#include "SymbolGuardian.h"
#include "MIPSAnalyst.h"
#include "DebugInterface.h"
#include "R5900OpcodeTables.h"
@@ -26,12 +26,11 @@ namespace MipsStackWalk
static u32 GuessEntry(DebugInterface* cpu, u32 pc)
{
SymbolInfo info;
if (cpu->GetSymbolMap().GetSymbolInfo(&info, pc))
{
return info.address;
}
return INVALIDTARGET;
FunctionInfo function = cpu->GetSymbolGuardian().FunctionOverlappingAddress(pc);
if (!function.address.valid())
return INVALIDTARGET;
return function.address.value;
}
bool IsSWInstr(const R5900::OPCODE& op)
File diff suppressed because it is too large Load Diff
+112
View File
@@ -0,0 +1,112 @@
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include <queue>
#include <atomic>
#include <thread>
#include <functional>
#include <shared_mutex>
#include <ccc/symbol_database.h>
#include <ccc/symbol_file.h>
#include "common/Pcsx2Types.h"
class DebugInterface;
struct SymbolInfo
{
std::optional<ccc::SymbolDescriptor> descriptor;
u32 handle = (u32)-1;
std::string name;
ccc::Address address;
u32 size = 0;
};
struct FunctionInfo
{
ccc::FunctionHandle handle;
std::string name;
ccc::Address address;
u32 size = 0;
bool is_no_return = false;
};
struct SymbolGuardian
{
public:
SymbolGuardian();
SymbolGuardian(const SymbolGuardian& rhs) = delete;
SymbolGuardian(SymbolGuardian&& rhs) = delete;
~SymbolGuardian();
SymbolGuardian& operator=(const SymbolGuardian& rhs) = delete;
SymbolGuardian& operator=(SymbolGuardian&& rhs) = delete;
using ReadCallback = std::function<void(const ccc::SymbolDatabase&)>;
using ReadWriteCallback = std::function<void(ccc::SymbolDatabase&)>;
// Take a shared lock on the symbol database and run the callback.
void Read(ReadCallback callback) const noexcept;
// Take an exclusive lock on the symbol database and run the callback.
void ReadWrite(ReadWriteCallback callback) noexcept;
// Delete all stored symbols and create some default built-ins. Should be
// called from the CPU thread.
void Reset();
// Import symbols from the ELF file, nocash symbols, and scan for functions.
// Should be called from the CPU thread.
void ImportElf(std::vector<u8> elf, std::string elf_file_name, const std::string& nocash_path);
// Interrupt the import thread. Should be called from the CPU thread.
void ShutdownWorkerThread();
static ccc::ModuleHandle ImportSymbolTables(
ccc::SymbolDatabase& database, const ccc::SymbolFile& symbol_file, const std::atomic_bool* interrupt);
static bool ImportNocashSymbols(ccc::SymbolDatabase& database, const std::string& file_name);
// Compute original hashes for all the functions based on the code stored in
// the ELF file.
static void ComputeOriginalFunctionHashes(ccc::SymbolDatabase& database, const ccc::ElfFile& elf);
// Compute new hashes for all the functions to check if any of them have
// been overwritten.
void UpdateFunctionHashes(DebugInterface& cpu);
// Delete all symbols from modules that have the "is_irx" flag set.
void ClearIrxModules();
// Copy commonly used attributes of a symbol into a temporary object.
SymbolInfo SymbolStartingAtAddress(
u32 address, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
SymbolInfo SymbolAfterAddress(
u32 address, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
SymbolInfo SymbolOverlappingAddress(
u32 address, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
SymbolInfo SymbolWithName(
const std::string& name, u32 descriptors = ccc::ALL_SYMBOL_TYPES) const;
bool FunctionExistsWithStartingAddress(u32 address) const;
bool FunctionExistsThatOverlapsAddress(u32 address) const;
// Copy commonly used attributes of a function so they can be used by the
// calling thread without needing to keep the lock held.
FunctionInfo FunctionStartingAtAddress(u32 address) const;
FunctionInfo FunctionOverlappingAddress(u32 address) const;
protected:
ccc::SymbolDatabase m_database;
mutable std::shared_mutex m_big_symbol_lock;
std::thread m_import_thread;
std::atomic_bool m_interrupt_import_thread = false;
std::queue<ccc::SymbolDatabase> m_load_queue;
std::mutex m_load_queue_lock;
};
extern SymbolGuardian R5900SymbolGuardian;
extern SymbolGuardian R3000SymbolGuardian;
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More