diff --git a/3rdparty/ccc/src/ccc/symbol_table.cpp b/3rdparty/ccc/src/ccc/symbol_table.cpp index d8b6db6428be77..863e23633f7a38 100644 --- a/3rdparty/ccc/src/ccc/symbol_table.cpp +++ b/3rdparty/ccc/src/ccc/symbol_table.cpp @@ -100,8 +100,9 @@ Result> create_elf_symbol_table( Result import_symbol_tables( SymbolDatabase& database, - std::string module_name, const std::vector>& symbol_tables, + std::string module_name, + Address base_address, u32 importer_flags, DemanglerFunctions demangler, const std::atomic_bool* interrupt) @@ -109,7 +110,8 @@ Result import_symbol_tables( Result module_source = database.get_symbol_source("Symbol Table Importer"); CCC_RETURN_IF_ERROR(module_source); - Result module_symbol = database.modules.create_symbol(std::move(module_name), *module_source, nullptr); + Result module_symbol = database.modules.create_symbol( + std::move(module_name), base_address, *module_source, nullptr); CCC_RETURN_IF_ERROR(module_symbol); ModuleHandle module_handle = (*module_symbol)->handle(); diff --git a/3rdparty/ccc/src/ccc/symbol_table.h b/3rdparty/ccc/src/ccc/symbol_table.h index 72f5ccde1196f6..84c84600539317 100644 --- a/3rdparty/ccc/src/ccc/symbol_table.h +++ b/3rdparty/ccc/src/ccc/symbol_table.h @@ -71,8 +71,9 @@ Result> create_elf_symbol_table( // and to generate a module handle. Result import_symbol_tables( SymbolDatabase& database, - std::string module_name, const std::vector>& symbol_tables, + std::string module_name, + Address base_address, u32 importer_flags, DemanglerFunctions demangler, const std::atomic_bool* interrupt); diff --git a/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.cpp b/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.cpp index a0fe6cc038188c..d2689a2830cae8 100644 --- a/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.cpp +++ b/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.cpp @@ -104,7 +104,7 @@ DebugAnalysisSettingsWidget::DebugAnalysisSettingsWidget(SettingsWindow* dialog, else { m_ui.symbolFileLabel->hide(); - m_ui.symbolFileList->hide(); + m_ui.symbolFileTable->hide(); m_ui.importSymbolFileButtons->hide(); } @@ -165,16 +165,32 @@ void DebugAnalysisSettingsWidget::parseSettingsFromWidgets(Pcsx2Config::DebugAna output.DemangleSymbols = m_ui.demangleSymbols->isChecked(); output.DemangleParameters = m_ui.demangleParameters->isChecked(); - for (int i = 0; i < m_ui.symbolFileList->count(); i++) + for (int i = 0; i < m_symbol_file_model->rowCount(); i++) { DebugExtraSymbolFile& file = output.ExtraSymbolFiles.emplace_back(); - file.Path = m_ui.symbolFileList->item(i)->text().toStdString(); + + file.Path = m_symbol_file_model->item(i, PATH_COLUMN)->text().toStdString(); + + bool ok; + u32 base_address = m_symbol_file_model->item(i, BASE_ADDRESS_COLUMN)->text().toUInt(&ok, 16); + if (ok) + file.BaseAddress = base_address; + + file.Condition = m_symbol_file_model->item(i, CONDITION_COLUMN)->text().toStdString(); } output.FunctionScanMode = static_cast(m_ui.functionScanMode->currentIndex()); output.CustomFunctionScanRange = m_ui.customAddressRange->isChecked(); - output.FunctionScanStartAddress = m_ui.addressRangeStart->text().toStdString(); - output.FunctionScanEndAddress = m_ui.addressRangeEnd->text().toStdString(); + + bool ok; + + u32 function_scan_start_address = m_ui.addressRangeStart->text().toUInt(&ok, 16); + if (ok) + output.FunctionScanStartAddress = function_scan_start_address; + + u32 function_scan_end_address = m_ui.addressRangeEnd->text().toUInt(&ok, 16); + if (ok) + output.FunctionScanEndAddress = function_scan_end_address; output.GenerateFunctionHashes = m_ui.grayOutOverwrittenFunctions->isChecked(); } @@ -187,27 +203,12 @@ void DebugAnalysisSettingsWidget::setupSymbolSourceGrid() { // Add symbol sources for which the user has already selected whether or // not they should be cleared. - int existing_symbol_source_count; - if (m_dialog) - existing_symbol_source_count = m_dialog->getEffectiveIntValue("Debugger/Analysis/SymbolSources", "Count", 0); - else - existing_symbol_source_count = Host::GetIntSettingValue("Debugger/Analysis/SymbolSources", "Count", 0); - + int existing_symbol_source_count = getIntSettingValue("Debugger/Analysis/SymbolSources", "Count", 0); for (int i = 0; i < existing_symbol_source_count; i++) { std::string section = "Debugger/Analysis/SymbolSources/" + std::to_string(i); - - std::string name; - if (m_dialog) - name = m_dialog->getEffectiveStringValue(section.c_str(), "Name", ""); - else - name = Host::GetStringSettingValue(section.c_str(), "Name", ""); - - bool value; - if (m_dialog) - value = m_dialog->getEffectiveBoolValue(section.c_str(), "ClearDuringAnalysis", false); - else - value = Host::GetBoolSettingValue(section.c_str(), "ClearDuringAnalysis", false); + std::string name = getStringSettingValue(section.c_str(), "Name", ""); + bool value = getBoolSettingValue(section.c_str(), "ClearDuringAnalysis", false); SymbolSourceTemp& source = m_symbol_sources[name]; source.previous_value = value; @@ -320,45 +321,95 @@ void DebugAnalysisSettingsWidget::saveSymbolSources() void DebugAnalysisSettingsWidget::setupSymbolFileList() { - int extra_symbol_file_count; - if (m_dialog) - extra_symbol_file_count = m_dialog->getEffectiveIntValue("Debugger/Analysis/ExtraSymbolFiles", "Count", 0); - else - extra_symbol_file_count = Host::GetIntSettingValue("Debugger/Analysis/ExtraSymbolFiles", "Count", 0); + m_symbol_file_model = new QStandardItemModel(0, SYMBOL_FILE_COLUMN_COUNT, m_ui.symbolFileTable); + + QStringList headers; + headers.emplace_back(tr("Path")); + headers.emplace_back(tr("Base Address")); + headers.emplace_back(tr("Condition")); + m_symbol_file_model->setHorizontalHeaderLabels(headers); + + m_ui.symbolFileTable->setModel(m_symbol_file_model); + m_ui.symbolFileTable->horizontalHeader()->setSectionResizeMode(PATH_COLUMN, QHeaderView::Stretch); + m_ui.symbolFileTable->horizontalHeader()->setSectionResizeMode(BASE_ADDRESS_COLUMN, QHeaderView::Fixed); + m_ui.symbolFileTable->horizontalHeader()->setSectionResizeMode(CONDITION_COLUMN, QHeaderView::Fixed); + + m_ui.symbolFileTable->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + + int extra_symbol_file_count = getIntSettingValue("Debugger/Analysis/ExtraSymbolFiles", "Count", 0); for (int i = 0; i < extra_symbol_file_count; i++) { std::string section = "Debugger/Analysis/ExtraSymbolFiles/" + std::to_string(i); - std::string path; - if (m_dialog) - path = m_dialog->getEffectiveStringValue(section.c_str(), "Path", ""); - else - path = Host::GetStringSettingValue(section.c_str(), "Path", ""); - m_ui.symbolFileList->addItem(QString::fromStdString(path)); + int row = m_symbol_file_model->rowCount(); + if (!m_symbol_file_model->insertRow(row)) + continue; + + QStandardItem* path_item = new QStandardItem(); + path_item->setText(QString::fromStdString(getStringSettingValue(section.c_str(), "Path", ""))); + m_symbol_file_model->setItem(row, PATH_COLUMN, path_item); + + QStandardItem* base_address_item = new QStandardItem(); + u32 base_address = getUIntSettingValue(section.c_str(), "BaseAddress", (u32)-1); + if (base_address != (u32)-1) + base_address_item->setText(QString::number(base_address, 16)); + m_symbol_file_model->setItem(row, BASE_ADDRESS_COLUMN, base_address_item); + + QStandardItem* condition_item = new QStandardItem(); + condition_item->setText(QString::fromStdString(getStringSettingValue(section.c_str(), "Condition"))); + m_symbol_file_model->setItem(row, CONDITION_COLUMN, condition_item); } connect(m_ui.addSymbolFile, &QPushButton::clicked, this, &DebugAnalysisSettingsWidget::addSymbolFile); connect(m_ui.removeSymbolFile, &QPushButton::clicked, this, &DebugAnalysisSettingsWidget::removeSymbolFile); + + connect(m_ui.symbolFileTable->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &DebugAnalysisSettingsWidget::updateEnabledStates); + + connect(m_symbol_file_model, &QStandardItemModel::dataChanged, + this, &DebugAnalysisSettingsWidget::saveSymbolFiles); + connect(m_symbol_file_model, &QStandardItemModel::dataChanged, + this, &DebugAnalysisSettingsWidget::updateEnabledStates); } void DebugAnalysisSettingsWidget::addSymbolFile() { - QString path = QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Add Symbol File"))); - if (path.isEmpty()) + std::string path = Path::ToNativePath(QFileDialog::getOpenFileName(this, tr("Add Symbol File")).toStdString()); + if (path.empty()) + return; + + std::string relative_path = Path::MakeRelative(path, EmuFolders::GameSettings); + if (!relative_path.starts_with("..")) + path = std::move(relative_path); + + int row = m_symbol_file_model->rowCount(); + if (!m_symbol_file_model->insertRow(row)) return; - m_ui.symbolFileList->addItem(path); + QStandardItem* path_item = new QStandardItem(); + path_item->setText(QString::fromStdString(path)); + m_symbol_file_model->setItem(row, PATH_COLUMN, path_item); + + QStandardItem* base_address_item = new QStandardItem(); + base_address_item->setText(""); + m_symbol_file_model->setItem(row, BASE_ADDRESS_COLUMN, base_address_item); + + QStandardItem* condition_item = new QStandardItem(); + condition_item->setText(""); + m_symbol_file_model->setItem(row, CONDITION_COLUMN, condition_item); saveSymbolFiles(); + updateEnabledStates(); } void DebugAnalysisSettingsWidget::removeSymbolFile() { - for (QListWidgetItem* item : m_ui.symbolFileList->selectedItems()) - delete item; + for (QModelIndex index : m_ui.symbolFileTable->selectionModel()->selectedRows()) + m_symbol_file_model->removeRow(index.row()); saveSymbolFiles(); + updateEnabledStates(); } void DebugAnalysisSettingsWidget::saveSymbolFiles() @@ -380,17 +431,29 @@ void DebugAnalysisSettingsWidget::saveSymbolFiles() sif->RemoveSection("Debugger/Analysis/ExtraSymbolFiles"); - if (m_ui.symbolFileList->count() == 0) + if (m_symbol_file_model->rowCount() == 0) return; // Make new configuration entries. - sif->SetIntValue("Debugger/Analysis/ExtraSymbolFiles", "Count", m_ui.symbolFileList->count()); + sif->SetIntValue("Debugger/Analysis/ExtraSymbolFiles", "Count", m_symbol_file_model->rowCount()); - for (int i = 0; i < m_ui.symbolFileList->count(); i++) + for (int i = 0; i < m_symbol_file_model->rowCount(); i++) { std::string section = "Debugger/Analysis/ExtraSymbolFiles/" + std::to_string(i); - std::string path = m_ui.symbolFileList->item(i)->text().toStdString(); - sif->SetStringValue(section.c_str(), "Path", path.c_str()); + + if (QStandardItem* path_item = m_symbol_file_model->item(i, PATH_COLUMN)) + sif->SetStringValue(section.c_str(), "Path", path_item->text().toStdString().c_str()); + + if (QStandardItem* base_address_item = m_symbol_file_model->item(i, BASE_ADDRESS_COLUMN)) + { + bool ok; + u32 base_address = base_address_item->text().toUInt(&ok, 16); + if (ok) + sif->SetUIntValue(section.c_str(), "BaseAddress", base_address); + } + + if (QStandardItem* condition_item = m_symbol_file_model->item(i, CONDITION_COLUMN)) + sif->SetStringValue(section.c_str(), "Condition", condition_item->text().toStdString().c_str()); } QtHost::SaveGameSettings(sif, true); @@ -423,5 +486,43 @@ void DebugAnalysisSettingsWidget::updateEnabledStates() m_ui.symbolSourceScrollArea->setEnabled(!m_ui.automaticallyClearSymbols->isChecked()); m_ui.symbolSourceErrorMessage->setEnabled(!m_ui.automaticallyClearSymbols->isChecked()); m_ui.demangleParameters->setEnabled(m_ui.demangleSymbols->isChecked()); + m_ui.removeSymbolFile->setEnabled( + m_ui.symbolFileTable->selectionModel() && m_ui.symbolFileTable->selectionModel()->hasSelection()); m_ui.customAddressRangeLineEdits->setEnabled(m_ui.customAddressRange->isChecked()); } + +std::string DebugAnalysisSettingsWidget::getStringSettingValue( + const char* section, const char* key, const char* default_value) +{ + if (m_dialog) + return m_dialog->getEffectiveStringValue(section, key, default_value); + + return Host::GetStringSettingValue(section, key, default_value); +} + +bool DebugAnalysisSettingsWidget::getBoolSettingValue( + const char* section, const char* key, bool default_value) +{ + if (m_dialog) + return m_dialog->getEffectiveBoolValue(section, key, default_value); + + return Host::GetBoolSettingValue(section, key, default_value); +} + +int DebugAnalysisSettingsWidget::getIntSettingValue( + const char* section, const char* key, int default_value) +{ + if (m_dialog) + return m_dialog->getEffectiveIntValue(section, key, default_value); + + return Host::GetIntSettingValue(section, key, default_value); +} + +uint DebugAnalysisSettingsWidget::getUIntSettingValue( + const char* section, const char* key, uint default_value) +{ + if (m_dialog) + return m_dialog->getEffectiveUIntValue(section, key, default_value); + + return Host::GetUIntSettingValue(section, key, default_value); +} diff --git a/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.h b/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.h index 52d81c1f0570b1..5e0ca390396843 100644 --- a/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.h +++ b/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.h @@ -6,6 +6,7 @@ #include "Config.h" +#include #include class SettingsWindow; @@ -16,7 +17,7 @@ class DebugAnalysisSettingsWidget : public QWidget public: // Create a widget that will discard any settings changed after it is - // closed, for use in the dialog opened by the "Reanalyze" button. + // closed, for use in the dialog opened by the "Analyze" button. DebugAnalysisSettingsWidget(QWidget* parent = nullptr); // Create a widget that will write back any settings changed to the config @@ -42,6 +43,11 @@ class DebugAnalysisSettingsWidget : public QWidget void updateEnabledStates(); + std::string getStringSettingValue(const char* section, const char* key, const char* default_value = ""); + bool getBoolSettingValue(const char* section, const char* key, bool default_value = false); + int getIntSettingValue(const char* section, const char* key, int default_value = 0); + uint getUIntSettingValue(const char* section, const char* key, uint default_value = 0); + struct SymbolSourceTemp { QCheckBox* check_box = nullptr; @@ -49,8 +55,18 @@ class DebugAnalysisSettingsWidget : public QWidget bool modified_by_user = false; }; + enum SymbolFileColumn + { + PATH_COLUMN = 0, + BASE_ADDRESS_COLUMN = 1, + CONDITION_COLUMN = 2, + SYMBOL_FILE_COLUMN_COUNT = 3 + }; + SettingsWindow* m_dialog = nullptr; std::map m_symbol_sources; + QStandardItemModel* m_symbol_file_model; + Ui::DebugAnalysisSettingsWidget m_ui; }; diff --git a/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.ui b/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.ui index 9f6575cbba0add..4b2759ef0b2433 100644 --- a/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.ui +++ b/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.ui @@ -171,7 +171,7 @@ - + 0 @@ -184,9 +184,27 @@ 100 - + + true + + + QAbstractItemView::SelectRows + + + Qt::ElideLeft + + false + + false + + + true + + + false + diff --git a/pcsx2-qt/Settings/SettingsWindow.cpp b/pcsx2-qt/Settings/SettingsWindow.cpp index ddbea6ed276607..2f53926f00bfed 100644 --- a/pcsx2-qt/Settings/SettingsWindow.cpp +++ b/pcsx2-qt/Settings/SettingsWindow.cpp @@ -465,6 +465,15 @@ int SettingsWindow::getEffectiveIntValue(const char* section, const char* key, i return Host::GetBaseIntSettingValue(section, key, default_value); } +uint SettingsWindow::getEffectiveUIntValue(const char* section, const char* key, uint default_value) const +{ + uint value; + if (m_sif && m_sif->GetUIntValue(section, key, &value)) + return value; + else + return Host::GetBaseUIntSettingValue(section, key, default_value); +} + float SettingsWindow::getEffectiveFloatValue(const char* section, const char* key, float default_value) const { float value; @@ -520,6 +529,25 @@ std::optional SettingsWindow::getIntValue(const char* section, const char* return value; } +std::optional SettingsWindow::getUIntValue(const char* section, const char* key, std::optional default_value) const +{ + std::optional value; + if (m_sif) + { + uint ivalue; + if (m_sif->GetUIntValue(section, key, &ivalue)) + value = ivalue; + else + value = default_value; + } + else + { + value = Host::GetBaseUIntSettingValue(section, key, default_value.value_or(0)); + } + + return value; +} + std::optional SettingsWindow::getFloatValue(const char* section, const char* key, std::optional default_value) const { std::optional value; @@ -589,6 +617,21 @@ void SettingsWindow::setIntSettingValue(const char* section, const char* key, st } } +void SettingsWindow::setUIntSettingValue(const char* section, const char* key, std::optional value) +{ + if (m_sif) + { + value.has_value() ? m_sif->SetUIntValue(section, key, value.value()) : m_sif->DeleteValue(section, key); + saveAndReloadGameSettings(); + } + else + { + value.has_value() ? Host::SetBaseUIntSettingValue(section, key, value.value()) : Host::RemoveBaseSettingValue(section, key); + Host::CommitBaseSettingChanges(); + g_emu_thread->applySettings(); + } +} + void SettingsWindow::setFloatSettingValue(const char* section, const char* key, std::optional value) { if (m_sif) diff --git a/pcsx2-qt/Settings/SettingsWindow.h b/pcsx2-qt/Settings/SettingsWindow.h index 7425f3bfb581f8..8729022ed0b684 100644 --- a/pcsx2-qt/Settings/SettingsWindow.h +++ b/pcsx2-qt/Settings/SettingsWindow.h @@ -79,16 +79,19 @@ class SettingsWindow final : public QWidget // Helper functions for reading effective setting values (from game -> global settings). bool getEffectiveBoolValue(const char* section, const char* key, bool default_value) const; int getEffectiveIntValue(const char* section, const char* key, int default_value) const; + uint getEffectiveUIntValue(const char* section, const char* key, uint default_value) const; float getEffectiveFloatValue(const char* section, const char* key, float default_value) const; std::string getEffectiveStringValue(const char* section, const char* key, const char* default_value) const; // Helper functions for reading setting values for this layer (game settings or global). std::optional getBoolValue(const char* section, const char* key, std::optional default_value) const; std::optional getIntValue(const char* section, const char* key, std::optional default_value) const; + std::optional getUIntValue(const char* section, const char* key, std::optional default_value) const; std::optional getFloatValue(const char* section, const char* key, std::optional default_value) const; std::optional getStringValue(const char* section, const char* key, std::optional default_value) const; void setBoolSettingValue(const char* section, const char* key, std::optional value); void setIntSettingValue(const char* section, const char* key, std::optional value); + void setUIntSettingValue(const char* section, const char* key, std::optional value); void setFloatSettingValue(const char* section, const char* key, std::optional value); void setStringSettingValue(const char* section, const char* key, std::optional value); bool containsSettingValue(const char* section, const char* key) const; diff --git a/pcsx2/Config.h b/pcsx2/Config.h index fc64f1c22bae3d..699ad01a115a70 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -208,6 +208,8 @@ struct DebugSymbolSource struct DebugExtraSymbolFile { std::string Path; + u32 BaseAddress = (u32)-1; + std::string Condition; friend auto operator<=>(const DebugExtraSymbolFile& lhs, const DebugExtraSymbolFile& rhs) = default; }; @@ -1103,8 +1105,8 @@ struct Pcsx2Config DebugFunctionScanMode FunctionScanMode = DebugFunctionScanMode::SCAN_ELF; bool CustomFunctionScanRange = false; - std::string FunctionScanStartAddress = "0"; - std::string FunctionScanEndAddress = "0"; + u32 FunctionScanStartAddress = 0; + u32 FunctionScanEndAddress = 0; bool GenerateFunctionHashes = true; @@ -1276,7 +1278,7 @@ struct Pcsx2Config EnableGameFixes : 1, // enables automatic game fixes SaveStateOnShutdown : 1, // default value for saving state on shutdown EnableDiscordPresence : 1, // enables discord rich presence integration - UseSavestateSelector: 1, + UseSavestateSelector : 1, InhibitScreensaver : 1, BackupSavestate : 1, McdFolderAutoManage : 1, diff --git a/pcsx2/DebugTools/DebugInterface.cpp b/pcsx2/DebugTools/DebugInterface.cpp index 8554ffa109fbf0..adb63faf8d3b99 100644 --- a/pcsx2/DebugTools/DebugInterface.cpp +++ b/pcsx2/DebugTools/DebugInterface.cpp @@ -34,227 +34,6 @@ enum ReferenceIndexType REF_INDEX_VFPU = 0x10000, REF_INDEX_VFPU_INT = 0x20000, REF_INDEX_IS_FLOAT = REF_INDEX_FPU | REF_INDEX_VFPU, - -}; - - -class MipsExpressionFunctions : public IExpressionFunctions -{ -public: - explicit MipsExpressionFunctions(DebugInterface* cpu, bool enumerateSymbols) - : m_cpu(cpu) - { - if (!enumerateSymbols) - return; - - m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) { - for (const ccc::Function& function : database.functions) - m_mangled_function_names_to_handles.emplace(function.mangled_name(), function.handle()); - - for (const ccc::GlobalVariable& global : database.global_variables) - m_mangled_global_names_to_handles.emplace(global.mangled_name(), global.handle()); - }); - } - - virtual bool parseReference(char* str, u64& referenceIndex) - { - for (int i = 0; i < 32; i++) - { - char reg[8]; - std::snprintf(reg, std::size(reg), "r%d", i); - if (StringUtil::Strcasecmp(str, reg) == 0 || StringUtil::Strcasecmp(str, m_cpu->getRegisterName(0, i)) == 0) - { - referenceIndex = i; - return true; - } - - std::snprintf(reg, std::size(reg), "f%d", i); - if (StringUtil::Strcasecmp(str, reg) == 0) - { - referenceIndex = i | REF_INDEX_FPU; - return true; - } - } - - if (StringUtil::Strcasecmp(str, "pc") == 0) - { - referenceIndex = REF_INDEX_PC; - return true; - } - - if (StringUtil::Strcasecmp(str, "hi") == 0) - { - referenceIndex = REF_INDEX_HI; - return true; - } - - if (StringUtil::Strcasecmp(str, "lo") == 0) - { - referenceIndex = REF_INDEX_LO; - return true; - } - - if (StringUtil::Strcasecmp(str, "target") == 0) - { - referenceIndex = REF_INDEX_OPTARGET; - return true; - } - - if (StringUtil::Strcasecmp(str, "load") == 0) - { - referenceIndex = REF_INDEX_OPLOAD; - return true; - } - - if (StringUtil::Strcasecmp(str, "store") == 0) - { - referenceIndex = REF_INDEX_OPSTORE; - return true; - } - return false; - } - - virtual bool parseSymbol(char* str, u64& symbolValue) - { - bool success = false; - m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) { - std::string name = str; - - // Check for mangled function names. - auto function_iterator = m_mangled_function_names_to_handles.find(name); - if (function_iterator != m_mangled_function_names_to_handles.end()) - { - const ccc::Function* function = database.functions.symbol_from_handle(function_iterator->second); - if (function && function->address().valid()) - { - symbolValue = function->address().value; - success = true; - return; - } - } - - // Check for mangled global variable names. - auto global_iterator = m_mangled_global_names_to_handles.find(name); - if (global_iterator != m_mangled_global_names_to_handles.end()) - { - const ccc::GlobalVariable* global = database.global_variables.symbol_from_handle(global_iterator->second); - if (global && global->address().valid()) - { - symbolValue = global->address().value; - success = true; - return; - } - } - - // Check for regular unmangled names. - const ccc::Symbol* symbol = database.symbol_with_name(name); - if (symbol && symbol->address().valid()) - { - symbolValue = symbol->address().value; - success = true; - return; - } - }); - - return success; - } - - virtual u64 getReferenceValue(u64 referenceIndex) - { - if (referenceIndex < 32) - return m_cpu->getRegister(0, referenceIndex)._u64[0]; - if (referenceIndex == REF_INDEX_PC) - return m_cpu->getPC(); - if (referenceIndex == REF_INDEX_HI) - return m_cpu->getHI()._u64[0]; - if (referenceIndex == REF_INDEX_LO) - return m_cpu->getLO()._u64[0]; - if (referenceIndex & REF_INDEX_IS_OPSL) - { - const u32 OP = m_cpu->read32(m_cpu->getPC()); - const R5900::OPCODE& opcode = R5900::GetInstruction(OP); - if (opcode.flags & IS_MEMORY) - { - // 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(OP); - - if (referenceIndex & REF_INDEX_OPTARGET) - { - return target; - } - else if (referenceIndex & REF_INDEX_OPLOAD) - { - return (opcode.flags & IS_LOAD) ? target : 0; - } - else if (referenceIndex & REF_INDEX_OPSTORE) - { - return (opcode.flags & IS_STORE) ? target : 0; - } - } - return 0; - } - if (referenceIndex & REF_INDEX_FPU) - { - return m_cpu->getRegister(EECAT_FPR, referenceIndex & 0x1F)._u64[0]; - } - return -1; - } - - virtual ExpressionType getReferenceType(u64 referenceIndex) - { - if (referenceIndex & REF_INDEX_IS_FLOAT) - { - return EXPR_TYPE_FLOAT; - } - return EXPR_TYPE_UINT; - } - - virtual bool getMemoryValue(u32 address, int size, u64& dest, std::string& error) - { - switch (size) - { - case 1: - case 2: - case 4: - case 8: - break; - default: - error = StringUtil::StdStringFromFormat( - TRANSLATE("ExpressionParser", "Invalid memory access size %d."), size); - return false; - } - - if (address % size) - { - error = TRANSLATE("ExpressionParser", "Invalid memory access (unaligned)."); - return false; - } - - switch (size) - { - case 1: - dest = m_cpu->read8(address); - break; - case 2: - dest = m_cpu->read16(address); - break; - case 4: - dest = m_cpu->read32(address); - break; - case 8: - dest = m_cpu->read64(address); - break; - } - - return true; - } - -protected: - DebugInterface* m_cpu; - std::map m_mangled_function_names_to_handles; - std::map m_mangled_global_names_to_handles; }; // @@ -1306,3 +1085,217 @@ u64 ElfMemoryReader::read64(u32 address, bool& valid) return *result; } + +// +// MipsExpressionFunctions +// + +MipsExpressionFunctions::MipsExpressionFunctions(DebugInterface* cpu, bool enumerateSymbols) + : m_cpu(cpu) +{ + if (!enumerateSymbols) + return; + + m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) { + for (const ccc::Function& function : database.functions) + m_mangled_function_names_to_handles.emplace(function.mangled_name(), function.handle()); + + for (const ccc::GlobalVariable& global : database.global_variables) + m_mangled_global_names_to_handles.emplace(global.mangled_name(), global.handle()); + }); +} + +bool MipsExpressionFunctions::parseReference(char* str, u64& referenceIndex) +{ + for (int i = 0; i < 32; i++) + { + char reg[8]; + std::snprintf(reg, std::size(reg), "r%d", i); + if (StringUtil::Strcasecmp(str, reg) == 0 || StringUtil::Strcasecmp(str, m_cpu->getRegisterName(0, i)) == 0) + { + referenceIndex = i; + return true; + } + + std::snprintf(reg, std::size(reg), "f%d", i); + if (StringUtil::Strcasecmp(str, reg) == 0) + { + referenceIndex = i | REF_INDEX_FPU; + return true; + } + } + + if (StringUtil::Strcasecmp(str, "pc") == 0) + { + referenceIndex = REF_INDEX_PC; + return true; + } + + if (StringUtil::Strcasecmp(str, "hi") == 0) + { + referenceIndex = REF_INDEX_HI; + return true; + } + + if (StringUtil::Strcasecmp(str, "lo") == 0) + { + referenceIndex = REF_INDEX_LO; + return true; + } + + if (StringUtil::Strcasecmp(str, "target") == 0) + { + referenceIndex = REF_INDEX_OPTARGET; + return true; + } + + if (StringUtil::Strcasecmp(str, "load") == 0) + { + referenceIndex = REF_INDEX_OPLOAD; + return true; + } + + if (StringUtil::Strcasecmp(str, "store") == 0) + { + referenceIndex = REF_INDEX_OPSTORE; + return true; + } + return false; +} + +bool MipsExpressionFunctions::parseSymbol(char* str, u64& symbolValue) +{ + bool success = false; + m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) { + std::string name = str; + + // Check for mangled function names. + auto function_iterator = m_mangled_function_names_to_handles.find(name); + if (function_iterator != m_mangled_function_names_to_handles.end()) + { + const ccc::Function* function = database.functions.symbol_from_handle(function_iterator->second); + if (function && function->address().valid()) + { + symbolValue = function->address().value; + success = true; + return; + } + } + + // Check for mangled global variable names. + auto global_iterator = m_mangled_global_names_to_handles.find(name); + if (global_iterator != m_mangled_global_names_to_handles.end()) + { + const ccc::GlobalVariable* global = database.global_variables.symbol_from_handle(global_iterator->second); + if (global && global->address().valid()) + { + symbolValue = global->address().value; + success = true; + return; + } + } + + // Check for regular unmangled names. + const ccc::Symbol* symbol = database.symbol_with_name(name); + if (symbol && symbol->address().valid()) + { + symbolValue = symbol->address().value; + success = true; + return; + } + }); + + return success; +} + +u64 MipsExpressionFunctions::getReferenceValue(u64 referenceIndex) +{ + if (referenceIndex < 32) + return m_cpu->getRegister(0, referenceIndex)._u64[0]; + if (referenceIndex == REF_INDEX_PC) + return m_cpu->getPC(); + if (referenceIndex == REF_INDEX_HI) + return m_cpu->getHI()._u64[0]; + if (referenceIndex == REF_INDEX_LO) + return m_cpu->getLO()._u64[0]; + if (referenceIndex & REF_INDEX_IS_OPSL) + { + const u32 OP = m_cpu->read32(m_cpu->getPC()); + const R5900::OPCODE& opcode = R5900::GetInstruction(OP); + if (opcode.flags & IS_MEMORY) + { + // 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(OP); + + if (referenceIndex & REF_INDEX_OPTARGET) + { + return target; + } + else if (referenceIndex & REF_INDEX_OPLOAD) + { + return (opcode.flags & IS_LOAD) ? target : 0; + } + else if (referenceIndex & REF_INDEX_OPSTORE) + { + return (opcode.flags & IS_STORE) ? target : 0; + } + } + return 0; + } + if (referenceIndex & REF_INDEX_FPU) + { + return m_cpu->getRegister(EECAT_FPR, referenceIndex & 0x1F)._u64[0]; + } + return -1; +} + +ExpressionType MipsExpressionFunctions::getReferenceType(u64 referenceIndex) +{ + if (referenceIndex & REF_INDEX_IS_FLOAT) + { + return EXPR_TYPE_FLOAT; + } + return EXPR_TYPE_UINT; +} + +bool MipsExpressionFunctions::getMemoryValue(u32 address, int size, u64& dest, std::string& error) +{ + switch (size) + { + case 1: + case 2: + case 4: + case 8: + break; + default: + error = StringUtil::StdStringFromFormat( + TRANSLATE("ExpressionParser", "Invalid memory access size %d."), size); + return false; + } + + if (address % size) + { + error = TRANSLATE("ExpressionParser", "Invalid memory access (unaligned)."); + return false; + } + + switch (size) + { + case 1: + dest = m_cpu->read8(address); + break; + case 2: + dest = m_cpu->read16(address); + break; + case 4: + dest = m_cpu->read32(address); + break; + case 8: + dest = m_cpu->read64(address); + break; + } + + return true; +} diff --git a/pcsx2/DebugTools/DebugInterface.h b/pcsx2/DebugTools/DebugInterface.h index 72111fe0bae25d..adecebc633be91 100644 --- a/pcsx2/DebugTools/DebugInterface.h +++ b/pcsx2/DebugTools/DebugInterface.h @@ -2,8 +2,8 @@ // SPDX-License-Identifier: GPL-3.0+ #pragma once -#include "DebugTools/BiosDebugData.h" -#include "MemoryTypes.h" + +#include "BiosDebugData.h" #include "ExpressionParser.h" #include "SymbolGuardian.h" #include "SymbolImporter.h" @@ -210,5 +210,22 @@ class ElfMemoryReader : public MemoryReader const ccc::ElfFile& m_elf; }; +class MipsExpressionFunctions : public IExpressionFunctions +{ +public: + MipsExpressionFunctions(DebugInterface* cpu, bool enumerateSymbols); + + bool parseReference(char* str, u64& referenceIndex) override; + bool parseSymbol(char* str, u64& symbolValue) override; + u64 getReferenceValue(u64 referenceIndex) override; + ExpressionType getReferenceType(u64 referenceIndex) override; + bool getMemoryValue(u32 address, int size, u64& dest, std::string& error) override; + +protected: + DebugInterface* m_cpu; + std::map m_mangled_function_names_to_handles; + std::map m_mangled_global_names_to_handles; +}; + extern R5900DebugInterface r5900Debug; extern R3000DebugInterface r3000Debug; diff --git a/pcsx2/DebugTools/SymbolImporter.cpp b/pcsx2/DebugTools/SymbolImporter.cpp index 4f9388c6d34f9b..eeeed7e527991b 100644 --- a/pcsx2/DebugTools/SymbolImporter.cpp +++ b/pcsx2/DebugTools/SymbolImporter.cpp @@ -276,13 +276,6 @@ void SymbolImporter::ImportSymbols( const std::map& builtin_types, const std::atomic_bool* interrupt) { - ccc::DemanglerFunctions demangler; - if (options.DemangleSymbols) - { - demangler.cplus_demangle = cplus_demangle; - demangler.cplus_demangle_opname = cplus_demangle_opname; - } - u32 importer_flags = ccc::NO_MEMBER_FUNCTIONS | ccc::NO_OPTIMIZED_OUT_FUNCTIONS | @@ -291,6 +284,13 @@ void SymbolImporter::ImportSymbols( if (options.DemangleParameters) importer_flags |= ccc::DEMANGLE_PARAMETERS; + ccc::DemanglerFunctions demangler; + if (options.DemangleSymbols) + { + demangler.cplus_demangle = cplus_demangle; + demangler.cplus_demangle_opname = cplus_demangle_opname; + } + if (options.ImportSymbolsFromELF) { ccc::Result>> symbol_tables = elf.get_all_symbol_tables(); @@ -301,7 +301,7 @@ void SymbolImporter::ImportSymbols( else { ccc::Result module_handle = ccc::import_symbol_tables( - database, elf.name(), *symbol_tables, importer_flags, demangler, interrupt); + database, *symbol_tables, elf.name(), ccc::Address(), importer_flags, demangler, interrupt); if (!module_handle.success()) { ccc::report_error(module_handle.error()); @@ -311,7 +311,7 @@ void SymbolImporter::ImportSymbols( if (!nocash_path.empty() && options.ImportSymFileFromDefaultLocation) { - ccc::Result nocash_result = ImportNocashSymbols(database, nocash_path, builtin_types); + ccc::Result nocash_result = ImportNocashSymbols(database, nocash_path, 0, builtin_types); if (!nocash_result.success()) { Console.Error("Failed to import symbol file '%s': %s", @@ -319,14 +319,52 @@ void SymbolImporter::ImportSymbols( } } + ImportExtraSymbols(database, options, builtin_types, importer_flags, demangler, interrupt); + + Console.WriteLn("Imported %d symbols.", database.symbol_count()); + + return; +} + +void SymbolImporter::ImportExtraSymbols( + ccc::SymbolDatabase& database, + const Pcsx2Config::DebugAnalysisOptions& options, + const std::map& builtin_types, + u32 importer_flags, + const ccc::DemanglerFunctions& demangler, + const std::atomic_bool* interrupt) +{ + MipsExpressionFunctions expression_functions(&r5900Debug, true); + for (const DebugExtraSymbolFile& extra_symbol_file : options.ExtraSymbolFiles) { if (*interrupt) return; - if (StringUtil::EndsWithNoCase(extra_symbol_file.Path, ".sym")) + std::string path = Path::ToNativePath(extra_symbol_file.Path); + if (!Path::IsAbsolute(path)) + path = Path::Combine(EmuFolders::GameSettings, path); + + if (!extra_symbol_file.Condition.empty()) { - ccc::Result nocash_result = ImportNocashSymbols(database, extra_symbol_file.Path, builtin_types); + u64 expression_result = 0; + std::string error; + if (!parseExpression(extra_symbol_file.Condition.c_str(), &expression_functions, expression_result, error)) + { + Console.Error("Failed to parse condition expression '%s' while importing extra symbol file '%s': %s.", + extra_symbol_file.Condition.c_str(), path.c_str(), error.c_str()); + } + + if (!expression_result) + continue; + } + + ccc::Address base_address(extra_symbol_file.BaseAddress); + + if (StringUtil::EndsWithNoCase(path, ".sym")) + { + ccc::Result nocash_result = ImportNocashSymbols( + database, path, base_address.get_or_zero(), builtin_types); if (!nocash_result.success()) { Console.Error("Failed to import symbol file '%s': %s", @@ -334,29 +372,30 @@ void SymbolImporter::ImportSymbols( } if (!*nocash_result) - Console.Error("Cannot open symbol file '%s'.", extra_symbol_file.Path.c_str()); + Console.Error("Cannot open symbol file '%s'.", path.c_str()); continue; } - Error error; - std::optional> image = FileSystem::ReadBinaryFile(extra_symbol_file.Path.c_str()); + std::optional> image = FileSystem::ReadBinaryFile(path.c_str()); if (!image.has_value()) { - Console.Error("Failed to read extra symbol file '%s'.", extra_symbol_file.Path.c_str()); + Console.Error("Failed to read extra symbol file '%s'.", path.c_str()); continue; } - std::string file_name(Path::GetFileName(extra_symbol_file.Path)); + std::string file_name(Path::GetFileName(path)); - ccc::Result> symbol_file = ccc::parse_symbol_file(std::move(*image), file_name.c_str()); + ccc::Result> symbol_file = ccc::parse_symbol_file( + std::move(*image), file_name.c_str()); if (!symbol_file.success()) { ccc::report_error(symbol_file.error()); continue; } - ccc::Result>> symbol_tables = (*symbol_file)->get_all_symbol_tables(); + ccc::Result>> symbol_tables = + (*symbol_file)->get_all_symbol_tables(); if (!symbol_tables.success()) { ccc::report_error(symbol_tables.error()); @@ -364,22 +403,19 @@ void SymbolImporter::ImportSymbols( } ccc::Result module_handle = ccc::import_symbol_tables( - database, elf.name(), *symbol_tables, importer_flags, demangler, interrupt); + database, *symbol_tables, (*symbol_file)->name(), base_address, importer_flags, demangler, interrupt); if (!module_handle.success()) { ccc::report_error(module_handle.error()); continue; } } - - Console.WriteLn("Imported %d symbols.", database.symbol_count()); - - return; } ccc::Result SymbolImporter::ImportNocashSymbols( ccc::SymbolDatabase& database, const std::string& file_path, + u32 base_address, const std::map& builtin_types) { auto file = FileSystem::OpenManagedCFile(file_path.c_str(), "r"); @@ -405,6 +441,8 @@ ccc::Result SymbolImporter::ImportNocashSymbols( if (address == 0 && strcmp(value, "0") == 0) continue; + address += base_address; + if (value[0] == '.') { // data directives @@ -502,8 +540,8 @@ void SymbolImporter::ScanForFunctions( u32 end_address = 0; if (options.CustomFunctionScanRange) { - start_address = static_cast(std::stoull(options.FunctionScanStartAddress.c_str(), nullptr, 16)); - end_address = static_cast(std::stoull(options.FunctionScanEndAddress.c_str(), nullptr, 16)); + start_address = options.FunctionScanStartAddress; + end_address = options.FunctionScanEndAddress; } else { diff --git a/pcsx2/DebugTools/SymbolImporter.h b/pcsx2/DebugTools/SymbolImporter.h index ff210cb07b1e7c..ad0496d3d09f83 100644 --- a/pcsx2/DebugTools/SymbolImporter.h +++ b/pcsx2/DebugTools/SymbolImporter.h @@ -46,9 +46,18 @@ class SymbolImporter const std::map& builtin_types, const std::atomic_bool* interrupt); + static void ImportExtraSymbols( + ccc::SymbolDatabase& database, + const Pcsx2Config::DebugAnalysisOptions& options, + const std::map& builtin_types, + u32 importer_flags, + const ccc::DemanglerFunctions& demangler, + const std::atomic_bool* interrupt); + static ccc::Result ImportNocashSymbols( ccc::SymbolDatabase& database, const std::string& file_path, + u32 base_address, const std::map& builtin_types); static std::unique_ptr GetBuiltInType( diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 071ff140ebae29..5f51c1d4fcc327 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -1625,6 +1625,9 @@ void Pcsx2Config::DebugAnalysisOptions::LoadSave(SettingsWrapper& wrap) file = ExtraSymbolFiles[i]; SettingsWrapEntryEx(file.Path, "Path"); + if (!wrap.IsSaving() || file.BaseAddress != (u32)-1) + SettingsWrapEntryEx(file.BaseAddress, "BaseAddress"); + SettingsWrapEntryEx(file.Condition, "Condition"); if (wrap.IsLoading()) ExtraSymbolFiles.emplace_back(std::move(file));