diff options
Diffstat (limited to 'Source/Core/DolphinQt2/Debugger/JITWidget.cpp')
| -rw-r--r-- | Source/Core/DolphinQt2/Debugger/JITWidget.cpp | 205 |
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); +} |
