mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
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:
+16
-233
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user