mirror of https://github.com/PCSX2/pcsx2.git
Debugger: Allow loading symbols conditionally and with a base address
This commit is contained in:
parent
efb43ac7f9
commit
cce0ae4369
|
@ -100,8 +100,9 @@ Result<std::unique_ptr<SymbolTable>> create_elf_symbol_table(
|
||||||
|
|
||||||
Result<ModuleHandle> import_symbol_tables(
|
Result<ModuleHandle> import_symbol_tables(
|
||||||
SymbolDatabase& database,
|
SymbolDatabase& database,
|
||||||
std::string module_name,
|
|
||||||
const std::vector<std::unique_ptr<SymbolTable>>& symbol_tables,
|
const std::vector<std::unique_ptr<SymbolTable>>& symbol_tables,
|
||||||
|
std::string module_name,
|
||||||
|
Address base_address,
|
||||||
u32 importer_flags,
|
u32 importer_flags,
|
||||||
DemanglerFunctions demangler,
|
DemanglerFunctions demangler,
|
||||||
const std::atomic_bool* interrupt)
|
const std::atomic_bool* interrupt)
|
||||||
|
@ -109,7 +110,8 @@ Result<ModuleHandle> import_symbol_tables(
|
||||||
Result<SymbolSourceHandle> module_source = database.get_symbol_source("Symbol Table Importer");
|
Result<SymbolSourceHandle> module_source = database.get_symbol_source("Symbol Table Importer");
|
||||||
CCC_RETURN_IF_ERROR(module_source);
|
CCC_RETURN_IF_ERROR(module_source);
|
||||||
|
|
||||||
Result<Module*> module_symbol = database.modules.create_symbol(std::move(module_name), *module_source, nullptr);
|
Result<Module*> module_symbol = database.modules.create_symbol(
|
||||||
|
std::move(module_name), base_address, *module_source, nullptr);
|
||||||
CCC_RETURN_IF_ERROR(module_symbol);
|
CCC_RETURN_IF_ERROR(module_symbol);
|
||||||
|
|
||||||
ModuleHandle module_handle = (*module_symbol)->handle();
|
ModuleHandle module_handle = (*module_symbol)->handle();
|
||||||
|
|
|
@ -71,8 +71,9 @@ Result<std::unique_ptr<SymbolTable>> create_elf_symbol_table(
|
||||||
// and to generate a module handle.
|
// and to generate a module handle.
|
||||||
Result<ModuleHandle> import_symbol_tables(
|
Result<ModuleHandle> import_symbol_tables(
|
||||||
SymbolDatabase& database,
|
SymbolDatabase& database,
|
||||||
std::string module_name,
|
|
||||||
const std::vector<std::unique_ptr<SymbolTable>>& symbol_tables,
|
const std::vector<std::unique_ptr<SymbolTable>>& symbol_tables,
|
||||||
|
std::string module_name,
|
||||||
|
Address base_address,
|
||||||
u32 importer_flags,
|
u32 importer_flags,
|
||||||
DemanglerFunctions demangler,
|
DemanglerFunctions demangler,
|
||||||
const std::atomic_bool* interrupt);
|
const std::atomic_bool* interrupt);
|
||||||
|
|
|
@ -104,7 +104,7 @@ DebugAnalysisSettingsWidget::DebugAnalysisSettingsWidget(SettingsWindow* dialog,
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_ui.symbolFileLabel->hide();
|
m_ui.symbolFileLabel->hide();
|
||||||
m_ui.symbolFileList->hide();
|
m_ui.symbolFileTable->hide();
|
||||||
m_ui.importSymbolFileButtons->hide();
|
m_ui.importSymbolFileButtons->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,18 +165,19 @@ void DebugAnalysisSettingsWidget::parseSettingsFromWidgets(Pcsx2Config::DebugAna
|
||||||
output.DemangleSymbols = m_ui.demangleSymbols->isChecked();
|
output.DemangleSymbols = m_ui.demangleSymbols->isChecked();
|
||||||
output.DemangleParameters = m_ui.demangleParameters->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();
|
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<DebugFunctionScanMode>(m_ui.functionScanMode->currentIndex());
|
output.FunctionScanMode = static_cast<DebugFunctionScanMode>(m_ui.functionScanMode->currentIndex());
|
||||||
output.CustomFunctionScanRange = m_ui.customAddressRange->isChecked();
|
output.CustomFunctionScanRange = m_ui.customAddressRange->isChecked();
|
||||||
output.FunctionScanStartAddress = m_ui.addressRangeStart->text().toStdString();
|
output.FunctionScanStartAddress = m_ui.addressRangeStart->text().toStdString();
|
||||||
output.FunctionScanEndAddress = m_ui.addressRangeEnd->text().toStdString();
|
output.FunctionScanEndAddress = m_ui.addressRangeEnd->text().toStdString();
|
||||||
|
|
||||||
output.GenerateFunctionHashes = m_ui.grayOutOverwrittenFunctions->isChecked();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugAnalysisSettingsWidget::setupSymbolSourceGrid()
|
void DebugAnalysisSettingsWidget::setupSymbolSourceGrid()
|
||||||
|
@ -187,27 +188,12 @@ void DebugAnalysisSettingsWidget::setupSymbolSourceGrid()
|
||||||
{
|
{
|
||||||
// Add symbol sources for which the user has already selected whether or
|
// Add symbol sources for which the user has already selected whether or
|
||||||
// not they should be cleared.
|
// not they should be cleared.
|
||||||
int existing_symbol_source_count;
|
int existing_symbol_source_count = getIntSettingValue("Debugger/Analysis/SymbolSources", "Count", 0);
|
||||||
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);
|
|
||||||
|
|
||||||
for (int i = 0; i < existing_symbol_source_count; i++)
|
for (int i = 0; i < existing_symbol_source_count; i++)
|
||||||
{
|
{
|
||||||
std::string section = "Debugger/Analysis/SymbolSources/" + std::to_string(i);
|
std::string section = "Debugger/Analysis/SymbolSources/" + std::to_string(i);
|
||||||
|
std::string name = getStringSettingValue(section.c_str(), "Name", "");
|
||||||
std::string name;
|
bool value = getBoolSettingValue(section.c_str(), "ClearDuringAnalysis", false);
|
||||||
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);
|
|
||||||
|
|
||||||
SymbolSourceTemp& source = m_symbol_sources[name];
|
SymbolSourceTemp& source = m_symbol_sources[name];
|
||||||
source.previous_value = value;
|
source.previous_value = value;
|
||||||
|
@ -320,45 +306,100 @@ void DebugAnalysisSettingsWidget::saveSymbolSources()
|
||||||
|
|
||||||
void DebugAnalysisSettingsWidget::setupSymbolFileList()
|
void DebugAnalysisSettingsWidget::setupSymbolFileList()
|
||||||
{
|
{
|
||||||
int extra_symbol_file_count;
|
m_symbol_file_model = new QStandardItemModel(0, SYMBOL_FILE_COLUMN_COUNT, m_ui.symbolFileTable);
|
||||||
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);
|
|
||||||
|
|
||||||
|
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++)
|
for (int i = 0; i < extra_symbol_file_count; i++)
|
||||||
{
|
{
|
||||||
std::string section = "Debugger/Analysis/ExtraSymbolFiles/" + std::to_string(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.addSymbolFile, &QPushButton::clicked, this, &DebugAnalysisSettingsWidget::addSymbolFile);
|
||||||
connect(m_ui.removeSymbolFile, &QPushButton::clicked, this, &DebugAnalysisSettingsWidget::removeSymbolFile);
|
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()
|
void DebugAnalysisSettingsWidget::addSymbolFile()
|
||||||
{
|
{
|
||||||
QString path = QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Add Symbol File")));
|
std::string path = Path::ToNativePath(QFileDialog::getOpenFileName(this, tr("Add Symbol File")).toStdString());
|
||||||
if (path.isEmpty())
|
if (path.empty())
|
||||||
return;
|
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();
|
saveSymbolFiles();
|
||||||
|
updateEnabledStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugAnalysisSettingsWidget::removeSymbolFile()
|
void DebugAnalysisSettingsWidget::removeSymbolFile()
|
||||||
{
|
{
|
||||||
for (QListWidgetItem* item : m_ui.symbolFileList->selectedItems())
|
QItemSelectionModel* selection_model = m_ui.symbolFileTable->selectionModel();
|
||||||
delete item;
|
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();
|
saveSymbolFiles();
|
||||||
|
updateEnabledStates();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebugAnalysisSettingsWidget::saveSymbolFiles()
|
void DebugAnalysisSettingsWidget::saveSymbolFiles()
|
||||||
|
@ -380,17 +421,24 @@ void DebugAnalysisSettingsWidget::saveSymbolFiles()
|
||||||
|
|
||||||
sif->RemoveSection("Debugger/Analysis/ExtraSymbolFiles");
|
sif->RemoveSection("Debugger/Analysis/ExtraSymbolFiles");
|
||||||
|
|
||||||
if (m_ui.symbolFileList->count() == 0)
|
if (m_symbol_file_model->rowCount() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Make new configuration entries.
|
// 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 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);
|
QtHost::SaveGameSettings(sif, true);
|
||||||
|
@ -423,5 +471,34 @@ void DebugAnalysisSettingsWidget::updateEnabledStates()
|
||||||
m_ui.symbolSourceScrollArea->setEnabled(!m_ui.automaticallyClearSymbols->isChecked());
|
m_ui.symbolSourceScrollArea->setEnabled(!m_ui.automaticallyClearSymbols->isChecked());
|
||||||
m_ui.symbolSourceErrorMessage->setEnabled(!m_ui.automaticallyClearSymbols->isChecked());
|
m_ui.symbolSourceErrorMessage->setEnabled(!m_ui.automaticallyClearSymbols->isChecked());
|
||||||
m_ui.demangleParameters->setEnabled(m_ui.demangleSymbols->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());
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
|
|
||||||
|
#include <QtGui/QStandardItemModel>
|
||||||
#include <QtWidgets/QDialog>
|
#include <QtWidgets/QDialog>
|
||||||
|
|
||||||
class SettingsWindow;
|
class SettingsWindow;
|
||||||
|
@ -16,7 +17,7 @@ class DebugAnalysisSettingsWidget : public QWidget
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Create a widget that will discard any settings changed after it is
|
// 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);
|
DebugAnalysisSettingsWidget(QWidget* parent = nullptr);
|
||||||
|
|
||||||
// Create a widget that will write back any settings changed to the config
|
// Create a widget that will write back any settings changed to the config
|
||||||
|
@ -42,6 +43,10 @@ protected:
|
||||||
|
|
||||||
void updateEnabledStates();
|
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
|
struct SymbolSourceTemp
|
||||||
{
|
{
|
||||||
QCheckBox* check_box = nullptr;
|
QCheckBox* check_box = nullptr;
|
||||||
|
@ -49,8 +54,18 @@ protected:
|
||||||
bool modified_by_user = false;
|
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;
|
SettingsWindow* m_dialog = nullptr;
|
||||||
std::map<std::string, SymbolSourceTemp> m_symbol_sources;
|
std::map<std::string, SymbolSourceTemp> m_symbol_sources;
|
||||||
|
|
||||||
|
QStandardItemModel* m_symbol_file_model;
|
||||||
|
|
||||||
Ui::DebugAnalysisSettingsWidget m_ui;
|
Ui::DebugAnalysisSettingsWidget m_ui;
|
||||||
};
|
};
|
||||||
|
|
|
@ -171,7 +171,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QListWidget" name="symbolFileList">
|
<widget class="QTableView" name="symbolFileTable">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
@ -184,9 +184,27 @@
|
||||||
<height>100</height>
|
<height>100</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="sortingEnabled">
|
<property name="alternatingRowColors">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="selectionBehavior">
|
||||||
|
<enum>QAbstractItemView::SelectRows</enum>
|
||||||
|
</property>
|
||||||
|
<property name="textElideMode">
|
||||||
|
<enum>Qt::ElideLeft</enum>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<attribute name="horizontalHeaderHighlightSections">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="horizontalHeaderStretchLastSection">
|
||||||
|
<bool>true</bool>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="verticalHeaderVisible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -208,6 +208,8 @@ struct DebugSymbolSource
|
||||||
struct DebugExtraSymbolFile
|
struct DebugExtraSymbolFile
|
||||||
{
|
{
|
||||||
std::string Path;
|
std::string Path;
|
||||||
|
std::string BaseAddress;
|
||||||
|
std::string Condition;
|
||||||
|
|
||||||
friend auto operator<=>(const DebugExtraSymbolFile& lhs, const DebugExtraSymbolFile& rhs) = default;
|
friend auto operator<=>(const DebugExtraSymbolFile& lhs, const DebugExtraSymbolFile& rhs) = default;
|
||||||
};
|
};
|
||||||
|
@ -1276,7 +1278,7 @@ struct Pcsx2Config
|
||||||
EnableGameFixes : 1, // enables automatic game fixes
|
EnableGameFixes : 1, // enables automatic game fixes
|
||||||
SaveStateOnShutdown : 1, // default value for saving state on shutdown
|
SaveStateOnShutdown : 1, // default value for saving state on shutdown
|
||||||
EnableDiscordPresence : 1, // enables discord rich presence integration
|
EnableDiscordPresence : 1, // enables discord rich presence integration
|
||||||
UseSavestateSelector: 1,
|
UseSavestateSelector : 1,
|
||||||
InhibitScreensaver : 1,
|
InhibitScreensaver : 1,
|
||||||
BackupSavestate : 1,
|
BackupSavestate : 1,
|
||||||
McdFolderAutoManage : 1,
|
McdFolderAutoManage : 1,
|
||||||
|
|
|
@ -34,227 +34,6 @@ enum ReferenceIndexType
|
||||||
REF_INDEX_VFPU = 0x10000,
|
REF_INDEX_VFPU = 0x10000,
|
||||||
REF_INDEX_VFPU_INT = 0x20000,
|
REF_INDEX_VFPU_INT = 0x20000,
|
||||||
REF_INDEX_IS_FLOAT = REF_INDEX_FPU | REF_INDEX_VFPU,
|
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<u16>(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<std::string, ccc::FunctionHandle> m_mangled_function_names_to_handles;
|
|
||||||
std::map<std::string, ccc::GlobalVariableHandle> m_mangled_global_names_to_handles;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -369,13 +148,13 @@ bool DebugInterface::evaluateExpression(const char* expression, u64& dest, std::
|
||||||
|
|
||||||
bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest, std::string& error)
|
bool DebugInterface::initExpression(const char* exp, PostfixExpression& dest, std::string& error)
|
||||||
{
|
{
|
||||||
MipsExpressionFunctions funcs(this, true);
|
MipsExpressionFunctions funcs(this, nullptr, true);
|
||||||
return initPostfixExpression(exp, &funcs, dest, error);
|
return initPostfixExpression(exp, &funcs, dest, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DebugInterface::parseExpression(PostfixExpression& exp, u64& dest, std::string& error)
|
bool DebugInterface::parseExpression(PostfixExpression& exp, u64& dest, std::string& error)
|
||||||
{
|
{
|
||||||
MipsExpressionFunctions funcs(this, false);
|
MipsExpressionFunctions funcs(this, nullptr, false);
|
||||||
return parsePostfixExpression(exp, &funcs, dest, error);
|
return parsePostfixExpression(exp, &funcs, dest, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1306,3 +1085,239 @@ u64 ElfMemoryReader::read64(u32 address, bool& valid)
|
||||||
|
|
||||||
return *result;
|
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<u16>(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;
|
||||||
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0+
|
// SPDX-License-Identifier: GPL-3.0+
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "DebugTools/BiosDebugData.h"
|
|
||||||
#include "MemoryTypes.h"
|
#include "BiosDebugData.h"
|
||||||
#include "ExpressionParser.h"
|
#include "ExpressionParser.h"
|
||||||
#include "SymbolGuardian.h"
|
#include "SymbolGuardian.h"
|
||||||
#include "SymbolImporter.h"
|
#include "SymbolImporter.h"
|
||||||
|
@ -210,5 +210,26 @@ protected:
|
||||||
const ccc::ElfFile& m_elf;
|
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<std::string, ccc::FunctionHandle> m_mangled_function_names_to_handles;
|
||||||
|
std::map<std::string, ccc::GlobalVariableHandle> m_mangled_global_names_to_handles;
|
||||||
|
};
|
||||||
|
|
||||||
extern R5900DebugInterface r5900Debug;
|
extern R5900DebugInterface r5900Debug;
|
||||||
extern R3000DebugInterface r3000Debug;
|
extern R3000DebugInterface r3000Debug;
|
||||||
|
|
|
@ -276,13 +276,6 @@ void SymbolImporter::ImportSymbols(
|
||||||
const std::map<std::string, ccc::DataTypeHandle>& builtin_types,
|
const std::map<std::string, ccc::DataTypeHandle>& builtin_types,
|
||||||
const std::atomic_bool* interrupt)
|
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 =
|
u32 importer_flags =
|
||||||
ccc::NO_MEMBER_FUNCTIONS |
|
ccc::NO_MEMBER_FUNCTIONS |
|
||||||
ccc::NO_OPTIMIZED_OUT_FUNCTIONS |
|
ccc::NO_OPTIMIZED_OUT_FUNCTIONS |
|
||||||
|
@ -291,6 +284,13 @@ void SymbolImporter::ImportSymbols(
|
||||||
if (options.DemangleParameters)
|
if (options.DemangleParameters)
|
||||||
importer_flags |= ccc::DEMANGLE_PARAMETERS;
|
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)
|
if (options.ImportSymbolsFromELF)
|
||||||
{
|
{
|
||||||
ccc::Result<std::vector<std::unique_ptr<ccc::SymbolTable>>> symbol_tables = elf.get_all_symbol_tables();
|
ccc::Result<std::vector<std::unique_ptr<ccc::SymbolTable>>> symbol_tables = elf.get_all_symbol_tables();
|
||||||
|
@ -301,7 +301,7 @@ void SymbolImporter::ImportSymbols(
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ccc::Result<ccc::ModuleHandle> module_handle = ccc::import_symbol_tables(
|
ccc::Result<ccc::ModuleHandle> 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())
|
if (!module_handle.success())
|
||||||
{
|
{
|
||||||
ccc::report_error(module_handle.error());
|
ccc::report_error(module_handle.error());
|
||||||
|
@ -311,7 +311,7 @@ void SymbolImporter::ImportSymbols(
|
||||||
|
|
||||||
if (!nocash_path.empty() && options.ImportSymFileFromDefaultLocation)
|
if (!nocash_path.empty() && options.ImportSymFileFromDefaultLocation)
|
||||||
{
|
{
|
||||||
ccc::Result<bool> nocash_result = ImportNocashSymbols(database, nocash_path, builtin_types);
|
ccc::Result<bool> nocash_result = ImportNocashSymbols(database, nocash_path, 0, builtin_types);
|
||||||
if (!nocash_result.success())
|
if (!nocash_result.success())
|
||||||
{
|
{
|
||||||
Console.Error("Failed to import symbol file '%s': %s",
|
Console.Error("Failed to import symbol file '%s': %s",
|
||||||
|
@ -319,14 +319,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<std::string, ccc::DataTypeHandle>& 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)
|
for (const DebugExtraSymbolFile& extra_symbol_file : options.ExtraSymbolFiles)
|
||||||
{
|
{
|
||||||
if (*interrupt)
|
if (*interrupt)
|
||||||
return;
|
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<bool> 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;
|
||||||
|
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 parse 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<u32>(expression_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtil::EndsWithNoCase(path, ".sym"))
|
||||||
|
{
|
||||||
|
ccc::Result<bool> nocash_result = ImportNocashSymbols(
|
||||||
|
database, path, base_address.get_or_zero(), builtin_types);
|
||||||
if (!nocash_result.success())
|
if (!nocash_result.success())
|
||||||
{
|
{
|
||||||
Console.Error("Failed to import symbol file '%s': %s",
|
Console.Error("Failed to import symbol file '%s': %s",
|
||||||
|
@ -334,29 +384,30 @@ void SymbolImporter::ImportSymbols(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*nocash_result)
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Error error;
|
std::optional<std::vector<u8>> image = FileSystem::ReadBinaryFile(path.c_str());
|
||||||
std::optional<std::vector<u8>> image = FileSystem::ReadBinaryFile(extra_symbol_file.Path.c_str());
|
|
||||||
if (!image.has_value())
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string file_name(Path::GetFileName(extra_symbol_file.Path));
|
std::string file_name(Path::GetFileName(path));
|
||||||
|
|
||||||
ccc::Result<std::unique_ptr<ccc::SymbolFile>> symbol_file = ccc::parse_symbol_file(std::move(*image), file_name.c_str());
|
ccc::Result<std::unique_ptr<ccc::SymbolFile>> symbol_file = ccc::parse_symbol_file(
|
||||||
|
std::move(*image), file_name.c_str());
|
||||||
if (!symbol_file.success())
|
if (!symbol_file.success())
|
||||||
{
|
{
|
||||||
ccc::report_error(symbol_file.error());
|
ccc::report_error(symbol_file.error());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ccc::Result<std::vector<std::unique_ptr<ccc::SymbolTable>>> symbol_tables = (*symbol_file)->get_all_symbol_tables();
|
ccc::Result<std::vector<std::unique_ptr<ccc::SymbolTable>>> symbol_tables =
|
||||||
|
(*symbol_file)->get_all_symbol_tables();
|
||||||
if (!symbol_tables.success())
|
if (!symbol_tables.success())
|
||||||
{
|
{
|
||||||
ccc::report_error(symbol_tables.error());
|
ccc::report_error(symbol_tables.error());
|
||||||
|
@ -364,22 +415,19 @@ void SymbolImporter::ImportSymbols(
|
||||||
}
|
}
|
||||||
|
|
||||||
ccc::Result<ccc::ModuleHandle> module_handle = ccc::import_symbol_tables(
|
ccc::Result<ccc::ModuleHandle> 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())
|
if (!module_handle.success())
|
||||||
{
|
{
|
||||||
ccc::report_error(module_handle.error());
|
ccc::report_error(module_handle.error());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLn("Imported %d symbols.", database.symbol_count());
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ccc::Result<bool> SymbolImporter::ImportNocashSymbols(
|
ccc::Result<bool> SymbolImporter::ImportNocashSymbols(
|
||||||
ccc::SymbolDatabase& database,
|
ccc::SymbolDatabase& database,
|
||||||
const std::string& file_path,
|
const std::string& file_path,
|
||||||
|
u32 base_address,
|
||||||
const std::map<std::string, ccc::DataTypeHandle>& builtin_types)
|
const std::map<std::string, ccc::DataTypeHandle>& builtin_types)
|
||||||
{
|
{
|
||||||
auto file = FileSystem::OpenManagedCFile(file_path.c_str(), "r");
|
auto file = FileSystem::OpenManagedCFile(file_path.c_str(), "r");
|
||||||
|
@ -405,6 +453,8 @@ ccc::Result<bool> SymbolImporter::ImportNocashSymbols(
|
||||||
if (address == 0 && strcmp(value, "0") == 0)
|
if (address == 0 && strcmp(value, "0") == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
address += base_address;
|
||||||
|
|
||||||
if (value[0] == '.')
|
if (value[0] == '.')
|
||||||
{
|
{
|
||||||
// data directives
|
// data directives
|
||||||
|
|
|
@ -46,9 +46,18 @@ public:
|
||||||
const std::map<std::string, ccc::DataTypeHandle>& builtin_types,
|
const std::map<std::string, ccc::DataTypeHandle>& builtin_types,
|
||||||
const std::atomic_bool* interrupt);
|
const std::atomic_bool* interrupt);
|
||||||
|
|
||||||
|
static void ImportExtraSymbols(
|
||||||
|
ccc::SymbolDatabase& database,
|
||||||
|
const Pcsx2Config::DebugAnalysisOptions& options,
|
||||||
|
const std::map<std::string, ccc::DataTypeHandle>& builtin_types,
|
||||||
|
u32 importer_flags,
|
||||||
|
const ccc::DemanglerFunctions& demangler,
|
||||||
|
const std::atomic_bool* interrupt);
|
||||||
|
|
||||||
static ccc::Result<bool> ImportNocashSymbols(
|
static ccc::Result<bool> ImportNocashSymbols(
|
||||||
ccc::SymbolDatabase& database,
|
ccc::SymbolDatabase& database,
|
||||||
const std::string& file_path,
|
const std::string& file_path,
|
||||||
|
u32 base_address,
|
||||||
const std::map<std::string, ccc::DataTypeHandle>& builtin_types);
|
const std::map<std::string, ccc::DataTypeHandle>& builtin_types);
|
||||||
|
|
||||||
static std::unique_ptr<ccc::ast::Node> GetBuiltInType(
|
static std::unique_ptr<ccc::ast::Node> GetBuiltInType(
|
||||||
|
|
|
@ -1625,6 +1625,8 @@ void Pcsx2Config::DebugAnalysisOptions::LoadSave(SettingsWrapper& wrap)
|
||||||
file = ExtraSymbolFiles[i];
|
file = ExtraSymbolFiles[i];
|
||||||
|
|
||||||
SettingsWrapEntryEx(file.Path, "Path");
|
SettingsWrapEntryEx(file.Path, "Path");
|
||||||
|
SettingsWrapEntryEx(file.BaseAddress, "BaseAddress");
|
||||||
|
SettingsWrapEntryEx(file.Condition, "Condition");
|
||||||
|
|
||||||
if (wrap.IsLoading())
|
if (wrap.IsLoading())
|
||||||
ExtraSymbolFiles.emplace_back(std::move(file));
|
ExtraSymbolFiles.emplace_back(std::move(file));
|
||||||
|
|
Loading…
Reference in New Issue