Break out the disassembler code from the WXWidgets UI.

This cleans up some of the code between core and UI for disassembling and dumping code blocks.
Should help the QT UI in bringing up its debug UI since it won't have to deal with this garbage now.
This commit is contained in:
Ryan Houdek 2015-05-31 16:21:47 -05:00
parent 210d7c4d54
commit d0ae3f7d24
9 changed files with 300 additions and 238 deletions

View file

@ -116,20 +116,44 @@ namespace JitInterface
void WriteProfileResults(const std::string& filename)
{
ProfileStats prof_stats;
GetProfileResults(&prof_stats);
File::IOFile f(filename, "w");
if (!f)
{
PanicAlert("Failed to open %s", filename.c_str());
return;
}
fprintf(f.GetHandle(), "origAddr\tblkName\tcost\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(ms)\tblkCodeSize\n");
for (auto& stat : prof_stats.block_stats)
{
std::string name = g_symbolDB.GetDescription(stat.addr);
double percent = 100.0 * (double)stat.cost / (double)prof_stats.cost_sum;
double timePercent = 100.0 * (double)stat.tick_counter / (double)prof_stats.timecost_sum;
fprintf(f.GetHandle(), "%08x\t%s\t%" PRIu64 "\t%" PRIu64 "\t%.2f\t%.2f\t%.2f\t%i\n",
stat.addr, name.c_str(), stat.cost,
stat.tick_counter, percent, timePercent,
(double)stat.tick_counter*1000.0/(double)prof_stats.countsPerSec, stat.block_size);
}
}
void GetProfileResults(ProfileStats* prof_stats)
{
prof_stats->cost_sum = 0;
prof_stats->timecost_sum = 0;
prof_stats->block_stats.clear();
prof_stats->block_stats.reserve(jit->GetBlockCache()->GetNumBlocks());
// Can't really do this with no jit core available
if (!jit)
return;
PowerPC::CPUState old_state = PowerPC::GetState();
if (old_state == PowerPC::CPUState::CPU_RUNNING)
PowerPC::Pause();
Core::EState old_state = Core::GetState();
if (old_state == Core::CORE_RUN)
Core::SetState(Core::CORE_PAUSE);
std::vector<BlockStat> stats;
stats.reserve(jit->GetBlockCache()->GetNumBlocks());
u64 cost_sum = 0;
u64 timecost_sum = 0;
u64 countsPerSec;
QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec);
QueryPerformanceFrequency((LARGE_INTEGER*)&prof_stats->countsPerSec);
for (int i = 0; i < jit->GetBlockCache()->GetNumBlocks(); i++)
{
const JitBlock *block = jit->GetBlockCache()->GetBlock(i);
@ -138,37 +162,60 @@ namespace JitInterface
u64 timecost = block->ticCounter;
// Todo: tweak.
if (block->runCount >= 1)
stats.emplace_back(i, cost);
cost_sum += cost;
timecost_sum += timecost;
prof_stats->block_stats.emplace_back(i, block->originalAddress,
cost, timecost, block->codeSize);
prof_stats->cost_sum += cost;
prof_stats->timecost_sum += timecost;
}
sort(stats.begin(), stats.end());
File::IOFile f(filename, "w");
if (!f)
sort(prof_stats->block_stats.begin(), prof_stats->block_stats.end());
if (old_state == Core::CORE_RUN)
Core::SetState(Core::CORE_RUN);
}
int GetHostCode(u32* address, const u8** code, u32* code_size)
{
if (!jit)
{
PanicAlert("Failed to open %s", filename.c_str());
return;
*code_size = 0;
return 1;
}
fprintf(f.GetHandle(), "origAddr\tblkName\tcost\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(ms)\tblkCodeSize\n");
for (auto& stat : stats)
int block_num = jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address);
if (block_num < 0)
{
const JitBlock *block = jit->GetBlockCache()->GetBlock(stat.blockNum);
if (block)
for (int i = 0; i < 500; i++)
{
std::string name = g_symbolDB.GetDescription(block->originalAddress);
double percent = 100.0 * (double)stat.cost / (double)cost_sum;
double timePercent = 100.0 * (double)block->ticCounter / (double)timecost_sum;
fprintf(f.GetHandle(), "%08x\t%s\t%" PRIu64 "\t%" PRIu64 "\t%.2f\t%.2f\t%.2f\t%i\n",
block->originalAddress, name.c_str(), stat.cost,
block->ticCounter, percent, timePercent,
(double)block->ticCounter*1000.0/(double)countsPerSec, block->codeSize);
block_num = jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address - 4 * i);
if (block_num >= 0)
break;
}
if (block_num >= 0)
{
JitBlock* block = jit->GetBlockCache()->GetBlock(block_num);
if (!(block->originalAddress <= *address &&
block->originalSize + block->originalAddress >= *address))
block_num = -1;
}
// Do not merge this "if" with the above - block_num changes inside it.
if (block_num < 0)
{
*code_size = 0;
return 2;
}
}
if (old_state == PowerPC::CPUState::CPU_RUNNING)
PowerPC::Start();
JitBlock* block = jit->GetBlockCache()->GetBlock(block_num);
*code = (const u8*)jit->GetBlockCache()->GetCompiledCodeFromBlock(block_num);
*code_size = block->codeSize;
*address = block->originalAddress;
return 0;
}
bool HandleFault(uintptr_t access_address, SContext* ctx)
{
return jit->HandleFault(access_address, ctx);

View file

@ -8,6 +8,7 @@
#include "Common/ChunkFile.h"
#include "Core/MachineContext.h"
#include "Core/PowerPC/CPUCoreBase.h"
#include "Core/PowerPC/Profiler.h"
namespace JitInterface
{
@ -25,6 +26,8 @@ namespace JitInterface
// Debugging
void WriteProfileResults(const std::string& filename);
void GetProfileResults(ProfileStats* prof_stats);
int GetHostCode(u32* address, const u8** code, u32* code_size);
// Memory Utilities
bool HandleFault(uintptr_t access_address, SContext* ctx);

View file

@ -44,13 +44,24 @@
struct BlockStat
{
BlockStat(int bn, u64 c) : blockNum(bn), cost(c) {}
BlockStat(int bn, u32 _addr, u64 c, u64 ticks, u32 size) :
blockNum(bn), addr(_addr), cost(c), tick_counter(ticks), block_size(size) {}
int blockNum;
u32 addr;
u64 cost;
u64 tick_counter;
u32 block_size;
bool operator <(const BlockStat &other) const
{ return cost > other.cost; }
};
struct ProfileStats
{
std::vector<BlockStat> block_stats;
u64 cost_sum;
u64 timecost_sum;
u64 countsPerSec;
};
namespace Profiler
{

View file

@ -7,14 +7,6 @@
#include <disasm.h> // Bochs
#include <sstream>
#if defined(HAS_LLVM)
// PowerPC.h defines PC.
// This conflicts with a function that has an argument named PC
#undef PC
#include <llvm-c/Disassembler.h>
#include <llvm-c/Target.h>
#endif
#include <wx/button.h>
#include <wx/listctrl.h>
#include <wx/panel.h>
@ -23,177 +15,12 @@
#include "Common/CommonTypes.h"
#include "Common/GekkoDisassembler.h"
#include "Common/StringUtil.h"
#include "Core/PowerPC/Gekko.h"
#include "Core/PowerPC/PPCAnalyst.h"
#include "Core/PowerPC/JitCommon/JitBase.h"
#include "Core/PowerPC/JitCommon/JitCache.h"
#include "DolphinWX/Globals.h"
#include "DolphinWX/WxUtils.h"
#include "DolphinWX/Debugger/JitWindow.h"
#if defined(HAS_LLVM)
// This class declaration should be in the header
// Due to the conflict with the PC define and the function with PC as an argument
// it has to be in this file instead.
// Once that conflict is resolved this can be moved to the header
class HostDisassemblerLLVM : public HostDisassembler
{
public:
HostDisassemblerLLVM(const std::string host_disasm, int inst_size = -1, const std::string cpu = "");
~HostDisassemblerLLVM()
{
if (m_can_disasm)
LLVMDisasmDispose(m_llvm_context);
}
private:
bool m_can_disasm;
LLVMDisasmContextRef m_llvm_context;
int m_instruction_size;
std::string DisassembleHostBlock(const u8* code_start, const u32 code_size, u32* host_instructions_count) override;
};
HostDisassemblerLLVM::HostDisassemblerLLVM(const std::string host_disasm, int inst_size, const std::string cpu)
: m_can_disasm(false), m_instruction_size(inst_size)
{
LLVMInitializeAllTargetInfos();
LLVMInitializeAllTargetMCs();
LLVMInitializeAllDisassemblers();
m_llvm_context = LLVMCreateDisasmCPU(host_disasm.c_str(), cpu.c_str(), nullptr, 0, 0, nullptr);
// Couldn't create llvm context
if (!m_llvm_context)
return;
LLVMSetDisasmOptions(m_llvm_context,
LLVMDisassembler_Option_AsmPrinterVariant |
LLVMDisassembler_Option_PrintLatency);
m_can_disasm = true;
}
std::string HostDisassemblerLLVM::DisassembleHostBlock(const u8* code_start, const u32 code_size, u32 *host_instructions_count)
{
if (!m_can_disasm)
return "(No LLVM context)";
u8* disasmPtr = (u8*)code_start;
const u8 *end = code_start + code_size;
std::ostringstream x86_disasm;
while ((u8*)disasmPtr < end)
{
char inst_disasm[256];
size_t inst_size = LLVMDisasmInstruction(m_llvm_context, disasmPtr, (u64)(end - disasmPtr), (u64)disasmPtr, inst_disasm, 256);
if (!inst_size)
{
x86_disasm << "Invalid inst:";
if (m_instruction_size != -1)
{
// If we are on an architecture that has a fixed instruction size
// We can continue onward past this bad instruction.
std::string inst_str = "";
for (int i = 0; i < m_instruction_size; ++i)
inst_str += StringFromFormat("%02x", disasmPtr[i]);
x86_disasm << inst_str << std::endl;
disasmPtr += m_instruction_size;
}
else
{
// We can't continue if we are on an architecture that has flexible instruction sizes
// Dump the rest of the block instead
std::string code_block = "";
for (int i = 0; (disasmPtr + i) < end; ++i)
code_block += StringFromFormat("%02x", disasmPtr[i]);
x86_disasm << code_block << std::endl;
break;
}
}
else
{
x86_disasm << inst_disasm << std::endl;
disasmPtr += inst_size;
}
(*host_instructions_count)++;
}
return x86_disasm.str();
}
#endif
std::string HostDisassembler::DisassembleBlock(u32* address, u32* host_instructions_count, u32* code_size)
{
if (!jit)
{
*host_instructions_count = 0;
*code_size = 0;
return "(No JIT active)";
}
int block_num = jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address);
if (block_num < 0)
{
for (int i = 0; i < 500; i++)
{
block_num = jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address - 4 * i);
if (block_num >= 0)
break;
}
if (block_num >= 0)
{
JitBlock* block = jit->GetBlockCache()->GetBlock(block_num);
if (!(block->originalAddress <= *address &&
block->originalSize + block->originalAddress >= *address))
block_num = -1;
}
// Do not merge this "if" with the above - block_num changes inside it.
if (block_num < 0)
{
host_instructions_count = 0;
code_size = 0;
return "(No translation)";
}
}
JitBlock* block = jit->GetBlockCache()->GetBlock(block_num);
const u8* code = (const u8*)jit->GetBlockCache()->GetCompiledCodeFromBlock(block_num);
*code_size = block->codeSize;
*address = block->originalAddress;
return DisassembleHostBlock(code, block->codeSize, host_instructions_count);
}
HostDisassemblerX86::HostDisassemblerX86()
{
m_disasm.set_syntax_intel();
}
std::string HostDisassemblerX86::DisassembleHostBlock(const u8* code_start, const u32 code_size, u32* host_instructions_count)
{
u64 disasmPtr = (u64)code_start;
const u8* end = code_start + code_size;
std::ostringstream x86_disasm;
while ((u8*)disasmPtr < end)
{
char inst_disasm[256];
disasmPtr += m_disasm.disasm64(disasmPtr, disasmPtr, (u8*)disasmPtr, inst_disasm);
x86_disasm << inst_disasm << std::endl;
(*host_instructions_count)++;
}
return x86_disasm.str();
}
#include "UICommon/Disassembler.h"
CJitWindow::CJitWindow(wxWindow* parent, wxWindowID id, const wxPoint& pos,
const wxSize& size, long style, const wxString& name)
@ -219,16 +46,14 @@ CJitWindow::CJitWindow(wxWindow* parent, wxWindowID id, const wxPoint& pos,
sizerSplit->Fit(this);
sizerBig->Fit(this);
#if defined(_M_X86) && defined(HAS_LLVM)
m_disassembler.reset(new HostDisassemblerLLVM("x86_64-none-unknown"));
#elif defined(_M_X86)
m_disassembler.reset(new HostDisassemblerX86());
#elif defined(_M_ARM_64) && defined(HAS_LLVM)
m_disassembler.reset(new HostDisassemblerLLVM("aarch64-none-unknown", 4, "cortex-a57"));
#elif defined(_M_ARM_32) && defined(HAS_LLVM)
m_disassembler.reset(new HostDisassemblerLLVM("armv7-none-unknown", 4, "cortex-a15"));
#if defined(_M_X86)
m_disassembler.reset(GetNewDisassembler("x86"));
#elif defined(_M_ARM_64)
m_disassembler.reset(GetNewDisassembler("aarch64"));
#elif defined(_M_ARM_32)
m_disassembler.reset(GetNewDisassembler("armv7"));
#else
m_disassembler.reset(new HostDisassembler());
m_disassembler.reset(GetNewDisassembler("UNK"));
#endif
}
@ -250,7 +75,7 @@ void CJitWindow::Compare(u32 em_address)
u32 host_instructions_count = 0;
u32 host_code_size = 0;
std::string host_instructions_disasm;
host_instructions_disasm = m_disassembler->DisassembleBlock(&em_address, &host_instructions_count, &host_code_size);
host_instructions_disasm = DisassembleBlock(m_disassembler.get(), &em_address, &host_instructions_count, &host_code_size);
x86_box->SetValue(host_instructions_disasm);

View file

@ -4,7 +4,6 @@
#pragma once
#include <disasm.h> // Bochs
#include <memory>
#include <vector>
@ -12,6 +11,7 @@
#include <wx/panel.h>
#include "Common/CommonTypes.h"
#include "UICommon/Disassembler.h"
class wxButton;
class wxListBox;
@ -26,26 +26,6 @@ public:
void Update() override;
};
class HostDisassembler
{
public:
virtual ~HostDisassembler() {}
std::string DisassembleBlock(u32* address, u32* host_instructions_count, u32* code_size);
private:
virtual std::string DisassembleHostBlock(const u8* code_start, const u32 code_size, u32* host_instructions_count) { return "(No disassembler)"; }
};
class HostDisassemblerX86 : public HostDisassembler
{
public:
HostDisassemblerX86();
private:
disassembler m_disasm;
std::string DisassembleHostBlock(const u8* code_start, const u32 code_size, u32* host_instructions_count) override;
};
class CJitWindow : public wxPanel
{

View file

@ -1,4 +1,5 @@
set(SRCS UICommon.cpp)
set(SRCS Disassembler.cpp
UICommon.cpp)
set(LIBS common)

View file

@ -0,0 +1,176 @@
#include <disasm.h> // Bochs
#if defined(HAS_LLVM)
// PowerPC.h defines PC.
// This conflicts with a function that has an argument named PC
#undef PC
#include <llvm-c/Disassembler.h>
#include <llvm-c/Target.h>
#endif
#include "Common/StringUtil.h"
#include "Core/PowerPC/JitInterface.h"
#include "Core/PowerPC/JitCommon/JitBase.h"
#include "Core/PowerPC/JitCommon/JitCache.h"
#include "UICommon/Disassembler.h"
class HostDisassemblerX86 : public HostDisassembler
{
public:
HostDisassemblerX86();
private:
disassembler m_disasm;
std::string DisassembleHostBlock(const u8* code_start, const u32 code_size, u32* host_instructions_count) override;
};
#if defined(HAS_LLVM)
class HostDisassemblerLLVM : public HostDisassembler
{
public:
HostDisassemblerLLVM(const std::string host_disasm, int inst_size = -1, const std::string cpu = "");
~HostDisassemblerLLVM()
{
if (m_can_disasm)
LLVMDisasmDispose(m_llvm_context);
}
private:
bool m_can_disasm;
LLVMDisasmContextRef m_llvm_context;
int m_instruction_size;
std::string DisassembleHostBlock(const u8* code_start, const u32 code_size, u32* host_instructions_count) override;
};
HostDisassemblerLLVM::HostDisassemblerLLVM(const std::string host_disasm, int inst_size, const std::string cpu)
: m_can_disasm(false), m_instruction_size(inst_size)
{
LLVMInitializeAllTargetInfos();
LLVMInitializeAllTargetMCs();
LLVMInitializeAllDisassemblers();
m_llvm_context = LLVMCreateDisasmCPU(host_disasm.c_str(), cpu.c_str(), nullptr, 0, 0, nullptr);
// Couldn't create llvm context
if (!m_llvm_context)
return;
LLVMSetDisasmOptions(m_llvm_context,
LLVMDisassembler_Option_AsmPrinterVariant |
LLVMDisassembler_Option_PrintLatency);
m_can_disasm = true;
}
std::string HostDisassemblerLLVM::DisassembleHostBlock(const u8* code_start, const u32 code_size, u32 *host_instructions_count)
{
if (!m_can_disasm)
return "(No LLVM context)";
u8* disasmPtr = (u8*)code_start;
const u8 *end = code_start + code_size;
std::ostringstream x86_disasm;
while ((u8*)disasmPtr < end)
{
char inst_disasm[256];
size_t inst_size = LLVMDisasmInstruction(m_llvm_context, disasmPtr, (u64)(end - disasmPtr), (u64)disasmPtr, inst_disasm, 256);
if (!inst_size)
{
x86_disasm << "Invalid inst:";
if (m_instruction_size != -1)
{
// If we are on an architecture that has a fixed instruction size
// We can continue onward past this bad instruction.
std::string inst_str = "";
for (int i = 0; i < m_instruction_size; ++i)
inst_str += StringFromFormat("%02x", disasmPtr[i]);
x86_disasm << inst_str << std::endl;
disasmPtr += m_instruction_size;
}
else
{
// We can't continue if we are on an architecture that has flexible instruction sizes
// Dump the rest of the block instead
std::string code_block = "";
for (int i = 0; (disasmPtr + i) < end; ++i)
code_block += StringFromFormat("%02x", disasmPtr[i]);
x86_disasm << code_block << std::endl;
break;
}
}
else
{
x86_disasm << inst_disasm << std::endl;
disasmPtr += inst_size;
}
(*host_instructions_count)++;
}
return x86_disasm.str();
}
#endif
HostDisassemblerX86::HostDisassemblerX86()
{
m_disasm.set_syntax_intel();
}
std::string HostDisassemblerX86::DisassembleHostBlock(const u8* code_start, const u32 code_size, u32* host_instructions_count)
{
u64 disasmPtr = (u64)code_start;
const u8* end = code_start + code_size;
std::ostringstream x86_disasm;
while ((u8*)disasmPtr < end)
{
char inst_disasm[256];
disasmPtr += m_disasm.disasm64(disasmPtr, disasmPtr, (u8*)disasmPtr, inst_disasm);
x86_disasm << inst_disasm << std::endl;
(*host_instructions_count)++;
}
return x86_disasm.str();
}
HostDisassembler* GetNewDisassembler(const std::string& arch)
{
#if defined(HAS_LLVM)
if (arch == "x86")
return new HostDisassemblerLLVM("x86_64-none-unknown");
else if (arch == "aarch64")
return new HostDisassemblerLLVM("aarch64-none-unknown", 4, "cortex-a57");
else if (arch == "armv7")
return new HostDisassemblerLLVM("armv7-none-unknown", 4, "cortex-a15");
#elif defined(_M_X86)
if (arch == "x86")
new HostDisassemblerX86();
#endif
return new HostDisassembler();
}
std::string DisassembleBlock(HostDisassembler* disasm, u32* address, u32* host_instructions_count, u32* code_size)
{
const u8* code;
int res = JitInterface::GetHostCode(address, &code, code_size);
if (res == 1)
{
*host_instructions_count = 0;
return "(No JIT active)";
}
else if (res == 2)
{
host_instructions_count = 0;
return "(No translation)";
}
return disasm->DisassembleHostBlock(code, *code_size, host_instructions_count);
}

View file

@ -0,0 +1,17 @@
// Copyright 2008 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Common/CommonTypes.h"
class HostDisassembler
{
public:
virtual ~HostDisassembler() {}
virtual std::string DisassembleHostBlock(const u8* code_start, const u32 code_size, u32* host_instructions_count) { return "(No disassembler)"; }
};
HostDisassembler* GetNewDisassembler(const std::string& arch);
std::string DisassembleBlock(HostDisassembler* disasm, u32* address, u32* host_instructions_count, u32* code_size);

View file

@ -44,11 +44,13 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="UICommon.cpp" />
<ClCompile Include="Disassembler.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="UICommon.h" />
<ClInclude Include="Disassembler.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>