summaryrefslogtreecommitdiff
path: root/Source/Core/DolphinQt2/Debugger/JITWidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/Core/DolphinQt2/Debugger/JITWidget.cpp')
-rw-r--r--Source/Core/DolphinQt2/Debugger/JITWidget.cpp205
1 files changed, 205 insertions, 0 deletions
diff --git a/Source/Core/DolphinQt2/Debugger/JITWidget.cpp b/Source/Core/DolphinQt2/Debugger/JITWidget.cpp
new file mode 100644
index 0000000000..b0b4a772a5
--- /dev/null
+++ b/Source/Core/DolphinQt2/Debugger/JITWidget.cpp
@@ -0,0 +1,205 @@
+// Copyright 2018 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include "DolphinQt2/Debugger/JITWidget.h"
+
+#include <QPushButton>
+#include <QSplitter>
+#include <QTableWidget>
+#include <QTextBrowser>
+#include <QVBoxLayout>
+
+#include "Common/GekkoDisassembler.h"
+#include "Common/StringUtil.h"
+#include "Core/PowerPC/PPCAnalyst.h"
+#include "UICommon/Disassembler.h"
+
+#include "DolphinQt2/Settings.h"
+
+JITWidget::JITWidget(QWidget* parent) : QDockWidget(parent)
+{
+ setWindowTitle(tr("JIT Blocks"));
+ setAllowedAreas(Qt::AllDockWidgetAreas);
+
+ auto& settings = Settings::GetQSettings();
+
+ CreateWidgets();
+
+ restoreGeometry(settings.value(QStringLiteral("jitwidget/geometry")).toByteArray());
+ setFloating(settings.value(QStringLiteral("jitwidget/floating")).toBool());
+
+ m_table_splitter->restoreState(
+ settings.value(QStringLiteral("jitwidget/tablesplitter")).toByteArray());
+ m_asm_splitter->restoreState(
+ settings.value(QStringLiteral("jitwidget/asmsplitter")).toByteArray());
+
+ connect(&Settings::Instance(), &Settings::JITVisibilityChanged,
+ [this](bool visible) { setHidden(!visible); });
+
+ connect(&Settings::Instance(), &Settings::DebugModeToggled,
+ [this](bool enabled) { setHidden(!enabled || !Settings::Instance().IsJITVisible()); });
+
+ connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, &JITWidget::Update);
+
+ setHidden(!Settings::Instance().IsJITVisible() || !Settings::Instance().IsDebugModeEnabled());
+
+ ConnectWidgets();
+
+#if defined(_M_X86)
+ m_disassembler = GetNewDisassembler("x86");
+#elif defined(_M_ARM_64)
+ m_disassembler = GetNewDisassembler("aarch64");
+#else
+ m_disassembler = GetNewDisassembler("UNK");
+#endif
+
+ Update();
+}
+
+JITWidget::~JITWidget()
+{
+ auto& settings = Settings::GetQSettings();
+
+ settings.setValue(QStringLiteral("jitwidget/geometry"), saveGeometry());
+ settings.setValue(QStringLiteral("jitwidget/floating"), isFloating());
+ settings.setValue(QStringLiteral("jitwidget/tablesplitter"), m_table_splitter->saveState());
+ settings.setValue(QStringLiteral("jitwidget/asmsplitter"), m_asm_splitter->saveState());
+}
+
+void JITWidget::CreateWidgets()
+{
+ m_table_widget = new QTableWidget;
+
+ m_table_widget->setColumnCount(7);
+ m_table_widget->setHorizontalHeaderLabels(
+ {tr("Address"), tr("PPC Size"), tr("Host Size"),
+ // i18n: The symbolic name of a code block
+ tr("Symbol"),
+ // i18n: These are the kinds of flags that a CPU uses (e.g. carry),
+ // not the kinds of flags that represent e.g. countries
+ tr("Flags"),
+ // i18n: The number of times a code block has been executed
+ tr("NumExec"),
+ // i18n: Performance cost, not monetary cost
+ tr("Cost")});
+
+ m_ppc_asm_widget = new QTextBrowser;
+ m_host_asm_widget = new QTextBrowser;
+
+ m_table_splitter = new QSplitter(Qt::Vertical);
+ m_asm_splitter = new QSplitter(Qt::Horizontal);
+
+ m_refresh_button = new QPushButton(tr("Refresh"));
+
+ m_table_splitter->addWidget(m_table_widget);
+ m_table_splitter->addWidget(m_asm_splitter);
+
+ m_asm_splitter->addWidget(m_ppc_asm_widget);
+ m_asm_splitter->addWidget(m_host_asm_widget);
+
+ QWidget* widget = new QWidget;
+ auto* layout = new QVBoxLayout;
+ widget->setLayout(layout);
+
+ layout->addWidget(m_table_splitter);
+ layout->addWidget(m_refresh_button);
+
+ setWidget(widget);
+}
+
+void JITWidget::ConnectWidgets()
+{
+ connect(m_refresh_button, &QPushButton::pressed, this, &JITWidget::Update);
+}
+
+void JITWidget::Compare(u32 address)
+{
+ m_address = address;
+ Update();
+}
+
+void JITWidget::Update()
+{
+ if (!m_address)
+ {
+ m_ppc_asm_widget->setHtml(QStringLiteral("<i>%1</i>").arg(tr("(ppc)")));
+ m_host_asm_widget->setHtml(QStringLiteral("<i>%1</i>").arg(tr("(host)")));
+ return;
+ }
+
+ // TODO: Actually do something with the table (Wx doesn't)
+
+ // Get host side code disassembly
+ u32 host_instructions_count = 0;
+ u32 host_code_size = 0;
+ std::string host_instructions_disasm;
+ host_instructions_disasm =
+ DisassembleBlock(m_disassembler.get(), &m_address, &host_instructions_count, &host_code_size);
+
+ m_host_asm_widget->setHtml(
+ QStringLiteral("<pre>%1</pre>").arg(QString::fromStdString(host_instructions_disasm)));
+
+ // == Fill in ppc box
+ u32 ppc_addr = m_address;
+ PPCAnalyst::CodeBuffer code_buffer(32000);
+ PPCAnalyst::BlockStats st;
+ PPCAnalyst::BlockRegStats gpa;
+ PPCAnalyst::BlockRegStats fpa;
+ PPCAnalyst::CodeBlock code_block;
+ PPCAnalyst::PPCAnalyzer analyzer;
+ analyzer.SetOption(PPCAnalyst::PPCAnalyzer::OPTION_CONDITIONAL_CONTINUE);
+ analyzer.SetOption(PPCAnalyst::PPCAnalyzer::OPTION_BRANCH_FOLLOW);
+
+ code_block.m_stats = &st;
+ code_block.m_gpa = &gpa;
+ code_block.m_fpa = &fpa;
+
+ if (analyzer.Analyze(ppc_addr, &code_block, &code_buffer, 32000) != 0xFFFFFFFF)
+ {
+ std::ostringstream ppc_disasm;
+ for (u32 i = 0; i < code_block.m_num_instructions; i++)
+ {
+ const PPCAnalyst::CodeOp& op = code_buffer.codebuffer[i];
+ std::string opcode = GekkoDisassembler::Disassemble(op.inst.hex, op.address);
+ ppc_disasm << std::setfill('0') << std::setw(8) << std::hex << op.address;
+ ppc_disasm << " " << opcode << std::endl;
+ }
+
+ // Add stats to the end of the ppc box since it's generally the shortest.
+ ppc_disasm << std::dec << std::endl;
+
+ // Add some generic analysis
+ if (st.isFirstBlockOfFunction)
+ ppc_disasm << "(first block of function)" << std::endl;
+ if (st.isLastBlockOfFunction)
+ ppc_disasm << "(last block of function)" << std::endl;
+
+ ppc_disasm << st.numCycles << " estimated cycles" << std::endl;
+
+ ppc_disasm << "Num instr: PPC: " << code_block.m_num_instructions
+ << " Host: " << host_instructions_count << " (blowup: "
+ << 100 * host_instructions_count / code_block.m_num_instructions - 100 << "%)"
+ << std::endl;
+
+ ppc_disasm << "Num bytes: PPC: " << code_block.m_num_instructions * 4
+ << " Host: " << host_code_size
+ << " (blowup: " << 100 * host_code_size / (4 * code_block.m_num_instructions) - 100
+ << "%)" << std::endl;
+
+ m_ppc_asm_widget->setHtml(
+ QStringLiteral("<pre>%1</pre>").arg(QString::fromStdString(ppc_disasm.str())));
+ }
+ else
+ {
+ m_host_asm_widget->setHtml(
+ QStringLiteral("<pre>%1</pre>")
+ .arg(QString::fromStdString(StringFromFormat("(non-code address: %08x)", m_address))));
+ m_ppc_asm_widget->setHtml(QStringLiteral("<i>---</i>"));
+ }
+}
+
+void JITWidget::closeEvent(QCloseEvent*)
+{
+ Settings::Instance().SetJITVisible(false);
+}