mirror of https://github.com/PCSX2/pcsx2.git
Debugger: Add settings to control the analysis passes
This commit is contained in:
parent
0fd7e1d7c2
commit
2c3abe33d5
|
@ -83,6 +83,9 @@ target_sources(pcsx2-qt PRIVATE
|
|||
Settings/ControllerSettingsWindow.h
|
||||
Settings/ControllerSettingsWindow.ui
|
||||
Settings/ControllerSettingWidgetBinder.h
|
||||
Settings/DebugAnalysisSettingsWidget.cpp
|
||||
Settings/DebugAnalysisSettingsWidget.h
|
||||
Settings/DebugAnalysisSettingsWidget.ui
|
||||
Settings/DebugSettingsWidget.cpp
|
||||
Settings/DebugSettingsWidget.h
|
||||
Settings/DebugSettingsWidget.ui
|
||||
|
@ -152,6 +155,9 @@ target_sources(pcsx2-qt PRIVATE
|
|||
Settings/USBBindingWidget_GunCon2.ui
|
||||
Settings/USBBindingWidget_RyojouhenCon.ui
|
||||
Settings/USBBindingWidget_ShinkansenCon.ui
|
||||
Debugger/AnalysisOptionsDialog.cpp
|
||||
Debugger/AnalysisOptionsDialog.h
|
||||
Debugger/AnalysisOptionsDialog.ui
|
||||
Debugger/CpuWidget.cpp
|
||||
Debugger/CpuWidget.h
|
||||
Debugger/CpuWidget.ui
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "AnalysisOptionsDialog.h"
|
||||
|
||||
#include "Host.h"
|
||||
#include "DebugTools/SymbolImporter.h"
|
||||
|
||||
AnalysisOptionsDialog::AnalysisOptionsDialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
m_analysis_settings = new DebugAnalysisSettingsWidget();
|
||||
|
||||
m_ui.analysisSettings->setLayout(new QVBoxLayout());
|
||||
m_ui.analysisSettings->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
m_ui.analysisSettings->layout()->addWidget(m_analysis_settings);
|
||||
|
||||
connect(m_ui.analyseButton, &QPushButton::clicked, this, &AnalysisOptionsDialog::analyse);
|
||||
connect(m_ui.closeButton, &QPushButton::clicked, this, &QDialog::reject);
|
||||
}
|
||||
|
||||
void AnalysisOptionsDialog::analyse()
|
||||
{
|
||||
Pcsx2Config::DebugAnalysisOptions options;
|
||||
m_analysis_settings->parseSettingsFromWidgets(options);
|
||||
|
||||
Host::RunOnCPUThread([options]() {
|
||||
R5900SymbolImporter.LoadAndAnalyseElf(options);
|
||||
});
|
||||
|
||||
if (m_ui.closeCheckBox->isChecked())
|
||||
accept();
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Settings/DebugAnalysisSettingsWidget.h"
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
#include "ui_AnalysisOptionsDialog.h"
|
||||
|
||||
class AnalysisOptionsDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AnalysisOptionsDialog(QWidget* parent = nullptr);
|
||||
|
||||
protected:
|
||||
void analyse();
|
||||
|
||||
DebugAnalysisSettingsWidget* m_analysis_settings;
|
||||
|
||||
Ui::AnalysisOptionsDialog m_ui;
|
||||
};
|
|
@ -0,0 +1,113 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AnalysisOptionsDialog</class>
|
||||
<widget class="QDialog" name="AnalysisOptionsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>750</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>500</width>
|
||||
<height>650</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Analysis Options</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Changes made here won't be saved. Edit these settings from the global or per-game settings dialogs to have your changes take effect for future analysis runs.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="analysisSettings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>488</width>
|
||||
<height>636</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttons">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="closeCheckBox">
|
||||
<property name="text">
|
||||
<string>Close dialog after analysis has started</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="analyseButton">
|
||||
<property name="text">
|
||||
<string>Analyze</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="closeButton">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -5,9 +5,11 @@
|
|||
|
||||
#include "DebugTools/DebugInterface.h"
|
||||
#include "DebugTools/Breakpoints.h"
|
||||
#include "DebugTools/SymbolImporter.h"
|
||||
#include "VMManager.h"
|
||||
#include "QtHost.h"
|
||||
#include "MainWindow.h"
|
||||
#include "AnalysisOptionsDialog.h"
|
||||
|
||||
DebuggerWindow::DebuggerWindow(QWidget* parent)
|
||||
: QMainWindow(parent)
|
||||
|
@ -28,6 +30,7 @@ DebuggerWindow::DebuggerWindow(QWidget* parent)
|
|||
connect(m_ui.actionStepInto, &QAction::triggered, this, &DebuggerWindow::onStepInto);
|
||||
connect(m_ui.actionStepOver, &QAction::triggered, this, &DebuggerWindow::onStepOver);
|
||||
connect(m_ui.actionStepOut, &QAction::triggered, this, &DebuggerWindow::onStepOut);
|
||||
connect(m_ui.actionAnalyse, &QAction::triggered, this, &DebuggerWindow::onAnalyse);
|
||||
connect(m_ui.actionOnTop, &QAction::triggered, [this] { this->setWindowFlags(this->windowFlags() ^ Qt::WindowStaysOnTopHint); this->show(); });
|
||||
|
||||
connect(g_emu_thread, &EmuThread::onVMPaused, this, &DebuggerWindow::onVMStateChanged);
|
||||
|
@ -38,7 +41,7 @@ DebuggerWindow::DebuggerWindow(QWidget* parent)
|
|||
// We can't do this in the designer, but we want to right align the actionOnTop action in the toolbar
|
||||
QWidget* spacer = new QWidget(this);
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
m_ui.toolBar->insertWidget(m_ui.actionOnTop, spacer);
|
||||
m_ui.toolBar->insertWidget(m_ui.actionAnalyse, spacer);
|
||||
|
||||
m_cpuWidget_r5900 = new CpuWidget(this, r5900Debug);
|
||||
m_cpuWidget_r3000 = new CpuWidget(this, r3000Debug);
|
||||
|
@ -127,3 +130,25 @@ void DebuggerWindow::onStepOut()
|
|||
CpuWidget* currentCpu = static_cast<CpuWidget*>(m_ui.cpuTabs->currentWidget());
|
||||
currentCpu->onStepOut();
|
||||
}
|
||||
|
||||
void DebuggerWindow::onAnalyse()
|
||||
{
|
||||
AnalysisOptionsDialog* dialog = new AnalysisOptionsDialog(this);
|
||||
dialog->show();
|
||||
}
|
||||
|
||||
void DebuggerWindow::showEvent(QShowEvent* event)
|
||||
{
|
||||
Host::RunOnCPUThread([]() {
|
||||
R5900SymbolImporter.OnDebuggerOpened();
|
||||
});
|
||||
QMainWindow::showEvent(event);
|
||||
}
|
||||
|
||||
void DebuggerWindow::hideEvent(QHideEvent* event)
|
||||
{
|
||||
Host::RunOnCPUThread([]() {
|
||||
R5900SymbolImporter.OnDebuggerClosed();
|
||||
});
|
||||
QMainWindow::hideEvent(event);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@ public slots:
|
|||
void onStepInto();
|
||||
void onStepOver();
|
||||
void onStepOut();
|
||||
void onAnalyse();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent* event);
|
||||
void hideEvent(QHideEvent *event);
|
||||
|
||||
private:
|
||||
Ui::DebuggerWindow m_ui;
|
||||
|
|
|
@ -29,12 +29,6 @@
|
|||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::PreventContextMenu</enum>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="floatable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="movable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
|
@ -44,6 +38,12 @@
|
|||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="floatable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="toolBarArea">
|
||||
<enum>TopToolBarArea</enum>
|
||||
</attribute>
|
||||
|
@ -54,11 +54,13 @@
|
|||
<addaction name="actionStepInto"/>
|
||||
<addaction name="actionStepOver"/>
|
||||
<addaction name="actionStepOut"/>
|
||||
<addaction name="actionAnalyse"/>
|
||||
<addaction name="actionOnTop"/>
|
||||
</widget>
|
||||
<action name="actionRun">
|
||||
<property name="icon">
|
||||
<iconset theme="play-line"/>
|
||||
<iconset theme="play-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Run</string>
|
||||
|
@ -66,7 +68,8 @@
|
|||
</action>
|
||||
<action name="actionStepInto">
|
||||
<property name="icon">
|
||||
<iconset theme="debug-step-into-line"/>
|
||||
<iconset theme="debug-step-into-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Step Into</string>
|
||||
|
@ -77,7 +80,8 @@
|
|||
</action>
|
||||
<action name="actionStepOver">
|
||||
<property name="icon">
|
||||
<iconset theme="debug-step-over-line"/>
|
||||
<iconset theme="debug-step-over-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Step Over</string>
|
||||
|
@ -88,7 +92,8 @@
|
|||
</action>
|
||||
<action name="actionStepOut">
|
||||
<property name="icon">
|
||||
<iconset theme="debug-step-out-line"/>
|
||||
<iconset theme="debug-step-out-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Step Out</string>
|
||||
|
@ -98,12 +103,13 @@
|
|||
</property>
|
||||
</action>
|
||||
<action name="actionOnTop">
|
||||
<property name="icon">
|
||||
<iconset theme="pin-filled"/>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="pin-filled">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Always On Top</string>
|
||||
</property>
|
||||
|
@ -111,6 +117,15 @@
|
|||
<string>Show this window on top</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAnalyse">
|
||||
<property name="icon">
|
||||
<iconset theme="restart-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Analyze</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
|
|
@ -337,7 +337,7 @@ void NewFunctionDialog::createSymbol()
|
|||
|
||||
QString error_message;
|
||||
m_cpu.GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("User-defined");
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("User-Defined");
|
||||
if (!source.success())
|
||||
{
|
||||
error_message = tr("Cannot create symbol source.");
|
||||
|
@ -398,7 +398,7 @@ void NewGlobalVariableDialog::createSymbol()
|
|||
|
||||
QString error_message;
|
||||
m_cpu.GetSymbolGuardian().ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("User-defined");
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("User-Defined");
|
||||
if (!source.success())
|
||||
{
|
||||
error_message = tr("Cannot create symbol source.");
|
||||
|
@ -508,7 +508,7 @@ void NewLocalVariableDialog::createSymbol()
|
|||
return;
|
||||
}
|
||||
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("User-defined");
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("User-Defined");
|
||||
if (!source.success())
|
||||
{
|
||||
error_message = tr("Cannot create symbol source.");
|
||||
|
@ -622,7 +622,7 @@ void NewParameterVariableDialog::createSymbol()
|
|||
return;
|
||||
}
|
||||
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("User-defined");
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("User-Defined");
|
||||
if (!source.success())
|
||||
{
|
||||
error_message = tr("Cannot create symbol source.");
|
||||
|
|
|
@ -50,14 +50,14 @@ protected:
|
|||
|
||||
protected slots:
|
||||
virtual bool parseUserInput() = 0;
|
||||
|
||||
|
||||
protected:
|
||||
virtual void createSymbol() = 0;
|
||||
|
||||
void setupRegisterField();
|
||||
void setupSizeField();
|
||||
void setupFunctionField();
|
||||
|
||||
|
||||
void connectInputWidgets();
|
||||
void updateErrorMessage(QString error_message);
|
||||
|
||||
|
@ -75,14 +75,14 @@ protected:
|
|||
|
||||
u32 storageType() const;
|
||||
void onStorageTabChanged(int index);
|
||||
|
||||
|
||||
std::string parseName(QString& error_message);
|
||||
u32 parseAddress(QString& error_message);
|
||||
|
||||
DebugInterface& m_cpu;
|
||||
Ui::NewSymbolDialog m_ui;
|
||||
|
||||
u32 m_alignment;
|
||||
u32 m_alignment;
|
||||
std::vector<ccc::FunctionHandle> m_functions;
|
||||
};
|
||||
|
||||
|
@ -96,7 +96,7 @@ public:
|
|||
protected:
|
||||
bool parseUserInput() override;
|
||||
void createSymbol() override;
|
||||
|
||||
|
||||
std::string m_name;
|
||||
u32 m_address = 0;
|
||||
u32 m_size = 0;
|
||||
|
@ -114,7 +114,7 @@ public:
|
|||
protected:
|
||||
bool parseUserInput() override;
|
||||
void createSymbol() override;
|
||||
|
||||
|
||||
std::string m_name;
|
||||
u32 m_address;
|
||||
std::unique_ptr<ccc::ast::Node> m_type;
|
||||
|
@ -130,7 +130,7 @@ public:
|
|||
protected:
|
||||
bool parseUserInput() override;
|
||||
void createSymbol() override;
|
||||
|
||||
|
||||
std::string m_name;
|
||||
std::variant<ccc::GlobalStorage, ccc::RegisterStorage, ccc::StackStorage> m_storage;
|
||||
u32 m_address = 0;
|
||||
|
@ -148,7 +148,7 @@ public:
|
|||
protected:
|
||||
bool parseUserInput() override;
|
||||
void createSymbol() override;
|
||||
|
||||
|
||||
std::string m_name;
|
||||
std::variant<ccc::RegisterStorage, ccc::StackStorage> m_storage;
|
||||
std::unique_ptr<ccc::ast::Node> m_type;
|
||||
|
|
|
@ -61,7 +61,8 @@ void SymbolTreeWidget::reset()
|
|||
|
||||
m_ui.treeView->setColumnHidden(SymbolTreeModel::SIZE, !m_show_size_column || !m_show_size_column->isChecked());
|
||||
|
||||
m_cpu.GetSymbolImporter().UpdateFunctionHashes(m_cpu);
|
||||
if (m_cpu.GetSymbolImporter())
|
||||
m_cpu.GetSymbolImporter()->UpdateFunctionHashes(m_cpu);
|
||||
|
||||
SymbolFilters filters;
|
||||
std::unique_ptr<SymbolTreeNode> root;
|
||||
|
|
|
@ -0,0 +1,426 @@
|
|||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "DebugAnalysisSettingsWidget.h"
|
||||
|
||||
#include "SettingsWindow.h"
|
||||
#include "SettingWidgetBinder.h"
|
||||
|
||||
#include "DebugTools/SymbolImporter.h"
|
||||
|
||||
#include <QtWidgets/QFileDialog>
|
||||
|
||||
DebugAnalysisSettingsWidget::DebugAnalysisSettingsWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
m_ui.automaticallyClearSymbols->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "AutomaticallySelectSymbolsToClear", true));
|
||||
|
||||
setupSymbolSourceGrid();
|
||||
|
||||
m_ui.importFromElf->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "ImportSymbolsFromELF", true));
|
||||
m_ui.importSymFileFromDefaultLocation->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "ImportSymFileFromDefaultLocation", true));
|
||||
m_ui.demangleSymbols->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "DemangleSymbols", true));
|
||||
m_ui.demangleParameters->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "DemangleParameters", true));
|
||||
|
||||
setupSymbolFileList();
|
||||
|
||||
std::string function_scan_mode = Host::GetStringSettingValue("Debugger/Analysis", "FunctionScanMode");
|
||||
for (int i = 0;; i++)
|
||||
{
|
||||
if (Pcsx2Config::DebugAnalysisOptions::FunctionScanModeNames[i] == nullptr)
|
||||
break;
|
||||
|
||||
if (function_scan_mode == Pcsx2Config::DebugAnalysisOptions::FunctionScanModeNames[i])
|
||||
m_ui.functionScanMode->setCurrentIndex(i);
|
||||
}
|
||||
|
||||
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.grayOutOverwrittenFunctions->setChecked(Host::GetBoolSettingValue("Debugger/Analysis", "GenerateFunctionHashes", true));
|
||||
|
||||
connect(m_ui.automaticallyClearSymbols, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
||||
connect(m_ui.demangleSymbols, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
||||
connect(m_ui.customAddressRange, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
||||
|
||||
updateEnabledStates();
|
||||
}
|
||||
|
||||
DebugAnalysisSettingsWidget::DebugAnalysisSettingsWidget(SettingsWindow* dialog, QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_dialog(dialog)
|
||||
{
|
||||
SettingsInterface* sif = dialog->getSettingsInterface();
|
||||
|
||||
m_ui.setupUi(this);
|
||||
|
||||
// Make sure the user doesn't select symbol sources from both the global
|
||||
// settings and the per-game settings, as these settings will conflict with
|
||||
// each other. It only really makes sense to modify these settings on a
|
||||
// per-game basis anyway.
|
||||
if (dialog->isPerGameSettings())
|
||||
{
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(
|
||||
sif, m_ui.automaticallyClearSymbols, "Debugger/Analysis", "AutomaticallySelectSymbolsToClear", true);
|
||||
|
||||
m_dialog->registerWidgetHelp(m_ui.automaticallyClearSymbols, tr("Automatically Select Symbols To Clear"), tr("Checked"),
|
||||
tr("Automatically delete symbols that were generated by any previous analysis runs."));
|
||||
|
||||
setupSymbolSourceGrid();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.clearExistingSymbolsGroup->hide();
|
||||
}
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(
|
||||
sif, m_ui.importFromElf, "Debugger/Analysis", "ImportSymbolsFromELF", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(
|
||||
sif, m_ui.importSymFileFromDefaultLocation, "Debugger/Analysis", "ImportSymFileFromDefaultLocation", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(
|
||||
sif, m_ui.demangleSymbols, "Debugger/Analysis", "DemangleSymbols", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(
|
||||
sif, m_ui.demangleParameters, "Debugger/Analysis", "DemangleParameters", true);
|
||||
|
||||
m_dialog->registerWidgetHelp(m_ui.importFromElf, tr("Import From ELF"), tr("Checked"),
|
||||
tr("Import symbol tables stored in the game's boot ELF."));
|
||||
m_dialog->registerWidgetHelp(m_ui.importSymFileFromDefaultLocation, tr("Import Default .sym File"), tr("Checked"),
|
||||
tr("Import symbols from a .sym file with the same name as the loaded ISO file on disk if such a file exists."));
|
||||
m_dialog->registerWidgetHelp(m_ui.demangleSymbols, tr("Demangle Symbols"), tr("Checked"),
|
||||
tr("Demangle C++ symbols during the import process so that the function and global variable names shown in the "
|
||||
"debugger are more readable."));
|
||||
m_dialog->registerWidgetHelp(m_ui.demangleParameters, tr("Demangle Parameters"), tr("Checked"),
|
||||
tr("Include parameter lists in demangled function names."));
|
||||
|
||||
// Same as above. It only makes sense to load extra symbol files on a
|
||||
// per-game basis.
|
||||
if (dialog->isPerGameSettings())
|
||||
{
|
||||
setupSymbolFileList();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.symbolFileLabel->hide();
|
||||
m_ui.symbolFileList->hide();
|
||||
m_ui.importSymbolFileButtons->hide();
|
||||
}
|
||||
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(
|
||||
sif, m_ui.functionScanMode, "Debugger/Analysis", "FunctionScanMode",
|
||||
Pcsx2Config::DebugAnalysisOptions::FunctionScanModeNames, DebugFunctionScanMode::SCAN_ELF);
|
||||
|
||||
m_dialog->registerWidgetHelp(m_ui.functionScanMode, tr("Scan Mode"), tr("Scan ELF"),
|
||||
tr("Choose where the function scanner looks to find functions. This option can be useful if the application "
|
||||
"loads additional code at runtime."));
|
||||
|
||||
// Same as above. It only makes sense to set a custom memory range on a
|
||||
// per-game basis.
|
||||
if (dialog->isPerGameSettings())
|
||||
{
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(
|
||||
sif, m_ui.customAddressRange, "Debugger/Analysis", "CustomFunctionScanRange", false);
|
||||
connect(m_ui.addressRangeStart, &QLineEdit::textChanged, this, &DebugAnalysisSettingsWidget::functionScanRangeChanged);
|
||||
connect(m_ui.addressRangeEnd, &QLineEdit::textChanged, this, &DebugAnalysisSettingsWidget::functionScanRangeChanged);
|
||||
|
||||
m_dialog->registerWidgetHelp(m_ui.customAddressRange, tr("Custom Address Range"), tr("Unchecked"),
|
||||
tr("Whether to look for functions from the address range specified (Checked), or from the ELF segment "
|
||||
"containing the entry point (Unchecked)."));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.customAddressRange->hide();
|
||||
m_ui.customAddressRangeLineEdits->hide();
|
||||
}
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(
|
||||
sif, m_ui.grayOutOverwrittenFunctions, "Debugger/Analysis", "GenerateFunctionHashes", true);
|
||||
|
||||
m_dialog->registerWidgetHelp(m_ui.grayOutOverwrittenFunctions, tr("Gray Out Symbols For Overwritten Functions"), tr("Checked"),
|
||||
tr("Generate hashes for all the detected functions, and gray out the symbols displayed in the debugger for "
|
||||
"functions that no longer match."));
|
||||
|
||||
connect(m_ui.automaticallyClearSymbols, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
||||
connect(m_ui.demangleSymbols, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
||||
connect(m_ui.customAddressRange, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::updateEnabledStates);
|
||||
|
||||
updateEnabledStates();
|
||||
}
|
||||
|
||||
void DebugAnalysisSettingsWidget::parseSettingsFromWidgets(Pcsx2Config::DebugAnalysisOptions& output)
|
||||
{
|
||||
output.AutomaticallySelectSymbolsToClear = m_ui.automaticallyClearSymbols->isChecked();
|
||||
|
||||
for (const auto& [name, temp] : m_symbol_sources)
|
||||
{
|
||||
DebugSymbolSource& source = output.SymbolSources.emplace_back();
|
||||
source.Name = name;
|
||||
source.ClearDuringAnalysis = temp.check_box->isChecked();
|
||||
}
|
||||
|
||||
output.ImportSymbolsFromELF = m_ui.importFromElf->isChecked();
|
||||
output.ImportSymFileFromDefaultLocation = m_ui.importSymFileFromDefaultLocation->isChecked();
|
||||
output.DemangleSymbols = m_ui.demangleSymbols->isChecked();
|
||||
output.DemangleParameters = m_ui.demangleParameters->isChecked();
|
||||
|
||||
for (int i = 0; i < m_ui.symbolFileList->count(); i++)
|
||||
{
|
||||
DebugExtraSymbolFile& file = output.ExtraSymbolFiles.emplace_back();
|
||||
file.Path = m_ui.symbolFileList->item(i)->text().toStdString();
|
||||
}
|
||||
|
||||
output.FunctionScanMode = static_cast<DebugFunctionScanMode>(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()
|
||||
{
|
||||
QGridLayout* layout = new QGridLayout(m_ui.symbolSourceGrid);
|
||||
|
||||
if (!m_dialog || m_dialog->getSerial() == QtHost::GetCurrentGameSerial().toStdString())
|
||||
{
|
||||
// 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);
|
||||
|
||||
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);
|
||||
|
||||
SymbolSourceTemp& source = m_symbol_sources[name];
|
||||
source.previous_value = value;
|
||||
source.modified_by_user = true;
|
||||
}
|
||||
|
||||
// Add any more symbol sources for which the user hasn't made a
|
||||
// selection. These are separate since we don't want to have to store
|
||||
// configuration data for them.
|
||||
R5900SymbolGuardian.Read([&](const ccc::SymbolDatabase& database) {
|
||||
for (const ccc::SymbolSource& symbol_source : database.symbol_sources)
|
||||
{
|
||||
if (m_symbol_sources.find(symbol_source.name()) == m_symbol_sources.end() && symbol_source.name() != "Built-In")
|
||||
{
|
||||
SymbolSourceTemp& source = m_symbol_sources[symbol_source.name()];
|
||||
source.previous_value = SymbolImporter::ShouldClearSymbolsFromSourceByDefault(symbol_source.name());
|
||||
source.modified_by_user = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (m_symbol_sources.empty())
|
||||
{
|
||||
m_ui.symbolSourceErrorMessage->setText(tr("<i>No symbol sources in database.</i>"));
|
||||
m_ui.symbolSourceScrollArea->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the check boxes.
|
||||
int i = 0;
|
||||
for (auto& [name, temp] : m_symbol_sources)
|
||||
{
|
||||
temp.check_box = new QCheckBox(QString::fromStdString(name));
|
||||
temp.check_box->setChecked(temp.previous_value);
|
||||
layout->addWidget(temp.check_box, i / 2, i % 2);
|
||||
|
||||
connect(temp.check_box, &QCheckBox::checkStateChanged, this, &DebugAnalysisSettingsWidget::symbolSourceCheckStateChanged);
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.symbolSourceErrorMessage->setText(tr("<i>Start this game to modify the symbol sources list.</i>"));
|
||||
m_ui.symbolSourceScrollArea->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
m_ui.symbolSourceErrorMessage->hide();
|
||||
}
|
||||
|
||||
void DebugAnalysisSettingsWidget::symbolSourceCheckStateChanged()
|
||||
{
|
||||
QComboBox* combo_box = qobject_cast<QComboBox*>(sender());
|
||||
if (!combo_box)
|
||||
return;
|
||||
|
||||
auto temp = m_symbol_sources.find(combo_box->currentText().toStdString());
|
||||
if (temp == m_symbol_sources.end())
|
||||
return;
|
||||
|
||||
temp->second.modified_by_user = true;
|
||||
|
||||
saveSymbolSources();
|
||||
}
|
||||
|
||||
void DebugAnalysisSettingsWidget::saveSymbolSources()
|
||||
{
|
||||
if (!m_dialog)
|
||||
return;
|
||||
|
||||
SettingsInterface* sif = m_dialog->getSettingsInterface();
|
||||
if (!sif)
|
||||
return;
|
||||
|
||||
// Clean up old configuration entries.
|
||||
int old_count = sif->GetIntValue("Debugger/Analysis/SymbolSources", "Count");
|
||||
for (int i = 0; i < old_count; i++)
|
||||
{
|
||||
std::string section = "Debugger/Analysis/SymbolSources/" + std::to_string(i);
|
||||
sif->RemoveSection(section.c_str());
|
||||
}
|
||||
|
||||
sif->RemoveSection("Debugger/Analysis/SymbolSources");
|
||||
|
||||
int symbol_sources_to_save = 0;
|
||||
for (auto& [name, temp] : m_symbol_sources)
|
||||
if (temp.modified_by_user)
|
||||
symbol_sources_to_save++;
|
||||
|
||||
if (symbol_sources_to_save == 0)
|
||||
return;
|
||||
|
||||
// Make new configuration entries.
|
||||
sif->SetIntValue("Debugger/Analysis/SymbolSources", "Count", symbol_sources_to_save);
|
||||
|
||||
int i = 0;
|
||||
for (auto& [name, temp] : m_symbol_sources)
|
||||
{
|
||||
if (!temp.modified_by_user)
|
||||
continue;
|
||||
|
||||
std::string section = "Debugger/Analysis/SymbolSources/" + std::to_string(i);
|
||||
sif->SetBoolValue(section.c_str(), "ClearDuringAnalysis", temp.check_box->isChecked());
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
connect(m_ui.addSymbolFile, &QPushButton::clicked, this, &DebugAnalysisSettingsWidget::addSymbolFile);
|
||||
connect(m_ui.removeSymbolFile, &QPushButton::clicked, this, &DebugAnalysisSettingsWidget::removeSymbolFile);
|
||||
}
|
||||
|
||||
void DebugAnalysisSettingsWidget::addSymbolFile()
|
||||
{
|
||||
QString path = QFileDialog::getOpenFileName(this, tr("Add Symbol File"));
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
|
||||
m_ui.symbolFileList->addItem(path);
|
||||
|
||||
saveSymbolFiles();
|
||||
}
|
||||
|
||||
void DebugAnalysisSettingsWidget::removeSymbolFile()
|
||||
{
|
||||
for (QListWidgetItem* item : m_ui.symbolFileList->selectedItems())
|
||||
delete item;
|
||||
|
||||
saveSymbolFiles();
|
||||
}
|
||||
|
||||
void DebugAnalysisSettingsWidget::saveSymbolFiles()
|
||||
{
|
||||
if (!m_dialog)
|
||||
return;
|
||||
|
||||
SettingsInterface* sif = m_dialog->getSettingsInterface();
|
||||
if (!sif)
|
||||
return;
|
||||
|
||||
// Clean up old configuration entries.
|
||||
int old_count = sif->GetIntValue("Debugger/Analysis/ExtraSymbolFiles", "Count");
|
||||
for (int i = 0; i < old_count; i++)
|
||||
{
|
||||
std::string section = "Debugger/Analysis/ExtraSymbolFiles/" + std::to_string(i);
|
||||
sif->RemoveSection(section.c_str());
|
||||
}
|
||||
|
||||
sif->RemoveSection("Debugger/Analysis/ExtraSymbolFiles");
|
||||
|
||||
if (m_ui.symbolFileList->count() == 0)
|
||||
return;
|
||||
|
||||
// Make new configuration entries.
|
||||
sif->SetIntValue("Debugger/Analysis/ExtraSymbolFiles", "Count", m_ui.symbolFileList->count());
|
||||
|
||||
for (int i = 0; i < m_ui.symbolFileList->count(); 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());
|
||||
}
|
||||
|
||||
QtHost::SaveGameSettings(sif, true);
|
||||
g_emu_thread->reloadGameSettings();
|
||||
}
|
||||
|
||||
void DebugAnalysisSettingsWidget::functionScanRangeChanged()
|
||||
{
|
||||
if (!m_dialog)
|
||||
return;
|
||||
|
||||
SettingsInterface* sif = m_dialog->getSettingsInterface();
|
||||
if (!sif)
|
||||
return;
|
||||
|
||||
QString start_address = m_ui.addressRangeStart->text();
|
||||
QString end_address = m_ui.addressRangeEnd->text();
|
||||
|
||||
bool ok;
|
||||
|
||||
if (start_address.toUInt(&ok, 16), ok)
|
||||
sif->SetStringValue("Debugger/Analysis", "FunctionScanStartAddress", start_address.toStdString().c_str());
|
||||
|
||||
if (end_address.toUInt(&ok, 16), ok)
|
||||
sif->SetStringValue("Debugger/Analysis", "FunctionScanEndAddress", end_address.toStdString().c_str());
|
||||
}
|
||||
|
||||
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.customAddressRangeLineEdits->setEnabled(m_ui.customAddressRange->isChecked());
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
#include "ui_DebugAnalysisSettingsWidget.h"
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
class SettingsWindow;
|
||||
|
||||
class DebugAnalysisSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// Create a widget that will discard any settings changed after it is
|
||||
// closed, for use in the dialog opened by the "Reanalyze" button.
|
||||
DebugAnalysisSettingsWidget(QWidget* parent = nullptr);
|
||||
|
||||
// Create a widget that will write back any settings changed to the config
|
||||
// system, for use in the settings dialog.
|
||||
DebugAnalysisSettingsWidget(SettingsWindow* dialog, QWidget* parent = nullptr);
|
||||
|
||||
// Read all the analysis settings from the widget tree and store them in the
|
||||
// output object. This is used by the analysis options dialog to start an
|
||||
// analysis run manually.
|
||||
void parseSettingsFromWidgets(Pcsx2Config::DebugAnalysisOptions& output);
|
||||
|
||||
protected:
|
||||
void setupSymbolSourceGrid();
|
||||
void symbolSourceCheckStateChanged();
|
||||
void saveSymbolSources();
|
||||
|
||||
void setupSymbolFileList();
|
||||
void addSymbolFile();
|
||||
void removeSymbolFile();
|
||||
void saveSymbolFiles();
|
||||
|
||||
void functionScanRangeChanged();
|
||||
|
||||
void updateEnabledStates();
|
||||
|
||||
struct SymbolSourceTemp
|
||||
{
|
||||
QCheckBox* check_box = nullptr;
|
||||
bool previous_value = false;
|
||||
bool modified_by_user = false;
|
||||
};
|
||||
|
||||
SettingsWindow* m_dialog = nullptr;
|
||||
std::map<std::string, SymbolSourceTemp> m_symbol_sources;
|
||||
|
||||
Ui::DebugAnalysisSettingsWidget m_ui;
|
||||
};
|
|
@ -0,0 +1,407 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DebugAnalysisSettingsWidget</class>
|
||||
<widget class="QWidget" name="DebugAnalysisSettingsWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>500</width>
|
||||
<height>750</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="clearExistingSymbolsGroup">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Clear Existing Symbols</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="automaticallyClearSymbols">
|
||||
<property name="text">
|
||||
<string>Automatically Select Symbols To Clear</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="symbolSourceScrollArea">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>150</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="symbolSourceGrid">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>480</width>
|
||||
<height>83</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="symbolSourceErrorMessage">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p><br/></p></body></html></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="importSymbolTablesGroup">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Import Symbols</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QWidget" name="importSymbolTableCheckBoxes" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="importSymbolTableCheckBoxesLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="importFromElf">
|
||||
<property name="text">
|
||||
<string>Import From ELF</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="demangleSymbols">
|
||||
<property name="text">
|
||||
<string>Demangle Symbols</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QCheckBox" name="demangleParameters">
|
||||
<property name="text">
|
||||
<string>Demangle Parameters</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="importSymFileFromDefaultLocation">
|
||||
<property name="text">
|
||||
<string>Import Default .sym File</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="symbolFileLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Import from file (.elf, .sym, etc):</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListWidget" name="symbolFileList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="importSymbolFileButtons" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="importSymbolFileButtonsLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="importSymbolFileSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="addSymbolFile">
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeSymbolFile">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="scanForFunctionsGroup">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Scan For Functions</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="functionScanForm">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="functionScanLabel">
|
||||
<property name="text">
|
||||
<string>Scan Mode:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="functionScanMode">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Scan ELF</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Scan Memory</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Skip</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="customAddressRange">
|
||||
<property name="text">
|
||||
<string>Custom Address Range:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="customAddressRangeLineEdits" native="true">
|
||||
<layout class="QHBoxLayout" name="memoryRangeLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="startSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Maximum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="addressRangeStartLabel">
|
||||
<property name="text">
|
||||
<string>Start:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="addressRangeStart"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="addressRangeEndLabel">
|
||||
<property name="text">
|
||||
<string>End:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="addressRangeEnd"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="endSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Maximum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Hash Functions</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="grayOutOverwrittenFunctions">
|
||||
<property name="text">
|
||||
<string>Gray Out Symbols For Overwritten Functions</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -2,6 +2,8 @@
|
|||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "DebugSettingsWidget.h"
|
||||
|
||||
#include "DebugAnalysisSettingsWidget.h"
|
||||
#include "QtUtils.h"
|
||||
#include "SettingWidgetBinder.h"
|
||||
#include "SettingsWindow.h"
|
||||
|
@ -18,6 +20,27 @@ DebugSettingsWidget::DebugSettingsWidget(SettingsWindow* dialog, QWidget* parent
|
|||
|
||||
m_ui.setupUi(this);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Analysis Settings
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SettingWidgetBinder::BindWidgetToEnumSetting(
|
||||
sif, m_ui.analysisCondition, "Debugger/Analysis", "RunCondition",
|
||||
Pcsx2Config::DebugAnalysisOptions::RunConditionNames, DebugAnalysisCondition::IF_DEBUGGER_IS_OPEN);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(
|
||||
sif, m_ui.generateSymbolsForIRXExportTables, "Debugger/Analysis", "GenerateSymbolsForIRXExports", true);
|
||||
|
||||
dialog->registerWidgetHelp(m_ui.analysisCondition, tr("Analyze Program"), tr("If Debugger Is Open"),
|
||||
tr("Choose when the analysis passes should be run: Always (to save time when opening the debugger), If "
|
||||
"Debugger Is Open (to save memory if you never open the debugger), or Never."));
|
||||
dialog->registerWidgetHelp(m_ui.generateSymbolsForIRXExportTables, tr("Generate Symbols for IRX Export Tables"), tr("Checked"),
|
||||
tr("Hook IRX module loading/unloading and generate symbols for exported functions on the fly."));
|
||||
|
||||
m_analysis_settings = new DebugAnalysisSettingsWidget(dialog);
|
||||
|
||||
m_ui.analysisSettings->setLayout(new QVBoxLayout());
|
||||
m_ui.analysisSettings->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
m_ui.analysisSettings->layout()->addWidget(m_analysis_settings);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// GS Settings
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "ui_DebugSettingsWidget.h"
|
||||
|
||||
class SettingsWindow;
|
||||
class DebugAnalysisSettingsWidget;
|
||||
|
||||
class DebugSettingsWidget : public QWidget
|
||||
{
|
||||
|
@ -22,6 +23,7 @@ private Q_SLOTS:
|
|||
|
||||
private:
|
||||
SettingsWindow* m_dialog;
|
||||
DebugAnalysisSettingsWidget* m_analysis_settings;
|
||||
|
||||
Ui::DebugSettingsWidget m_ui;
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>861</width>
|
||||
<width>527</width>
|
||||
<height>501</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -24,20 +24,136 @@
|
|||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTabWidget" name="gsTab">
|
||||
<widget class="QTabWidget" name="tabs">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="analysisTabWidget">
|
||||
<attribute name="title">
|
||||
<string>Analysis</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="analysisScrollArea">
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="analysisScrollAreaContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>523</width>
|
||||
<height>464</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="analysisLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>These settings control what and when analysis passes should be performed on the program running in the virtual machine so that the resultant information can be shown in the debugger.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="analysisGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Analysis</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="analysisForm">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="analysisConditionLabel">
|
||||
<property name="text">
|
||||
<string>Automatically Analyze Program:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="analysisCondition">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Always</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>If Debugger Is Open</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Never</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="generateSymbolsForIRXExportTables">
|
||||
<property name="text">
|
||||
<string>Generate Symbols For IRX Exports</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="analysisSettings" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="gsTabWidget">
|
||||
<attribute name="title">
|
||||
<string>GS</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<widget class="QGroupBox" name="drawDumpingGroupBox">
|
||||
<property name="title">
|
||||
<string>Draw Dumping</string>
|
||||
</property>
|
||||
|
@ -175,19 +291,6 @@
|
|||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
|
|
@ -97,6 +97,7 @@
|
|||
<ClCompile Include="EarlyHardwareCheck.cpp" />
|
||||
<ClCompile Include="LogWindow.cpp" />
|
||||
<ClCompile Include="QtProgressCallback.cpp" />
|
||||
<ClCompile Include="Settings\DebugAnalysisSettingsWidget.cpp" />
|
||||
<ClCompile Include="Settings\DebugSettingsWidget.cpp" />
|
||||
<ClCompile Include="Settings\FolderSettingsWidget.cpp" />
|
||||
<ClCompile Include="Settings\GameCheatSettingsWidget.cpp" />
|
||||
|
@ -106,6 +107,7 @@
|
|||
<ClCompile Include="Themes.cpp" />
|
||||
<ClCompile Include="Tools\InputRecording\InputRecordingViewer.cpp" />
|
||||
<ClCompile Include="Tools\InputRecording\NewInputRecordingDlg.cpp" />
|
||||
<ClCompile Include="Debugger\AnalysisOptionsDialog.cpp" />
|
||||
<ClCompile Include="Debugger\CpuWidget.cpp" />
|
||||
<ClCompile Include="Debugger\DebuggerWindow.cpp" />
|
||||
<ClCompile Include="Debugger\DisassemblyWidget.cpp" />
|
||||
|
@ -192,6 +194,7 @@
|
|||
<QtMoc Include="Debugger\SymbolTree\TypeString.h" />
|
||||
<ClInclude Include="Settings\ControllerSettingWidgetBinder.h" />
|
||||
<QtMoc Include="Settings\FolderSettingsWidget.h" />
|
||||
<QtMoc Include="Settings\DebugAnalysisSettingsWidget.h" />
|
||||
<QtMoc Include="Settings\DebugSettingsWidget.h" />
|
||||
<QtMoc Include="Settings\GameCheatSettingsWidget.h" />
|
||||
<QtMoc Include="Settings\GamePatchSettingsWidget.h" />
|
||||
|
@ -206,6 +209,7 @@
|
|||
<QtMoc Include="Tools\InputRecording\NewInputRecordingDlg.h" />
|
||||
<QtMoc Include="Tools\InputRecording\InputRecordingViewer.h" />
|
||||
<ClInclude Include="QtUtils.h" />
|
||||
<QtMoc Include="Debugger\AnalysisOptionsDialog.h" />
|
||||
<QtMoc Include="Debugger\CpuWidget.h" />
|
||||
<QtMoc Include="Debugger\DebuggerWindow.h" />
|
||||
<QtMoc Include="Debugger\DisassemblyWidget.h" />
|
||||
|
@ -263,7 +267,9 @@
|
|||
<ClCompile Include="$(IntDir)Settings\moc_GameSummaryWidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)Settings\moc_AchievementLoginDialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)Settings\moc_AchievementSettingsWidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)Settings\moc_DebugAnalysisSettingsWidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)Settings\moc_DebugSettingsWidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)Debugger\moc_AnalysisOptionsDialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)Debugger\moc_DebuggerWindow.cpp" />
|
||||
<ClCompile Include="$(IntDir)Debugger\moc_CpuWidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)Debugger\moc_DisassemblyWidget.cpp" />
|
||||
|
@ -392,6 +398,9 @@
|
|||
<QtUi Include="Tools\InputRecording\InputRecordingViewer.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<QtUi Include="Debugger\AnalysisOptionsDialog.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<QtUi Include="Debugger\DebuggerWindow.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
|
@ -421,6 +430,9 @@
|
|||
<QtTs Include="Translations\pcsx2-qt_en.ts">
|
||||
<FileType>Document</FileType>
|
||||
</QtTs>
|
||||
<QtUi Include="Settings\DebugAnalysisSettingsWidget.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\DebugSettingsWidget.ui">
|
||||
<FileType>Document</FileType>
|
||||
</QtUi>
|
||||
|
|
|
@ -257,12 +257,21 @@
|
|||
<ClCompile Include="Tools\InputRecording\InputRecordingViewer.cpp">
|
||||
<Filter>Tools\Input Recording</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Settings\DebugAnalysisSettingsWidget.cpp">
|
||||
<Filter>Settings</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(IntDir)Settings\moc_DebugAnalysisSettingsWidget.cpp">
|
||||
<Filter>moc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Settings\DebugSettingsWidget.cpp">
|
||||
<Filter>Settings</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(IntDir)Settings\moc_DebugSettingsWidget.cpp">
|
||||
<Filter>moc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Debugger\AnalysisOptionsDialog.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Debugger\DebuggerWindow.cpp">
|
||||
<Filter>Debugger</Filter>
|
||||
</ClCompile>
|
||||
|
@ -296,6 +305,9 @@
|
|||
<ClCompile Include="Debugger\Models\SavedAddressesModel.cpp">
|
||||
<Filter>Debugger\Models</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(IntDir)Debugger\moc_AnalysisOptionsDialog.cpp">
|
||||
<Filter>moc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(IntDir)Debugger\moc_CpuWidget.cpp">
|
||||
<Filter>moc</Filter>
|
||||
</ClCompile>
|
||||
|
@ -520,9 +532,15 @@
|
|||
<Filter>Settings</Filter>
|
||||
</QtMoc>
|
||||
<QtMoc Include="Tools\InputRecording\InputRecordingViewer.h" />
|
||||
<QtMoc Include="Settings\DebugAnalysisSettingsWidget.h">
|
||||
<Filter>Settings</Filter>
|
||||
</QtMoc>
|
||||
<QtMoc Include="Settings\DebugSettingsWidget.h">
|
||||
<Filter>Settings</Filter>
|
||||
</QtMoc>
|
||||
<QtMoc Include="Debugger\AnalysisOptionsDialog.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</QtMoc>
|
||||
<QtMoc Include="Debugger\DebuggerWindow.h">
|
||||
<Filter>Debugger</Filter>
|
||||
</QtMoc>
|
||||
|
@ -665,9 +683,15 @@
|
|||
<QtUi Include="Settings\USBDeviceWidget.ui">
|
||||
<Filter>Settings</Filter>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\DebugAnalysisSettingsWidget.ui">
|
||||
<Filter>Settings</Filter>
|
||||
</QtUi>
|
||||
<QtUi Include="Settings\DebugSettingsWidget.ui">
|
||||
<Filter>Settings</Filter>
|
||||
</QtUi>
|
||||
<QtUi Include="Debugger\AnalysisOptionsDialog.ui">
|
||||
<Filter>Debugger</Filter>
|
||||
</QtUi>
|
||||
<QtUi Include="Debugger\DebuggerWindow.ui">
|
||||
<Filter>Debugger</Filter>
|
||||
</QtUi>
|
||||
|
|
|
@ -35,13 +35,13 @@ enum class CDVD_SourceType : uint8_t;
|
|||
|
||||
namespace Pad
|
||||
{
|
||||
enum class ControllerType : u8;
|
||||
enum class ControllerType : u8;
|
||||
}
|
||||
|
||||
/// Generic setting information which can be reused in multiple components.
|
||||
struct SettingInfo
|
||||
{
|
||||
using GetOptionsCallback = std::vector<std::pair<std::string, std::string>>(*)();
|
||||
using GetOptionsCallback = std::vector<std::pair<std::string, std::string>> (*)();
|
||||
|
||||
enum class Type
|
||||
{
|
||||
|
@ -190,6 +190,35 @@ enum class SpeedHack
|
|||
MaxCount,
|
||||
};
|
||||
|
||||
enum class DebugAnalysisCondition
|
||||
{
|
||||
ALWAYS,
|
||||
IF_DEBUGGER_IS_OPEN,
|
||||
NEVER
|
||||
};
|
||||
|
||||
struct DebugSymbolSource
|
||||
{
|
||||
std::string Name;
|
||||
bool ClearDuringAnalysis = false;
|
||||
|
||||
friend auto operator<=>(const DebugSymbolSource& lhs, const DebugSymbolSource& rhs) = default;
|
||||
};
|
||||
|
||||
struct DebugExtraSymbolFile
|
||||
{
|
||||
std::string Path;
|
||||
|
||||
friend auto operator<=>(const DebugExtraSymbolFile& lhs, const DebugExtraSymbolFile& rhs) = default;
|
||||
};
|
||||
|
||||
enum class DebugFunctionScanMode
|
||||
{
|
||||
SCAN_ELF,
|
||||
SCAN_MEMORY,
|
||||
SKIP
|
||||
};
|
||||
|
||||
enum class AspectRatioType : u8
|
||||
{
|
||||
Stretch,
|
||||
|
@ -647,7 +676,7 @@ struct Pcsx2Config
|
|||
OsdShowInputs : 1,
|
||||
OsdShowFrameTimes : 1,
|
||||
OsdShowVersion : 1,
|
||||
OsdShowVideoCapture: 1,
|
||||
OsdShowVideoCapture : 1,
|
||||
OsdShowInputRec : 1,
|
||||
OsdShowHardwareInfo : 1,
|
||||
HWSpinGPUForReadbacks : 1,
|
||||
|
@ -1007,6 +1036,7 @@ struct Pcsx2Config
|
|||
u32 WindowHeight;
|
||||
u32 MemoryViewBytesPerRow;
|
||||
|
||||
|
||||
DebugOptions();
|
||||
void LoadSave(SettingsWrapper& wrap);
|
||||
|
||||
|
@ -1014,6 +1044,37 @@ struct Pcsx2Config
|
|||
bool operator!=(const DebugOptions& right) const;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
struct DebugAnalysisOptions
|
||||
{
|
||||
|
||||
static const char* RunConditionNames[];
|
||||
static const char* FunctionScanModeNames[];
|
||||
|
||||
DebugAnalysisCondition RunCondition = DebugAnalysisCondition::IF_DEBUGGER_IS_OPEN;
|
||||
bool GenerateSymbolsForIRXExports = true;
|
||||
|
||||
bool AutomaticallySelectSymbolsToClear = true;
|
||||
std::vector<DebugSymbolSource> SymbolSources;
|
||||
|
||||
bool ImportSymbolsFromELF = true;
|
||||
bool ImportSymFileFromDefaultLocation = true;
|
||||
bool DemangleSymbols = true;
|
||||
bool DemangleParameters = true;
|
||||
std::vector<DebugExtraSymbolFile> ExtraSymbolFiles;
|
||||
|
||||
DebugFunctionScanMode FunctionScanMode = DebugFunctionScanMode::SCAN_ELF;
|
||||
bool CustomFunctionScanRange = false;
|
||||
std::string FunctionScanStartAddress = "0";
|
||||
std::string FunctionScanEndAddress = "0";
|
||||
|
||||
bool GenerateFunctionHashes = true;
|
||||
|
||||
void LoadSave(SettingsWrapper& wrap);
|
||||
|
||||
friend auto operator<=>(const DebugAnalysisOptions& lhs, const DebugAnalysisOptions& rhs) = default;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
struct EmulationSpeedOptions
|
||||
{
|
||||
|
@ -1149,10 +1210,10 @@ struct Pcsx2Config
|
|||
{
|
||||
SavestateOptions();
|
||||
void LoadSave(SettingsWrapper& wrap);
|
||||
|
||||
|
||||
SavestateCompressionMethod CompressionType = SavestateCompressionMethod::Zstandard;
|
||||
SavestateCompressionLevel CompressionRatio = SavestateCompressionLevel::Medium;
|
||||
|
||||
|
||||
bool operator==(const SavestateOptions& right) const;
|
||||
bool operator!=(const SavestateOptions& right) const;
|
||||
};
|
||||
|
@ -1192,6 +1253,7 @@ struct Pcsx2Config
|
|||
GamefixOptions Gamefixes;
|
||||
ProfilerOptions Profiler;
|
||||
DebugOptions Debugger;
|
||||
DebugAnalysisOptions DebuggerAnalysis;
|
||||
EmulationSpeedOptions EmulationSpeed;
|
||||
SavestateOptions Savestate;
|
||||
SPU2Options SPU2;
|
||||
|
|
|
@ -894,9 +894,9 @@ SymbolGuardian& R5900DebugInterface::GetSymbolGuardian() const
|
|||
return R5900SymbolGuardian;
|
||||
}
|
||||
|
||||
SymbolImporter& R5900DebugInterface::GetSymbolImporter() const
|
||||
SymbolImporter* R5900DebugInterface::GetSymbolImporter() const
|
||||
{
|
||||
return R5900SymbolImporter;
|
||||
return &R5900SymbolImporter;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<BiosThread>> R5900DebugInterface::GetThreadList() const
|
||||
|
@ -1218,9 +1218,9 @@ SymbolGuardian& R3000DebugInterface::GetSymbolGuardian() const
|
|||
return R3000SymbolGuardian;
|
||||
}
|
||||
|
||||
SymbolImporter& R3000DebugInterface::GetSymbolImporter() const
|
||||
SymbolImporter* R3000DebugInterface::GetSymbolImporter() const
|
||||
{
|
||||
return R3000SymbolImporter;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<BiosThread>> R3000DebugInterface::GetThreadList() const
|
||||
|
|
|
@ -83,7 +83,7 @@ public:
|
|||
virtual u32 getCycles() = 0;
|
||||
virtual BreakPointCpu getCpuType() = 0;
|
||||
virtual SymbolGuardian& GetSymbolGuardian() const = 0;
|
||||
virtual SymbolImporter& GetSymbolImporter() const = 0;
|
||||
virtual SymbolImporter* GetSymbolImporter() const = 0;
|
||||
virtual std::vector<std::unique_ptr<BiosThread>> GetThreadList() const = 0;
|
||||
|
||||
bool evaluateExpression(const char* expression, u64& dest);
|
||||
|
@ -139,7 +139,7 @@ public:
|
|||
void setPc(u32 newPc) override;
|
||||
void setRegister(int cat, int num, u128 newValue) override;
|
||||
SymbolGuardian& GetSymbolGuardian() const override;
|
||||
SymbolImporter& GetSymbolImporter() const override;
|
||||
SymbolImporter* GetSymbolImporter() const override;
|
||||
std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
|
||||
|
||||
std::string disasm(u32 address, bool simplify) override;
|
||||
|
@ -182,7 +182,7 @@ public:
|
|||
void setPc(u32 newPc) override;
|
||||
void setRegister(int cat, int num, u128 newValue) override;
|
||||
SymbolGuardian& GetSymbolGuardian() const override;
|
||||
SymbolImporter& GetSymbolImporter() const override;
|
||||
SymbolImporter* GetSymbolImporter() const override;
|
||||
std::vector<std::unique_ptr<BiosThread>> GetThreadList() const override;
|
||||
|
||||
std::string disasm(u32 address, bool simplify) override;
|
||||
|
|
|
@ -308,7 +308,7 @@ namespace MIPSAnalyst
|
|||
currentFunction.end = addr + 4;
|
||||
functions.push_back(currentFunction);
|
||||
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Analysis");
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Function Scanner");
|
||||
if (!source.success()) {
|
||||
Console.Error("MIPSAnalyst: %s", source.error().message.c_str());
|
||||
return;
|
||||
|
|
|
@ -8,13 +8,13 @@ SymbolGuardian R3000SymbolGuardian;
|
|||
|
||||
void SymbolGuardian::Read(ReadCallback callback) const noexcept
|
||||
{
|
||||
std::shared_lock l(m_big_symbol_lock);
|
||||
std::shared_lock lock(m_big_symbol_lock);
|
||||
callback(m_database);
|
||||
}
|
||||
|
||||
void SymbolGuardian::ReadWrite(ReadWriteCallback callback) noexcept
|
||||
{
|
||||
std::unique_lock l(m_big_symbol_lock);
|
||||
std::unique_lock lock(m_big_symbol_lock);
|
||||
callback(m_database);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "common/Console.h"
|
||||
#include "common/Error.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Threading.h"
|
||||
|
||||
|
@ -22,7 +23,6 @@
|
|||
|
||||
#include <demangle.h>
|
||||
|
||||
SymbolImporter R3000SymbolImporter(R3000SymbolGuardian);
|
||||
SymbolImporter R5900SymbolImporter(R5900SymbolGuardian);
|
||||
|
||||
struct DefaultBuiltInType
|
||||
|
@ -84,7 +84,40 @@ SymbolImporter::SymbolImporter(SymbolGuardian& guardian)
|
|||
void SymbolImporter::OnElfChanged(std::vector<u8> elf, const std::string& elf_file_name)
|
||||
{
|
||||
Reset();
|
||||
AnalyseElf(std::move(elf), elf_file_name);
|
||||
|
||||
if (EmuConfig.DebuggerAnalysis.RunCondition == DebugAnalysisCondition::NEVER)
|
||||
{
|
||||
m_symbol_table_loaded_on_boot = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_debugger_open && EmuConfig.DebuggerAnalysis.RunCondition == DebugAnalysisCondition::IF_DEBUGGER_IS_OPEN)
|
||||
{
|
||||
m_symbol_table_loaded_on_boot = false;
|
||||
return;
|
||||
}
|
||||
|
||||
AnalyseElf(std::move(elf), elf_file_name, EmuConfig.DebuggerAnalysis);
|
||||
|
||||
m_symbol_table_loaded_on_boot = true;
|
||||
}
|
||||
|
||||
void SymbolImporter::OnDebuggerOpened()
|
||||
{
|
||||
m_debugger_open = true;
|
||||
|
||||
if (EmuConfig.DebuggerAnalysis.RunCondition == DebugAnalysisCondition::NEVER)
|
||||
return;
|
||||
|
||||
if (m_symbol_table_loaded_on_boot)
|
||||
return;
|
||||
|
||||
LoadAndAnalyseElf(EmuConfig.DebuggerAnalysis);
|
||||
}
|
||||
|
||||
void SymbolImporter::OnDebuggerClosed()
|
||||
{
|
||||
m_debugger_open = false;
|
||||
}
|
||||
|
||||
void SymbolImporter::Reset()
|
||||
|
@ -94,7 +127,7 @@ void SymbolImporter::Reset()
|
|||
m_guardian.ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
database.clear();
|
||||
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Built-in");
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Built-In");
|
||||
if (!source.success())
|
||||
return;
|
||||
|
||||
|
@ -116,7 +149,24 @@ void SymbolImporter::Reset()
|
|||
});
|
||||
}
|
||||
|
||||
void SymbolImporter::AnalyseElf(std::vector<u8> elf, const std::string& elf_file_name)
|
||||
void SymbolImporter::LoadAndAnalyseElf(Pcsx2Config::DebugAnalysisOptions options)
|
||||
{
|
||||
const std::string& elf_path = VMManager::GetCurrentELF();
|
||||
|
||||
Error error;
|
||||
ElfObject elfo;
|
||||
if (elf_path.empty() || !cdvdLoadElf(&elfo, elf_path, false, &error))
|
||||
{
|
||||
if (!elf_path.empty())
|
||||
Console.Error(fmt::format("Failed to read ELF for symbol import: {}: {}", elf_path, error.GetDescription()));
|
||||
return;
|
||||
}
|
||||
|
||||
AnalyseElf(elfo.ReleaseData(), elf_path, options);
|
||||
}
|
||||
|
||||
void SymbolImporter::AnalyseElf(
|
||||
std::vector<u8> elf, const std::string& elf_file_name, Pcsx2Config::DebugAnalysisOptions options)
|
||||
{
|
||||
// Search for a .sym file to load symbols from.
|
||||
std::string nocash_path;
|
||||
|
@ -143,36 +193,33 @@ void SymbolImporter::AnalyseElf(std::vector<u8> elf, const std::string& elf_file
|
|||
|
||||
ShutdownWorkerThread();
|
||||
|
||||
m_import_thread = std::thread([this, nocash_path, worker_symbol_file = std::move(symbol_file)]() {
|
||||
m_import_thread = std::thread([this, nocash_path, options, worker_symbol_file = std::move(symbol_file)]() {
|
||||
Threading::SetNameOfCurrentThread("Symbol Worker");
|
||||
|
||||
ccc::SymbolDatabase temp_database;
|
||||
ImportSymbolTables(temp_database, worker_symbol_file, &m_interrupt_import_thread);
|
||||
|
||||
ImportSymbols(temp_database, worker_symbol_file, nocash_path, options, &m_interrupt_import_thread);
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
ImportNocashSymbols(temp_database, nocash_path);
|
||||
if (options.GenerateFunctionHashes)
|
||||
ComputeOriginalFunctionHashes(temp_database, worker_symbol_file.elf());
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
const ccc::ElfProgramHeader* entry_segment = worker_symbol_file.elf().entry_point_segment();
|
||||
if (entry_segment)
|
||||
{
|
||||
ElfMemoryReader reader(worker_symbol_file.elf());
|
||||
MIPSAnalyst::ScanForFunctions(temp_database, reader, entry_segment->vaddr, entry_segment->vaddr + entry_segment->filesz);
|
||||
}
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
ComputeOriginalFunctionHashes(temp_database, worker_symbol_file.elf());
|
||||
ScanForFunctions(temp_database, worker_symbol_file, options);
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
m_guardian.ReadWrite([&](ccc::SymbolDatabase& database) {
|
||||
ClearExistingSymbols(database, options);
|
||||
|
||||
if (m_interrupt_import_thread)
|
||||
return;
|
||||
|
||||
database.merge_from(temp_database);
|
||||
});
|
||||
});
|
||||
|
@ -180,55 +227,145 @@ void SymbolImporter::AnalyseElf(std::vector<u8> elf, const std::string& elf_file
|
|||
|
||||
void SymbolImporter::ShutdownWorkerThread()
|
||||
{
|
||||
m_interrupt_import_thread = true;
|
||||
if (m_import_thread.joinable())
|
||||
{
|
||||
m_interrupt_import_thread = true;
|
||||
m_import_thread.join();
|
||||
m_interrupt_import_thread = false;
|
||||
m_interrupt_import_thread = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ccc::ModuleHandle SymbolImporter::ImportSymbolTables(
|
||||
ccc::SymbolDatabase& database, const ccc::SymbolFile& symbol_file, const std::atomic_bool* interrupt)
|
||||
void SymbolImporter::ClearExistingSymbols(ccc::SymbolDatabase& database, const Pcsx2Config::DebugAnalysisOptions& options)
|
||||
{
|
||||
ccc::Result<std::vector<std::unique_ptr<ccc::SymbolTable>>> symbol_tables = symbol_file.get_all_symbol_tables();
|
||||
if (!symbol_tables.success())
|
||||
std::vector<ccc::SymbolSourceHandle> sources_to_destroy;
|
||||
for (const ccc::SymbolSource& source : database.symbol_sources)
|
||||
{
|
||||
ccc::report_error(symbol_tables.error());
|
||||
return ccc::ModuleHandle();
|
||||
bool should_destroy = ShouldClearSymbolsFromSourceByDefault(source.name());
|
||||
|
||||
for (const DebugSymbolSource& source_config : options.SymbolSources)
|
||||
if (source_config.Name == source.name())
|
||||
should_destroy = source_config.ClearDuringAnalysis;
|
||||
|
||||
if (should_destroy)
|
||||
sources_to_destroy.emplace_back(source.handle());
|
||||
}
|
||||
|
||||
for (ccc::SymbolSourceHandle handle : sources_to_destroy)
|
||||
database.destroy_symbols_from_source(handle, true);
|
||||
}
|
||||
|
||||
bool SymbolImporter::ShouldClearSymbolsFromSourceByDefault(const std::string& source_name)
|
||||
{
|
||||
return source_name.find("Symbol Table") != std::string::npos ||
|
||||
source_name == "ELF Section Headers" ||
|
||||
source_name == "Function Scanner" ||
|
||||
source_name == "Nocash Symbols";
|
||||
}
|
||||
|
||||
void SymbolImporter::ImportSymbols(
|
||||
ccc::SymbolDatabase& database,
|
||||
const ccc::ElfSymbolFile& elf,
|
||||
const std::string& nocash_path,
|
||||
const Pcsx2Config::DebugAnalysisOptions& options,
|
||||
const std::atomic_bool* interrupt)
|
||||
{
|
||||
ccc::DemanglerFunctions demangler;
|
||||
demangler.cplus_demangle = cplus_demangle;
|
||||
demangler.cplus_demangle_opname = cplus_demangle_opname;
|
||||
if (options.DemangleSymbols)
|
||||
{
|
||||
demangler.cplus_demangle = cplus_demangle;
|
||||
demangler.cplus_demangle_opname = cplus_demangle_opname;
|
||||
}
|
||||
|
||||
u32 importer_flags =
|
||||
ccc::DEMANGLE_PARAMETERS |
|
||||
ccc::DEMANGLE_RETURN_TYPE |
|
||||
ccc::NO_MEMBER_FUNCTIONS |
|
||||
ccc::NO_OPTIMIZED_OUT_FUNCTIONS |
|
||||
ccc::UNIQUE_FUNCTIONS;
|
||||
|
||||
ccc::Result<ccc::ModuleHandle> module_handle = ccc::import_symbol_tables(
|
||||
database, symbol_file.name(), *symbol_tables, importer_flags, demangler, interrupt);
|
||||
if (!module_handle.success())
|
||||
if (options.DemangleParameters)
|
||||
importer_flags |= ccc::DEMANGLE_PARAMETERS;
|
||||
|
||||
if (options.ImportSymbolsFromELF)
|
||||
{
|
||||
ccc::report_error(module_handle.error());
|
||||
return ccc::ModuleHandle();
|
||||
ccc::Result<std::vector<std::unique_ptr<ccc::SymbolTable>>> symbol_tables = elf.get_all_symbol_tables();
|
||||
if (!symbol_tables.success())
|
||||
{
|
||||
ccc::report_error(symbol_tables.error());
|
||||
}
|
||||
else
|
||||
{
|
||||
ccc::Result<ccc::ModuleHandle> module_handle = ccc::import_symbol_tables(
|
||||
database, elf.name(), *symbol_tables, importer_flags, demangler, interrupt);
|
||||
if (!module_handle.success())
|
||||
{
|
||||
ccc::report_error(module_handle.error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!nocash_path.empty() && options.ImportSymFileFromDefaultLocation)
|
||||
{
|
||||
if (!ImportNocashSymbols(database, nocash_path))
|
||||
Console.Error("Failed to read symbol file from default location '%s'.", nocash_path.c_str());
|
||||
}
|
||||
|
||||
for (const DebugExtraSymbolFile& extra_symbol_file : options.ExtraSymbolFiles)
|
||||
{
|
||||
if (*interrupt)
|
||||
return;
|
||||
|
||||
if (StringUtil::EndsWithNoCase(extra_symbol_file.Path, ".sym"))
|
||||
{
|
||||
if (!ImportNocashSymbols(database, extra_symbol_file.Path))
|
||||
Console.Error("Failed to read extra symbol file '%s'.", extra_symbol_file.Path.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
Error error;
|
||||
std::optional<std::vector<u8>> image = FileSystem::ReadBinaryFile(extra_symbol_file.Path.c_str());
|
||||
if (!image.has_value())
|
||||
{
|
||||
Console.Error("Failed to read extra symbol file '%s'.", extra_symbol_file.Path.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string file_name(Path::GetFileName(extra_symbol_file.Path));
|
||||
|
||||
ccc::Result<std::unique_ptr<ccc::SymbolFile>> 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<std::vector<std::unique_ptr<ccc::SymbolTable>>> symbol_tables = elf.get_all_symbol_tables();
|
||||
if (!symbol_tables.success())
|
||||
{
|
||||
ccc::report_error(symbol_tables.error());
|
||||
continue;
|
||||
}
|
||||
|
||||
ccc::Result<ccc::ModuleHandle> module_handle = ccc::import_symbol_tables(
|
||||
database, elf.name(), *symbol_tables, importer_flags, demangler, interrupt);
|
||||
if (!module_handle.success())
|
||||
{
|
||||
ccc::report_error(module_handle.error());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLn("Imported %d symbols.", database.symbol_count());
|
||||
|
||||
return *module_handle;
|
||||
return;
|
||||
}
|
||||
|
||||
bool SymbolImporter::ImportNocashSymbols(ccc::SymbolDatabase& database, const std::string& file_name)
|
||||
bool SymbolImporter::ImportNocashSymbols(ccc::SymbolDatabase& database, const std::string& file_path)
|
||||
{
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Nocash Symbols");
|
||||
if (!source.success())
|
||||
FILE* f = FileSystem::OpenCFile(file_path.c_str(), "r");
|
||||
if (!f)
|
||||
return false;
|
||||
|
||||
FILE* f = FileSystem::OpenCFile(file_name.c_str(), "r");
|
||||
if (!f)
|
||||
ccc::Result<ccc::SymbolSourceHandle> source = database.get_symbol_source("Nocash Symbols");
|
||||
if (!source.success())
|
||||
return false;
|
||||
|
||||
while (!feof(f))
|
||||
|
@ -344,6 +481,46 @@ bool SymbolImporter::ImportNocashSymbols(ccc::SymbolDatabase& database, const st
|
|||
return true;
|
||||
}
|
||||
|
||||
void SymbolImporter::ScanForFunctions(
|
||||
ccc::SymbolDatabase& database, const ccc::ElfSymbolFile& elf, const Pcsx2Config::DebugAnalysisOptions& options)
|
||||
{
|
||||
u32 start_address = 0;
|
||||
u32 end_address = 0;
|
||||
if (options.CustomFunctionScanRange)
|
||||
{
|
||||
start_address = static_cast<u32>(std::stoull(options.FunctionScanStartAddress.c_str(), nullptr, 16));
|
||||
end_address = static_cast<u32>(std::stoull(options.FunctionScanEndAddress.c_str(), nullptr, 16));
|
||||
}
|
||||
else
|
||||
{
|
||||
const ccc::ElfProgramHeader* entry_segment = elf.elf().entry_point_segment();
|
||||
if (!entry_segment)
|
||||
return;
|
||||
|
||||
start_address = entry_segment->vaddr;
|
||||
end_address = entry_segment->vaddr + entry_segment->filesz;
|
||||
}
|
||||
|
||||
switch (options.FunctionScanMode)
|
||||
{
|
||||
case DebugFunctionScanMode::SCAN_ELF:
|
||||
{
|
||||
ElfMemoryReader reader(elf.elf());
|
||||
MIPSAnalyst::ScanForFunctions(database, reader, start_address, end_address);
|
||||
break;
|
||||
}
|
||||
case DebugFunctionScanMode::SCAN_MEMORY:
|
||||
{
|
||||
MIPSAnalyst::ScanForFunctions(database, r5900Debug, start_address, end_address);
|
||||
break;
|
||||
}
|
||||
case DebugFunctionScanMode::SKIP:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolImporter::ComputeOriginalFunctionHashes(ccc::SymbolDatabase& database, const ccc::ElfFile& elf)
|
||||
{
|
||||
for (ccc::Function& function : database.functions)
|
||||
|
|
|
@ -3,16 +3,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Config.h"
|
||||
#include "SymbolGuardian.h"
|
||||
|
||||
class DebugInterface;
|
||||
|
||||
struct SymbolImporterOptions
|
||||
{
|
||||
std::vector<ccc::SymbolSourceHandle> symbols_to_destroy;
|
||||
|
||||
};
|
||||
|
||||
class SymbolImporter
|
||||
{
|
||||
public:
|
||||
|
@ -22,23 +17,37 @@ public:
|
|||
// that are used to determine when symbol tables should be loaded, and
|
||||
// should be called from the CPU thread.
|
||||
void OnElfChanged(std::vector<u8> elf, const std::string& elf_file_name);
|
||||
|
||||
void AutoAnalyse();
|
||||
void OnDebuggerOpened();
|
||||
void OnDebuggerClosed();
|
||||
|
||||
// Delete all stored symbols and create some default built-ins. Should be
|
||||
// called from the CPU thread.
|
||||
void Reset();
|
||||
|
||||
// Load the current ELF file and call AnalyseElf on it. Should be called
|
||||
// from the CPU thread.
|
||||
void LoadAndAnalyseElf(Pcsx2Config::DebugAnalysisOptions options);
|
||||
|
||||
// Import symbols from the ELF file, nocash symbols, and scan for functions.
|
||||
// Should be called from the CPU thread.
|
||||
void AnalyseElf(std::vector<u8> elf, const std::string& elf_file_name);
|
||||
void AnalyseElf(std::vector<u8> elf, const std::string& elf_file_name, Pcsx2Config::DebugAnalysisOptions options);
|
||||
|
||||
// Interrupt the import thread. Should be called from the CPU thread.
|
||||
void ShutdownWorkerThread();
|
||||
|
||||
static ccc::ModuleHandle ImportSymbolTables(
|
||||
ccc::SymbolDatabase& database, const ccc::SymbolFile& symbol_file, const std::atomic_bool* interrupt);
|
||||
static bool ImportNocashSymbols(ccc::SymbolDatabase& database, const std::string& file_name);
|
||||
static void ClearExistingSymbols(ccc::SymbolDatabase& database, const Pcsx2Config::DebugAnalysisOptions& options);
|
||||
static bool ShouldClearSymbolsFromSourceByDefault(const std::string& source_name);
|
||||
|
||||
static void ImportSymbols(
|
||||
ccc::SymbolDatabase& database,
|
||||
const ccc::ElfSymbolFile& elf,
|
||||
const std::string& nocash_path,
|
||||
const Pcsx2Config::DebugAnalysisOptions& options,
|
||||
const std::atomic_bool* interrupt);
|
||||
static bool ImportNocashSymbols(ccc::SymbolDatabase& database, const std::string& file_path);
|
||||
|
||||
static void ScanForFunctions(
|
||||
ccc::SymbolDatabase& database, const ccc::ElfSymbolFile& elf, const Pcsx2Config::DebugAnalysisOptions& options);
|
||||
|
||||
// Compute original hashes for all the functions based on the code stored in
|
||||
// the ELF file.
|
||||
|
@ -51,9 +60,11 @@ public:
|
|||
protected:
|
||||
SymbolGuardian& m_guardian;
|
||||
|
||||
bool m_symbol_table_loaded_on_boot = false;
|
||||
bool m_debugger_open = false;
|
||||
|
||||
std::thread m_import_thread;
|
||||
std::atomic_bool m_interrupt_import_thread = false;
|
||||
};
|
||||
|
||||
extern SymbolImporter R3000SymbolImporter;
|
||||
extern SymbolImporter R5900SymbolImporter;
|
||||
|
|
|
@ -1086,6 +1086,9 @@ namespace R3000A
|
|||
|
||||
void LoadFuncs(u32 a0reg)
|
||||
{
|
||||
if (!EmuConfig.DebuggerAnalysis.GenerateSymbolsForIRXExports)
|
||||
return;
|
||||
|
||||
const std::string modname = iopMemReadString(a0reg + 12, 8);
|
||||
s32 version_major = iopMemRead8(a0reg + 9);
|
||||
s32 version_minor = iopMemRead8(a0reg + 8);
|
||||
|
|
|
@ -324,7 +324,7 @@ void Pcsx2Config::SpeedhackOptions::LoadSave(SettingsWrapper& wrap)
|
|||
EECycleSkip = std::min(EECycleSkip, MAX_EE_CYCLE_SKIP);
|
||||
}
|
||||
|
||||
Pcsx2Config::ProfilerOptions::ProfilerOptions()
|
||||
Pcsx2Config::ProfilerOptions::ProfilerOptions()
|
||||
: bitset(0xfffffffe)
|
||||
{
|
||||
}
|
||||
|
@ -587,7 +587,7 @@ const char* Pcsx2Config::GSOptions::GetRendererName(GSRendererType type)
|
|||
{
|
||||
switch (type)
|
||||
{
|
||||
// clang-format off
|
||||
// clang-format off
|
||||
case GSRendererType::Auto: return "Auto";
|
||||
case GSRendererType::DX11: return "Direct3D 11";
|
||||
case GSRendererType::DX12: return "Direct3D 12";
|
||||
|
@ -1025,7 +1025,7 @@ bool Pcsx2Config::GSOptions::UseHardwareRenderer() const
|
|||
|
||||
static constexpr const std::array s_spu2_sync_mode_names = {
|
||||
"Disabled",
|
||||
"TimeStretch"
|
||||
"TimeStretch",
|
||||
};
|
||||
static constexpr const std::array s_spu2_sync_mode_display_names = {
|
||||
TRANSLATE_NOOP("Pcsx2Config", "Disabled (Noisy)"),
|
||||
|
@ -1111,7 +1111,7 @@ void Pcsx2Config::SPU2Options::LoadSave(SettingsWrapper& wrap)
|
|||
SettingsWrapEntry(DeviceName);
|
||||
StreamParameters.LoadSave(wrap, CURRENT_SETTINGS_SECTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Pcsx2Config::SPU2Options::operator!=(const SPU2Options& right) const
|
||||
{
|
||||
|
@ -1338,7 +1338,7 @@ void Pcsx2Config::GamefixOptions::Set(GamefixId id, bool enabled)
|
|||
{
|
||||
switch (id)
|
||||
{
|
||||
// clang-format off
|
||||
// clang-format off
|
||||
case Fix_VuAddSub: VuAddSubHack = enabled; break;
|
||||
case Fix_FpuMultiply: FpuMulHack = enabled; break;
|
||||
case Fix_XGKick: XgKickHack = enabled; break;
|
||||
|
@ -1376,7 +1376,7 @@ bool Pcsx2Config::GamefixOptions::Get(GamefixId id) const
|
|||
{
|
||||
switch (id)
|
||||
{
|
||||
// clang-format off
|
||||
// clang-format off
|
||||
case Fix_VuAddSub: return VuAddSubHack;
|
||||
case Fix_FpuMultiply: return FpuMulHack;
|
||||
case Fix_XGKick: return XgKickHack;
|
||||
|
@ -1425,7 +1425,6 @@ void Pcsx2Config::GamefixOptions::LoadSave(SettingsWrapper& wrap)
|
|||
SettingsWrapBitBool(FullVU0SyncHack);
|
||||
}
|
||||
|
||||
|
||||
Pcsx2Config::DebugOptions::DebugOptions()
|
||||
{
|
||||
ShowDebuggerOnStart = false;
|
||||
|
@ -1457,7 +1456,92 @@ bool Pcsx2Config::DebugOptions::operator!=(const DebugOptions& right) const
|
|||
|
||||
bool Pcsx2Config::DebugOptions::operator==(const DebugOptions& right) const
|
||||
{
|
||||
return OpEqu(bitset) && OpEqu(FontWidth) && OpEqu(FontHeight) && OpEqu(WindowWidth) && OpEqu(WindowHeight) && OpEqu(MemoryViewBytesPerRow);
|
||||
return OpEqu(bitset) &&
|
||||
OpEqu(FontWidth) &&
|
||||
OpEqu(FontHeight) &&
|
||||
OpEqu(WindowWidth) &&
|
||||
OpEqu(WindowHeight) &&
|
||||
OpEqu(MemoryViewBytesPerRow);
|
||||
}
|
||||
|
||||
const char* Pcsx2Config::DebugAnalysisOptions::RunConditionNames[] = {
|
||||
"Always",
|
||||
"If Debugger Is Open",
|
||||
"Never",
|
||||
nullptr,
|
||||
};
|
||||
|
||||
const char* Pcsx2Config::DebugAnalysisOptions::FunctionScanModeNames[] = {
|
||||
"Scan From ELF",
|
||||
"Scan From Memory",
|
||||
"Skip",
|
||||
nullptr,
|
||||
};
|
||||
|
||||
void Pcsx2Config::DebugAnalysisOptions::LoadSave(SettingsWrapper& wrap)
|
||||
{
|
||||
{
|
||||
SettingsWrapSection("Debugger/Analysis");
|
||||
|
||||
SettingsWrapEnumEx(RunCondition, "RunCondition", RunConditionNames);
|
||||
SettingsWrapBitBool(GenerateSymbolsForIRXExports);
|
||||
|
||||
SettingsWrapBitBool(AutomaticallySelectSymbolsToClear);
|
||||
|
||||
SettingsWrapBitBool(ImportSymbolsFromELF);
|
||||
SettingsWrapBitBool(DemangleSymbols);
|
||||
SettingsWrapBitBool(DemangleParameters);
|
||||
|
||||
SettingsWrapEnumEx(FunctionScanMode, "FunctionScanMode", FunctionScanModeNames);
|
||||
SettingsWrapBitBool(CustomFunctionScanRange);
|
||||
SettingsWrapEntry(FunctionScanStartAddress);
|
||||
SettingsWrapEntry(FunctionScanEndAddress);
|
||||
|
||||
SettingsWrapBitBool(GenerateFunctionHashes);
|
||||
}
|
||||
|
||||
int symbolSourceCount = static_cast<int>(SymbolSources.size());
|
||||
{
|
||||
SettingsWrapSection("Debugger/Analysis/SymbolSources");
|
||||
SettingsWrapEntryEx(symbolSourceCount, "Count");
|
||||
}
|
||||
|
||||
for (int i = 0; i < symbolSourceCount; i++)
|
||||
{
|
||||
std::string section = "Debugger/Analysis/SymbolSources/" + std::to_string(i);
|
||||
SettingsWrapSection(section.c_str());
|
||||
|
||||
DebugSymbolSource Source;
|
||||
if (wrap.IsSaving())
|
||||
Source = SymbolSources[i];
|
||||
|
||||
SettingsWrapEntryEx(Source.Name, "Name");
|
||||
SettingsWrapBitBoolEx(Source.ClearDuringAnalysis, "ClearDuringAnalysis");
|
||||
|
||||
if (wrap.IsLoading())
|
||||
SymbolSources.emplace_back(std::move(Source));
|
||||
}
|
||||
|
||||
int extraSymbolFileCount = static_cast<int>(ExtraSymbolFiles.size());
|
||||
{
|
||||
SettingsWrapSection("Debugger/Analysis/ExtraSymbolFiles");
|
||||
SettingsWrapEntryEx(extraSymbolFileCount, "Count");
|
||||
}
|
||||
|
||||
for (int i = 0; i < extraSymbolFileCount; i++)
|
||||
{
|
||||
std::string section = "Debugger/Analysis/ExtraSymbolFiles/" + std::to_string(i);
|
||||
SettingsWrapSection(section.c_str());
|
||||
|
||||
DebugExtraSymbolFile file;
|
||||
if (wrap.IsSaving())
|
||||
file = ExtraSymbolFiles[i];
|
||||
|
||||
SettingsWrapEntryEx(file.Path, "Path");
|
||||
|
||||
if (wrap.IsLoading())
|
||||
ExtraSymbolFiles.emplace_back(std::move(file));
|
||||
}
|
||||
}
|
||||
|
||||
Pcsx2Config::SavestateOptions::SavestateOptions()
|
||||
|
@ -1778,6 +1862,7 @@ void Pcsx2Config::LoadSaveCore(SettingsWrapper& wrap)
|
|||
Savestate.LoadSave(wrap);
|
||||
|
||||
Debugger.LoadSave(wrap);
|
||||
DebuggerAnalysis.LoadSave(wrap);
|
||||
Trace.LoadSave(wrap);
|
||||
|
||||
Achievements.LoadSave(wrap);
|
||||
|
@ -1905,13 +1990,13 @@ void EmuFolders::SetAppRoot()
|
|||
|
||||
bool EmuFolders::SetResourcesDirectory()
|
||||
{
|
||||
#ifndef __APPLE__
|
||||
#ifndef PCSX2_APP_DATADIR
|
||||
// On Windows/Linux, these are in the binary directory.
|
||||
Resources = Path::Combine(AppRoot, "resources");
|
||||
#else
|
||||
Resources = Path::Canonicalize(Path::Combine(AppRoot, PCSX2_APP_DATADIR "/resources"));
|
||||
#endif
|
||||
#ifndef __APPLE__
|
||||
#ifndef PCSX2_APP_DATADIR
|
||||
// On Windows/Linux, these are in the binary directory.
|
||||
Resources = Path::Combine(AppRoot, "resources");
|
||||
#else
|
||||
Resources = Path::Canonicalize(Path::Combine(AppRoot, PCSX2_APP_DATADIR "/resources"));
|
||||
#endif
|
||||
#else
|
||||
// On macOS, this is in the bundle resources directory.
|
||||
const std::string program_path = FileSystem::GetProgramPath();
|
||||
|
|
|
@ -447,7 +447,6 @@ void VMManager::Internal::CPUThreadShutdown()
|
|||
// Ensure emulog gets flushed.
|
||||
Log::SetFileOutputLevel(LOGLEVEL_NONE, std::string());
|
||||
|
||||
R3000SymbolImporter.ShutdownWorkerThread();
|
||||
R5900SymbolImporter.ShutdownWorkerThread();
|
||||
}
|
||||
|
||||
|
@ -1413,9 +1412,9 @@ bool VMManager::Initialize(VMBootParameters boot_params)
|
|||
|
||||
Achievements::ConfirmHardcoreModeDisableAsync(trigger,
|
||||
[boot_params = std::move(boot_params)](bool approved) mutable {
|
||||
if (approved && Initialize(std::move(boot_params)))
|
||||
SetState(VMState::Running);
|
||||
});
|
||||
if (approved && Initialize(std::move(boot_params)))
|
||||
SetState(VMState::Running);
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -3098,7 +3097,7 @@ void VMManager::WarnAboutUnsafeSettings()
|
|||
append(ICON_FA_TACHOMETER_ALT,
|
||||
TRANSLATE_SV("VMManager", "Cycle rate/skip is not at default, this may crash or make games run too slow."));
|
||||
}
|
||||
|
||||
|
||||
const bool is_sw_renderer = EmuConfig.GS.Renderer == GSRendererType::SW;
|
||||
if (!is_sw_renderer)
|
||||
{
|
||||
|
@ -3615,7 +3614,7 @@ void VMManager::UpdateDiscordPresence(bool update_session_time)
|
|||
rp.largeImageKey = "4k-pcsx2";
|
||||
rp.largeImageText = "PCSX2 PS2 Emulator";
|
||||
rp.startTimestamp = s_discord_presence_time_epoch;
|
||||
rp.details = s_title.empty() ? TRANSLATE("VMManager","No Game Running") : s_title.c_str();
|
||||
rp.details = s_title.empty() ? TRANSLATE("VMManager", "No Game Running") : s_title.c_str();
|
||||
|
||||
std::string state_string;
|
||||
|
||||
|
|
Loading…
Reference in New Issue