diff --git a/3rdparty/ccc/src/ccc/elf.cpp b/3rdparty/ccc/src/ccc/elf.cpp index 8c3ed99e57097..5bb11dd72c362 100644 --- a/3rdparty/ccc/src/ccc/elf.cpp +++ b/3rdparty/ccc/src/ccc/elf.cpp @@ -93,7 +93,7 @@ const ElfProgramHeader* ElfFile::entry_point_segment() const return entry_segment; } -Result> ElfFile::get_virtual(u32 address, u32 size) const +std::optional> ElfFile::get_virtual(u32 address, u32 size) const { u32 end_address = address + size; @@ -109,17 +109,19 @@ Result> ElfFile::get_virtual(u32 address, u32 size) const } } - return CCC_FAILURE("No ELF segment for address range 0x%x to 0x%x.", address, end_address); + return std::nullopt; } -Result ElfFile::copy_virtual(u8* dest, u32 address, u32 size) const +bool ElfFile::copy_virtual(u8* dest, u32 address, u32 size) const { - Result> block = get_virtual(address, size); - CCC_RETURN_IF_ERROR(block); + std::optional> block = get_virtual(address, size); + if(!block.has_value()) { + return false; + } memcpy(dest, block->data(), size); - return Result(); + return true; } } diff --git a/3rdparty/ccc/src/ccc/elf.h b/3rdparty/ccc/src/ccc/elf.h index 33d89e759cbd9..35f6203298732 100644 --- a/3rdparty/ccc/src/ccc/elf.h +++ b/3rdparty/ccc/src/ccc/elf.h @@ -125,18 +125,20 @@ struct ElfFile { const ElfProgramHeader* entry_point_segment() const; // Retrieve a block of data in an ELF file given its address and size. - Result> get_virtual(u32 address, u32 size) const; + std::optional> get_virtual(u32 address, u32 size) const; // Copy a block of data in an ELF file to the destination buffer given its // address and size. - Result copy_virtual(u8* dest, u32 address, u32 size) const; + bool copy_virtual(u8* dest, u32 address, u32 size) const; // Retrieve an object of type T from an ELF file given its address. template - Result get_object_virtual(u32 address) const + std::optional get_object_virtual(u32 address) const { - Result> result = get_virtual(address, sizeof(T)); - CCC_RETURN_IF_ERROR(result); + std::optional> result = get_virtual(address, sizeof(T)); + if(!result.has_value()) { + return std::nullopt; + } return *(T*) result->data(); } @@ -144,10 +146,12 @@ struct ElfFile { // Retrieve an array of objects of type T from an ELF file given its // address and element count. template - Result> get_array_virtual(u32 address, u32 element_count) const + std::optional> get_array_virtual(u32 address, u32 element_count) const { - Result> result = get_virtual(address, element_count * sizeof(T)); - CCC_RETURN_IF_ERROR(result); + std::optional> result = get_virtual(address, element_count * sizeof(T)); + if(!result.has_value()) { + return std::nullopt; + } return std::span((T*) result->data(), (T*) (result->data() + result->size())); } diff --git a/3rdparty/ccc/src/ccc/symbol_database.h b/3rdparty/ccc/src/ccc/symbol_database.h index 52c6f1ece489e..2356e56023c12 100644 --- a/3rdparty/ccc/src/ccc/symbol_database.h +++ b/3rdparty/ccc/src/ccc/symbol_database.h @@ -435,7 +435,7 @@ class Label : public Symbol { public: static constexpr const SymbolDescriptor DESCRIPTOR = LABEL; static constexpr const char* NAME = "Label"; - static constexpr u32 FLAGS = WITH_ADDRESS_MAP; + static constexpr u32 FLAGS = WITH_ADDRESS_MAP | WITH_NAME_MAP; LabelHandle handle() const { return m_handle; } diff --git a/3rdparty/ccc/src/ccc/symbol_table.cpp b/3rdparty/ccc/src/ccc/symbol_table.cpp index d8b6db6428be7..863e23633f7a3 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 72f5ccde1196f..84c8460053931 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/Debugger/BreakpointDialog.cpp b/pcsx2-qt/Debugger/BreakpointDialog.cpp index 1d5c5110944b9..b38b9f38a0b6a 100644 --- a/pcsx2-qt/Debugger/BreakpointDialog.cpp +++ b/pcsx2-qt/Debugger/BreakpointDialog.cpp @@ -80,6 +80,8 @@ void BreakpointDialog::onRdoButtonToggled() void BreakpointDialog::accept() { + std::string error; + if (m_purpose == PURPOSE::CREATE) { if (m_ui.rdoExecute->isChecked()) @@ -93,9 +95,9 @@ void BreakpointDialog::accept() PostfixExpression expr; u64 address; - if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), address)) + if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), address, error)) { - QMessageBox::warning(this, tr("Invalid Address"), getExpressionError()); + QMessageBox::warning(this, tr("Invalid Address"), QString::fromStdString(error)); return; } @@ -108,9 +110,9 @@ void BreakpointDialog::accept() bp->hasCond = true; bp->cond.debug = m_cpu; - if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr)) + if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr, error)) { - QMessageBox::warning(this, tr("Invalid Condition"), getExpressionError()); + QMessageBox::warning(this, tr("Invalid Condition"), QString::fromStdString(error)); return; } @@ -121,16 +123,16 @@ void BreakpointDialog::accept() if (auto* mc = std::get_if(&m_bp_mc)) { u64 startAddress; - if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), startAddress)) + if (!m_cpu->evaluateExpression(m_ui.txtAddress->text().toStdString().c_str(), startAddress, error)) { - QMessageBox::warning(this, tr("Invalid Address"), getExpressionError()); + QMessageBox::warning(this, tr("Invalid Address"), QString::fromStdString(error)); return; } u64 size; - if (!m_cpu->evaluateExpression(m_ui.txtSize->text().toStdString().c_str(), size) || !size) + if (!m_cpu->evaluateExpression(m_ui.txtSize->text().toStdString().c_str(), size, error) || !size) { - QMessageBox::warning(this, tr("Invalid Size"), getExpressionError()); + QMessageBox::warning(this, tr("Invalid Size"), QString::fromStdString(error)); return; } @@ -143,9 +145,9 @@ void BreakpointDialog::accept() mc->cond.debug = m_cpu; PostfixExpression expr; - if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr)) + if (!m_cpu->initExpression(m_ui.txtCondition->text().toStdString().c_str(), expr, error)) { - QMessageBox::warning(this, tr("Invalid Condition"), getExpressionError()); + QMessageBox::warning(this, tr("Invalid Condition"), QString::fromStdString(error)); return; } diff --git a/pcsx2-qt/Debugger/DisassemblyWidget.cpp b/pcsx2-qt/Debugger/DisassemblyWidget.cpp index 4f75ad08a0f5e..887b8185ff0ce 100644 --- a/pcsx2-qt/Debugger/DisassemblyWidget.cpp +++ b/pcsx2-qt/Debugger/DisassemblyWidget.cpp @@ -170,9 +170,10 @@ void DisassemblyWidget::contextGoToAddress() return; u64 address = 0; - if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address)) + std::string error; + if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address, error)) { - QMessageBox::warning(this, tr("Cannot Go To"), getExpressionError()); + QMessageBox::warning(this, tr("Cannot Go To"), QString::fromStdString(error)); return; } diff --git a/pcsx2-qt/Debugger/MemoryViewWidget.cpp b/pcsx2-qt/Debugger/MemoryViewWidget.cpp index f635dbc6324ec..5fa56ff741f3e 100644 --- a/pcsx2-qt/Debugger/MemoryViewWidget.cpp +++ b/pcsx2-qt/Debugger/MemoryViewWidget.cpp @@ -599,9 +599,10 @@ void MemoryViewWidget::contextGoToAddress() return; u64 address = 0; - if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address)) + std::string error; + if (!m_cpu->evaluateExpression(targetString.toStdString().c_str(), address, error)) { - QMessageBox::warning(this, tr("Cannot Go To"), getExpressionError()); + QMessageBox::warning(this, tr("Cannot Go To"), QString::fromStdString(error)); return; } diff --git a/pcsx2-qt/Debugger/Models/BreakpointModel.cpp b/pcsx2-qt/Debugger/Models/BreakpointModel.cpp index 07bb9bb898a89..fc4daa293ab38 100644 --- a/pcsx2-qt/Debugger/Models/BreakpointModel.cpp +++ b/pcsx2-qt/Debugger/Models/BreakpointModel.cpp @@ -273,6 +273,8 @@ Qt::ItemFlags BreakpointModel::flags(const QModelIndex& index) const bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, int role) { + std::string error; + if (role == Qt::CheckStateRole && index.column() == BreakpointColumns::ENABLED) { auto bp_mc = m_breakpoints.at(index.row()); @@ -314,9 +316,9 @@ bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, i { PostfixExpression expr; - if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr)) + if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr, error)) { - QMessageBox::warning(nullptr, "Condition Error", QString(getExpressionError())); + QMessageBox::warning(nullptr, "Condition Error", QString::fromStdString(error)); return false; } @@ -347,9 +349,9 @@ bool BreakpointModel::setData(const QModelIndex& index, const QVariant& value, i { PostfixExpression expr; - if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr)) + if (!m_cpu.initExpression(condValue.toLocal8Bit().constData(), expr, error)) { - QMessageBox::warning(nullptr, "Condition Error", QString(getExpressionError())); + QMessageBox::warning(nullptr, "Condition Error", QString::fromStdString(error)); return false; } @@ -456,17 +458,20 @@ void BreakpointModel::refreshData() void BreakpointModel::loadBreakpointFromFieldList(QStringList fields) { + std::string error; + bool ok; - if (fields.size() != BreakpointModel::BreakpointColumns::COLUMN_COUNT) + if (fields.size() != BreakpointColumns::COLUMN_COUNT) { Console.WriteLn("Debugger Breakpoint Model: Invalid number of columns, skipping"); return; } - const int type = fields[BreakpointModel::BreakpointColumns::TYPE].toUInt(&ok); + const int type = fields[BreakpointColumns::TYPE].toUInt(&ok); if (!ok) { - Console.WriteLn("Debugger Breakpoint Model: Failed to parse type '%s', skipping", fields[BreakpointModel::BreakpointColumns::TYPE].toUtf8().constData()); + Console.WriteLn("Debugger Breakpoint Model: Failed to parse type '%s', skipping", + fields[BreakpointColumns::TYPE].toUtf8().constData()); return; } @@ -476,34 +481,37 @@ void BreakpointModel::loadBreakpointFromFieldList(QStringList fields) BreakPoint bp; // Address - bp.addr = fields[BreakpointModel::BreakpointColumns::OFFSET].toUInt(&ok, 16); + bp.addr = fields[BreakpointColumns::OFFSET].toUInt(&ok, 16); if (!ok) { - Console.WriteLn("Debugger Breakpoint Model: Failed to parse address '%s', skipping", fields[BreakpointModel::BreakpointColumns::OFFSET].toUtf8().constData()); + Console.WriteLn("Debugger Breakpoint Model: Failed to parse address '%s', skipping", + fields[BreakpointColumns::OFFSET].toUtf8().constData()); return; } // Condition - if (!fields[BreakpointModel::BreakpointColumns::CONDITION].isEmpty()) + if (!fields[BreakpointColumns::CONDITION].isEmpty()) { PostfixExpression expr; bp.hasCond = true; bp.cond.debug = &m_cpu; - if (!m_cpu.initExpression(fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData(), expr)) + if (!m_cpu.initExpression(fields[BreakpointColumns::CONDITION].toUtf8().constData(), expr, error)) { - Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond '%s', skipping", fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData()); + Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond '%s', skipping", + fields[BreakpointModel::CONDITION].toUtf8().constData()); return; } bp.cond.expression = expr; - bp.cond.expressionString = fields[BreakpointModel::BreakpointColumns::CONDITION].toStdString(); + bp.cond.expressionString = fields[BreakpointColumns::CONDITION].toStdString(); } // Enabled - bp.enabled = fields[BreakpointModel::BreakpointColumns::ENABLED].toUInt(&ok); + bp.enabled = fields[BreakpointColumns::ENABLED].toUInt(&ok); if (!ok) { - Console.WriteLn("Debugger Breakpoint Model: Failed to parse enable flag '%s', skipping", fields[BreakpointModel::BreakpointColumns::ENABLED].toUtf8().constData()); + Console.WriteLn("Debugger Breakpoint Model: Failed to parse enable flag '%s', skipping", + fields[BreakpointColumns::ENABLED].toUtf8().constData()); return; } @@ -515,49 +523,54 @@ void BreakpointModel::loadBreakpointFromFieldList(QStringList fields) // Mode if (type >= MEMCHECK_INVALID) { - Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond type '%s', skipping", fields[BreakpointModel::BreakpointColumns::TYPE].toUtf8().constData()); + Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond type '%s', skipping", + fields[BreakpointColumns::TYPE].toUtf8().constData()); return; } mc.memCond = static_cast(type); // Address - QString test = fields[BreakpointModel::BreakpointColumns::OFFSET]; - mc.start = fields[BreakpointModel::BreakpointColumns::OFFSET].toUInt(&ok, 16); + QString test = fields[BreakpointColumns::OFFSET]; + mc.start = fields[BreakpointColumns::OFFSET].toUInt(&ok, 16); if (!ok) { - Console.WriteLn("Debugger Breakpoint Model: Failed to parse address '%s', skipping", fields[BreakpointModel::BreakpointColumns::OFFSET].toUtf8().constData()); + Console.WriteLn("Debugger Breakpoint Model: Failed to parse address '%s', skipping", + fields[BreakpointColumns::OFFSET].toUtf8().constData()); return; } // Size - mc.end = fields[BreakpointModel::BreakpointColumns::SIZE_LABEL].toUInt(&ok) + mc.start; + mc.end = fields[BreakpointColumns::SIZE_LABEL].toUInt(&ok) + mc.start; if (!ok) { - Console.WriteLn("Debugger Breakpoint Model: Failed to parse length '%s', skipping", fields[BreakpointModel::BreakpointColumns::SIZE_LABEL].toUtf8().constData()); + Console.WriteLn("Debugger Breakpoint Model: Failed to parse length '%s', skipping", + fields[BreakpointColumns::SIZE_LABEL].toUtf8().constData()); return; } // Condition - if (!fields[BreakpointModel::BreakpointColumns::CONDITION].isEmpty()) + if (!fields[BreakpointColumns::CONDITION].isEmpty()) { PostfixExpression expr; mc.hasCond = true; mc.cond.debug = &m_cpu; - if (!m_cpu.initExpression(fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData(), expr)) + if (!m_cpu.initExpression(fields[BreakpointColumns::CONDITION].toUtf8().constData(), expr, error)) { - Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond '%s', skipping", fields[BreakpointModel::BreakpointColumns::CONDITION].toUtf8().constData()); + Console.WriteLn("Debugger Breakpoint Model: Failed to parse cond '%s', skipping", + fields[BreakpointColumns::CONDITION].toUtf8().constData()); return; } mc.cond.expression = expr; - mc.cond.expressionString = fields[BreakpointModel::BreakpointColumns::CONDITION].toStdString(); + mc.cond.expressionString = fields[BreakpointColumns::CONDITION].toStdString(); } // Result - const int result = fields[BreakpointModel::BreakpointColumns::ENABLED].toUInt(&ok); + const int result = fields[BreakpointColumns::ENABLED].toUInt(&ok); if (!ok) { - Console.WriteLn("Debugger Breakpoint Model: Failed to parse result flag '%s', skipping", fields[BreakpointModel::BreakpointColumns::ENABLED].toUtf8().constData()); + Console.WriteLn("Debugger Breakpoint Model: Failed to parse result flag '%s', skipping", + fields[BreakpointColumns::ENABLED].toUtf8().constData()); return; } mc.result = static_cast(result); diff --git a/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.cpp b/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.cpp index a0fe6cc038188..b0856068cab92 100644 --- a/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.cpp +++ b/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.cpp @@ -37,8 +37,8 @@ DebugAnalysisSettingsWidget::DebugAnalysisSettingsWidget(QWidget* parent) } m_ui.customAddressRange->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "CustomFunctionScanRange", false)); - m_ui.addressRangeStart->setText(QString::fromStdString(Host::GetStringSettingValue("Debugger/Analysis", "FunctionScanStartAddress", "0"))); - m_ui.addressRangeEnd->setText(QString::fromStdString(Host::GetStringSettingValue("Debugger/Analysis", "FunctionScanEndAddress", "0"))); + m_ui.addressRangeStart->setText(QString::fromStdString(Host::GetStringSettingValue("Debugger/Analysis", "FunctionScanStartAddress", ""))); + m_ui.addressRangeEnd->setText(QString::fromStdString(Host::GetStringSettingValue("Debugger/Analysis", "FunctionScanEndAddress", ""))); m_ui.grayOutOverwrittenFunctions->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "GenerateFunctionHashes", true)); @@ -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,18 +165,19 @@ 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(); + file.BaseAddress = m_symbol_file_model->item(i, BASE_ADDRESS_COLUMN)->text().toStdString(); + 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(); - - output.GenerateFunctionHashes = m_ui.grayOutOverwrittenFunctions->isChecked(); } void DebugAnalysisSettingsWidget::setupSymbolSourceGrid() @@ -187,27 +188,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 +306,100 @@ 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(); + base_address_item->setText(QString::fromStdString(getStringSettingValue(section.c_str(), "BaseAddress"))); + 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; - m_ui.symbolFileList->addItem(path); + 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; + + 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; + QItemSelectionModel* selection_model = m_ui.symbolFileTable->selectionModel(); + if (!selection_model) + return; + + while (!selection_model->selectedIndexes().isEmpty()) + { + QModelIndex index = selection_model->selectedIndexes().first(); + m_symbol_file_model->removeRow(index.row(), index.parent()); + } saveSymbolFiles(); + updateEnabledStates(); } void DebugAnalysisSettingsWidget::saveSymbolFiles() @@ -380,17 +421,24 @@ 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)) + sif->SetStringValue(section.c_str(), "BaseAddress", base_address_item->text().toStdString().c_str()); + + 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 +471,34 @@ 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); +} diff --git a/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.h b/pcsx2-qt/Settings/DebugAnalysisSettingsWidget.h index 52d81c1f0570b..77192e65a3b7d 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,10 @@ 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); + struct SymbolSourceTemp { QCheckBox* check_box = nullptr; @@ -49,8 +54,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 9f6575cbba0ad..4b2759ef0b243 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/Config.h b/pcsx2/Config.h index fc64f1c22bae3..403900ff2c7ea 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -208,6 +208,8 @@ struct DebugSymbolSource struct DebugExtraSymbolFile { std::string Path; + std::string BaseAddress; + 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"; + std::string FunctionScanStartAddress; + std::string FunctionScanEndAddress; 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/Breakpoints.h b/pcsx2/DebugTools/Breakpoints.h index 361c284f45c60..58942df01201f 100644 --- a/pcsx2/DebugTools/Breakpoints.h +++ b/pcsx2/DebugTools/Breakpoints.h @@ -24,7 +24,8 @@ struct BreakPointCond u32 Evaluate() { u64 result; - if (!debug->parseExpression(expression, result) || result == 0) + std::string error; + if (!debug->parseExpression(expression, result, error) || result == 0) return 0; return 1; } diff --git a/pcsx2/DebugTools/DebugInterface.cpp b/pcsx2/DebugTools/DebugInterface.cpp index 37da99332282e..db4ac668de033 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; }; // @@ -340,7 +119,7 @@ std::optional DebugInterface::getStackFrameSize(const ccc::Function& functi // 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. + // "addiu $sp, $sp, frame_size" instead. u32 instruction = read32(function.address().value); @@ -354,29 +133,29 @@ std::optional DebugInterface::getStackFrameSize(const ccc::Function& functi return static_cast(stack_frame_size); } -bool DebugInterface::evaluateExpression(const char* expression, u64& dest) +bool DebugInterface::evaluateExpression(const char* expression, u64& dest, std::string& error) { PostfixExpression postfix; - if (!initExpression(expression, postfix)) + if (!initExpression(expression, postfix, error)) return false; - if (!parseExpression(postfix, dest)) + if (!parseExpression(postfix, dest, error)) return false; return true; } -bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest) +bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest, std::string& error) { - MipsExpressionFunctions funcs(this, true); - return initPostfixExpression(exp, &funcs, dest); + MipsExpressionFunctions funcs(this, nullptr, true); + return initPostfixExpression(exp, &funcs, dest, error); } -bool DebugInterface::parseExpression(PostfixExpression& exp, u64& dest) +bool DebugInterface::parseExpression(PostfixExpression& exp, u64& dest, std::string& error) { - MipsExpressionFunctions funcs(this, false); - return parsePostfixExpression(exp, &funcs, dest); + MipsExpressionFunctions funcs(this, nullptr, false); + return parsePostfixExpression(exp, &funcs, dest, error); } // @@ -904,12 +683,10 @@ std::vector> R5900DebugInterface::GetThreadList() co return getEEThreads(); } - // // R3000DebugInterface // - BreakPointCpu R3000DebugInterface::getCpuType() { return BREAKPOINT_IOP; @@ -1235,8 +1012,8 @@ ElfMemoryReader::ElfMemoryReader(const ccc::ElfFile& elf) u32 ElfMemoryReader::read8(u32 address) { - ccc::Result result = m_elf.get_object_virtual(address); - if (!result.success()) + std::optional result = m_elf.get_object_virtual(address); + if (!result.has_value()) return 0; return *result; @@ -1244,8 +1021,8 @@ u32 ElfMemoryReader::read8(u32 address) u32 ElfMemoryReader::read8(u32 address, bool& valid) { - ccc::Result result = m_elf.get_object_virtual(address); - valid = result.success(); + std::optional result = m_elf.get_object_virtual(address); + valid = result.has_value(); if (!valid) return 0; @@ -1254,8 +1031,8 @@ u32 ElfMemoryReader::read8(u32 address, bool& valid) u32 ElfMemoryReader::read16(u32 address) { - ccc::Result result = m_elf.get_object_virtual(address); - if (!result.success()) + std::optional result = m_elf.get_object_virtual(address); + if (!result.has_value()) return 0; return *result; @@ -1263,8 +1040,8 @@ u32 ElfMemoryReader::read16(u32 address) u32 ElfMemoryReader::read16(u32 address, bool& valid) { - ccc::Result result = m_elf.get_object_virtual(address); - valid = result.success(); + std::optional result = m_elf.get_object_virtual(address); + valid = result.has_value(); if (!valid) return 0; @@ -1273,8 +1050,8 @@ u32 ElfMemoryReader::read16(u32 address, bool& valid) u32 ElfMemoryReader::read32(u32 address) { - ccc::Result result = m_elf.get_object_virtual(address); - if (!result.success()) + std::optional result = m_elf.get_object_virtual(address); + if (!result.has_value()) return 0; return *result; @@ -1282,8 +1059,8 @@ u32 ElfMemoryReader::read32(u32 address) u32 ElfMemoryReader::read32(u32 address, bool& valid) { - ccc::Result result = m_elf.get_object_virtual(address); - valid = result.success(); + std::optional result = m_elf.get_object_virtual(address); + valid = result.has_value(); if (!valid) return 0; @@ -1292,8 +1069,8 @@ u32 ElfMemoryReader::read32(u32 address, bool& valid) u64 ElfMemoryReader::read64(u32 address) { - ccc::Result result = m_elf.get_object_virtual(address); - if (!result.success()) + std::optional result = m_elf.get_object_virtual(address); + if (!result.has_value()) return 0; return *result; @@ -1301,10 +1078,246 @@ u64 ElfMemoryReader::read64(u32 address) u64 ElfMemoryReader::read64(u32 address, bool& valid) { - ccc::Result result = m_elf.get_object_virtual(address); - valid = result.success(); + std::optional result = m_elf.get_object_virtual(address); + valid = result.has_value(); if (!valid) return 0; return *result; } + +// +// MipsExpressionFunctions +// + +MipsExpressionFunctions::MipsExpressionFunctions( + DebugInterface* cpu, const ccc::SymbolDatabase* symbolDatabase, bool shouldEnumerateSymbols) + : m_cpu(cpu) + , m_database(symbolDatabase) +{ + if (!shouldEnumerateSymbols) + return; + + if (symbolDatabase) + { + enumerateSymbols(*symbolDatabase); + } + else + { + m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) { + enumerateSymbols(database); + }); + } +} + +void MipsExpressionFunctions::enumerateSymbols(const ccc::SymbolDatabase& database) +{ + // TODO: Add mangled symbol name maps to CCC and remove this. + + 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) +{ + if (m_database) + return parseSymbol(str, symbolValue, *m_database); + + bool success = false; + m_cpu->GetSymbolGuardian().Read([&](const ccc::SymbolDatabase& database) { + success = parseSymbol(str, symbolValue, database); + }); + return success; +} + +bool MipsExpressionFunctions::parseSymbol(char* str, u64& symbolValue, 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; + return true; + } + } + + // 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; + return true; + } + } + + // Check for regular unmangled names. + const ccc::Symbol* symbol = database.symbol_with_name(name); + if (symbol && symbol->address().valid()) + { + symbolValue = symbol->address().value; + return true; + } + + return false; +} + +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 fceedb9fea1ef..2626becb5d383 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" @@ -86,9 +86,9 @@ class DebugInterface : public MemoryReader virtual SymbolImporter* GetSymbolImporter() const = 0; virtual std::vector> GetThreadList() const = 0; - bool evaluateExpression(const char* expression, u64& dest); - bool initExpression(const char* exp, PostfixExpression& dest); - bool parseExpression(PostfixExpression& exp, u64& dest); + bool evaluateExpression(const char* expression, u64& dest, std::string& error); + bool initExpression(const char* exp, PostfixExpression& dest, std::string& error); + bool parseExpression(PostfixExpression& exp, u64& dest, std::string& error); bool isAlive(); bool isCpuPaused(); void pauseCpu(); @@ -210,5 +210,26 @@ class ElfMemoryReader : public MemoryReader const ccc::ElfFile& m_elf; }; +class MipsExpressionFunctions : public IExpressionFunctions +{ +public: + MipsExpressionFunctions( + DebugInterface* cpu, const ccc::SymbolDatabase* symbolDatabase, bool shouldEnumerateSymbols); + + 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: + void enumerateSymbols(const ccc::SymbolDatabase& database); + bool parseSymbol(char* str, u64& symbolValue, const ccc::SymbolDatabase& database); + DebugInterface* m_cpu; + const ccc::SymbolDatabase* m_database; + 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/ExpressionParser.cpp b/pcsx2/DebugTools/ExpressionParser.cpp index 7fc0a8866b758..170796e5124a3 100644 --- a/pcsx2/DebugTools/ExpressionParser.cpp +++ b/pcsx2/DebugTools/ExpressionParser.cpp @@ -21,8 +21,6 @@ typedef enum { typedef enum { EXCOMM_CONST, EXCOMM_CONST_FLOAT, EXCOMM_REF, EXCOMM_OP } ExpressionCommand; -static std::string expressionError; - typedef struct { char Name[4]; unsigned char Priority; @@ -209,10 +207,8 @@ bool isAlphaNum(char c) c == '@' || c == '_' || c == '$' || c == '.'); } -bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest) +bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest, std::string& error) { - expressionError.clear(); - int infixPos = 0; int infixLen = (int)strlen(infix); ExpressionOpcodeType lastOpcode = EXOP_NONE; @@ -241,7 +237,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf if(subPos == sizeof(subStr) - 1) { - expressionError = TRANSLATE("ExpressionParser", "Token too long."); + error = TRANSLATE("ExpressionParser", "Token too long."); return false; } @@ -251,14 +247,14 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf isFloat = true; else if (!parseNumber(subStr,16,subPos,value)) { - expressionError = StringUtil::StdStringFromFormat( + error = StringUtil::StdStringFromFormat( TRANSLATE("ExpressionParser", "Invalid number \"%s\"."), subStr); return false; } dest.push_back(ExpressionPair(isFloat?EXCOMM_CONST_FLOAT:EXCOMM_CONST,value)); lastOpcode = EXOP_NUMBER; - } else if ((first >= 'a' && first <= 'z') || first == '@') + } else if ((first >= 'a' && first <= 'z') || first == '@' || first == '_') { while (isAlphaNum(infix[infixPos]) && subPos < static_cast(sizeof(subStr)) - 1) { @@ -268,7 +264,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf if(subPos == sizeof(subStr) - 1) { - expressionError = TRANSLATE("ExpressionParser", "Token too long."); + error = TRANSLATE("ExpressionParser", "Token too long."); return false; } @@ -294,7 +290,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf continue; } - expressionError = StringUtil::StdStringFromFormat( + error = StringUtil::StdStringFromFormat( TRANSLATE("ExpressionParser", "Invalid symbol \"%s\"."), subStr); return false; } else { @@ -302,7 +298,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf ExpressionOpcodeType type = getExpressionOpcode(&infix[infixPos],len,lastOpcode); if (type == EXOP_NONE) { - expressionError = StringUtil::StdStringFromFormat( + error = StringUtil::StdStringFromFormat( TRANSLATE("ExpressionParser", "Invalid operator at \"%s\"."), &infix[infixPos]); return false; } @@ -318,7 +314,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf { if (opcodeStack.empty()) { - expressionError = TRANSLATE("ExpressionParser", "Closing parenthesis without opening one."); + error = TRANSLATE("ExpressionParser", "Closing parenthesis without opening one."); return false; } ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1]; @@ -332,7 +328,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf { if (opcodeStack.empty()) { - expressionError = TRANSLATE("ExpressionParser", "Closing bracket without opening one."); + error = TRANSLATE("ExpressionParser", "Closing bracket without opening one."); return false; } ExpressionOpcodeType t = opcodeStack[opcodeStack.size()-1]; @@ -385,7 +381,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf if (t == EXOP_BRACKETL) // opening bracket without closing one { - expressionError = TRANSLATE("ExpressionParser", "Parenthesis not closed."); + error = TRANSLATE("ExpressionParser", "Parenthesis not closed."); return false; } dest.push_back(ExpressionPair(EXCOMM_OP,t)); @@ -415,7 +411,7 @@ bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, Postf return true; } -bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, u64& dest) +bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, u64& dest, std::string& error) { size_t num = 0; u64 opcode; @@ -444,7 +440,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, opcode = exp[num++].second; if (valueStack.size() < ExpressionOpcodes[opcode].args) { - expressionError = TRANSLATE("ExpressionParser", "Not enough arguments."); + error = TRANSLATE("ExpressionParser", "Not enough arguments."); return false; } for (int l = 0; l < ExpressionOpcodes[opcode].args; l++) @@ -459,12 +455,12 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, case EXOP_MEMSIZE: // must be followed by EXOP_MEM if (exp[num++].second != EXOP_MEM) { - expressionError = TRANSLATE("ExpressionParser", "Invalid memsize operator."); + error = TRANSLATE("ExpressionParser", "Invalid memsize operator."); return false; } u64 val; - if(!funcs->getMemoryValue(arg[1],arg[0],val,expressionError)) + if(!funcs->getMemoryValue(arg[1],arg[0],val,error)) { return false; } @@ -473,7 +469,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, case EXOP_MEM: { u64 val; - if (!funcs->getMemoryValue(arg[0],4,val,expressionError)) + if (!funcs->getMemoryValue(arg[0],4,val,error)) { return false; } @@ -503,7 +499,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, case EXOP_DIV: // a/b if (arg[0] == 0) { - expressionError = TRANSLATE("ExpressionParser", "Division by zero."); + error = TRANSLATE("ExpressionParser", "Division by zero."); return false; } if (useFloat) @@ -514,7 +510,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, case EXOP_MOD: // a%b if (arg[0] == 0) { - expressionError = TRANSLATE("ExpressionParser", "Modulo by zero."); + error = TRANSLATE("ExpressionParser", "Modulo by zero."); return false; } valueStack.push_back(arg[1]%arg[0]); @@ -593,7 +589,7 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, case EXOP_TERTELSE: // exp ? exp : exp, else muss zuerst kommen! if (exp[num++].second != EXOP_TERTIF) { - expressionError = TRANSLATE("ExpressionParser", "Invalid tertiary operator."); + error = TRANSLATE("ExpressionParser", "Invalid tertiary operator."); return false; } valueStack.push_back(arg[2]?arg[1]:arg[0]); @@ -608,17 +604,9 @@ bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, return true; } -bool parseExpression(char* exp, IExpressionFunctions* funcs, u64& dest) +bool parseExpression(const char* exp, IExpressionFunctions* funcs, u64& dest, std::string& error) { PostfixExpression postfix; - if (!initPostfixExpression(exp,funcs,postfix)) return false; - return parsePostfixExpression(postfix,funcs,dest); -} - -const char* getExpressionError() -{ - if (expressionError.empty()) - return TRANSLATE("ExpressionParser", "Invalid expression."); - - return expressionError.c_str(); + if (!initPostfixExpression(exp,funcs,postfix,error)) return false; + return parsePostfixExpression(postfix,funcs,dest,error); } diff --git a/pcsx2/DebugTools/ExpressionParser.h b/pcsx2/DebugTools/ExpressionParser.h index a7f637149c901..a34bc4857395a 100644 --- a/pcsx2/DebugTools/ExpressionParser.h +++ b/pcsx2/DebugTools/ExpressionParser.h @@ -26,7 +26,6 @@ class IExpressionFunctions virtual bool getMemoryValue(u32 address, int size, u64& dest, std::string& error) = 0; }; -bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest); -bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, u64& dest); -bool parseExpression(const char* exp, IExpressionFunctions* funcs, u64& dest); -const char* getExpressionError(); +bool initPostfixExpression(const char* infix, IExpressionFunctions* funcs, PostfixExpression& dest, std::string& error); +bool parsePostfixExpression(PostfixExpression& exp, IExpressionFunctions* funcs, u64& dest, std::string& error); +bool parseExpression(const char* exp, IExpressionFunctions* funcs, u64& dest, std::string& error); diff --git a/pcsx2/DebugTools/MipsAssembler.cpp b/pcsx2/DebugTools/MipsAssembler.cpp index 4edd01cdf0602..5db774bf111fe 100644 --- a/pcsx2/DebugTools/MipsAssembler.cpp +++ b/pcsx2/DebugTools/MipsAssembler.cpp @@ -355,11 +355,12 @@ bool MipsCheckImmediate(const char* Source, DebugInterface* cpu, int& dest, int& RetLen = SourceLen; PostfixExpression postfix; - if (!cpu->initExpression(Buffer,postfix)) + std::string error; + if (!cpu->initExpression(Buffer,postfix,error)) return false; u64 value; - if (!cpu->parseExpression(postfix,value)) + if (!cpu->parseExpression(postfix,value,error)) return false; dest = (int) value; diff --git a/pcsx2/DebugTools/SymbolImporter.cpp b/pcsx2/DebugTools/SymbolImporter.cpp index 4f9388c6d34f9..5d3eeb97835e1 100644 --- a/pcsx2/DebugTools/SymbolImporter.cpp +++ b/pcsx2/DebugTools/SymbolImporter.cpp @@ -212,11 +212,6 @@ void SymbolImporter::AnalyseElf( SymbolGuardian::GenerateFunctionHashes(temp_database, reader); } - if (m_interrupt_import_thread) - return; - - ScanForFunctions(temp_database, worker_symbol_file, options); - if (m_interrupt_import_thread) return; @@ -227,6 +222,14 @@ void SymbolImporter::AnalyseElf( return; database.merge_from(temp_database); + + if (m_interrupt_import_thread) + return; + + // The function scanner has to be run on the main database so that + // functions created before the importer was run are still + // considered. Otherwise, duplicate functions will be created. + ScanForFunctions(database, worker_symbol_file, options); }); }); } @@ -276,13 +279,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 +287,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 +304,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 +314,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 +322,64 @@ 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, &database, 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()) + { + u64 expression_result = 0; + std::string error; + if (!parseExpression(extra_symbol_file.Condition.c_str(), &expression_functions, expression_result, error)) + { + Console.Error("Failed to evaluate 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; + if (!extra_symbol_file.BaseAddress.empty()) + { + u64 expression_result = 0; + std::string error; + if (!parseExpression(extra_symbol_file.BaseAddress.c_str(), &expression_functions, expression_result, error)) + { + Console.Error("Failed to evaluate base address expression '%s' while importing extra symbol file '%s': %s", + extra_symbol_file.BaseAddress.c_str(), path.c_str(), error.c_str()); + } + + base_address = static_cast(expression_result); + } + + if (StringUtil::EndsWithNoCase(path, ".sym")) { - ccc::Result nocash_result = ImportNocashSymbols(database, extra_symbol_file.Path, builtin_types); + 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 +387,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 +418,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 +456,8 @@ ccc::Result SymbolImporter::ImportNocashSymbols( if (address == 0 && strcmp(value, "0") == 0) continue; + address += base_address; + if (value[0] == '.') { // data directives @@ -498,12 +551,32 @@ std::unique_ptr SymbolImporter::GetBuiltInType( void SymbolImporter::ScanForFunctions( ccc::SymbolDatabase& database, const ccc::ElfSymbolFile& elf, const Pcsx2Config::DebugAnalysisOptions& options) { + MipsExpressionFunctions expression_functions(&r5900Debug, &database, true); + u32 start_address = 0; 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)); + u64 expression_result = 0; + std::string error; + + if (!parseExpression(options.FunctionScanStartAddress.c_str(), &expression_functions, expression_result, error)) + { + Console.Error("Failed to evaluate start address expression '%s' while scanning for functions: %s", + options.FunctionScanStartAddress.c_str(), error.c_str()); + return; + } + + start_address = static_cast(expression_result); + + if (!parseExpression(options.FunctionScanEndAddress.c_str(), &expression_functions, expression_result, error)) + { + Console.Error("Failed to evaluate end address expression '%s' while scanning for functions: %s", + options.FunctionScanEndAddress.c_str(), error.c_str()); + return; + } + + end_address = static_cast(expression_result); } else { diff --git a/pcsx2/DebugTools/SymbolImporter.h b/pcsx2/DebugTools/SymbolImporter.h index ff210cb07b1e7..ad0496d3d09f8 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 071ff140ebae2..655c11af5885f 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -1625,6 +1625,8 @@ void Pcsx2Config::DebugAnalysisOptions::LoadSave(SettingsWrapper& wrap) file = ExtraSymbolFiles[i]; SettingsWrapEntryEx(file.Path, "Path"); + SettingsWrapEntryEx(file.BaseAddress, "BaseAddress"); + SettingsWrapEntryEx(file.Condition, "Condition"); if (wrap.IsLoading()) ExtraSymbolFiles.emplace_back(std::move(file));