diff --git a/Source/Core/Core/PowerPC/JitInterface.cpp b/Source/Core/Core/PowerPC/JitInterface.cpp index e5dab22d75..f76011ec8a 100644 --- a/Source/Core/Core/PowerPC/JitInterface.cpp +++ b/Source/Core/Core/PowerPC/JitInterface.cpp @@ -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 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); diff --git a/Source/Core/Core/PowerPC/JitInterface.h b/Source/Core/Core/PowerPC/JitInterface.h index 08208f2280..bc5601a769 100644 --- a/Source/Core/Core/PowerPC/JitInterface.h +++ b/Source/Core/Core/PowerPC/JitInterface.h @@ -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); diff --git a/Source/Core/Core/PowerPC/Profiler.h b/Source/Core/Core/PowerPC/Profiler.h index 3df533de29..e3785847fe 100644 --- a/Source/Core/Core/PowerPC/Profiler.h +++ b/Source/Core/Core/PowerPC/Profiler.h @@ -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 block_stats; + u64 cost_sum; + u64 timecost_sum; + u64 countsPerSec; +}; namespace Profiler { diff --git a/Source/Core/DolphinWX/Debugger/JitWindow.cpp b/Source/Core/DolphinWX/Debugger/JitWindow.cpp index 1e78d76eca..bf13cd7f08 100644 --- a/Source/Core/DolphinWX/Debugger/JitWindow.cpp +++ b/Source/Core/DolphinWX/Debugger/JitWindow.cpp @@ -7,14 +7,6 @@ #include // Bochs #include -#if defined(HAS_LLVM) -// PowerPC.h defines PC. -// This conflicts with a function that has an argument named PC -#undef PC -#include -#include -#endif - #include #include #include @@ -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); diff --git a/Source/Core/DolphinWX/Debugger/JitWindow.h b/Source/Core/DolphinWX/Debugger/JitWindow.h index f9daeed550..e49bbe68af 100644 --- a/Source/Core/DolphinWX/Debugger/JitWindow.h +++ b/Source/Core/DolphinWX/Debugger/JitWindow.h @@ -4,7 +4,6 @@ #pragma once -#include // Bochs #include #include @@ -12,6 +11,7 @@ #include #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 { diff --git a/Source/Core/UICommon/CMakeLists.txt b/Source/Core/UICommon/CMakeLists.txt index 59e9443da7..65630b6b8e 100644 --- a/Source/Core/UICommon/CMakeLists.txt +++ b/Source/Core/UICommon/CMakeLists.txt @@ -1,4 +1,5 @@ -set(SRCS UICommon.cpp) +set(SRCS Disassembler.cpp + UICommon.cpp) set(LIBS common) diff --git a/Source/Core/UICommon/Disassembler.cpp b/Source/Core/UICommon/Disassembler.cpp new file mode 100644 index 0000000000..795ff57307 --- /dev/null +++ b/Source/Core/UICommon/Disassembler.cpp @@ -0,0 +1,176 @@ +#include // Bochs + +#if defined(HAS_LLVM) +// PowerPC.h defines PC. +// This conflicts with a function that has an argument named PC +#undef PC +#include +#include +#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); +} diff --git a/Source/Core/UICommon/Disassembler.h b/Source/Core/UICommon/Disassembler.h new file mode 100644 index 0000000000..af3cf6f388 --- /dev/null +++ b/Source/Core/UICommon/Disassembler.h @@ -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); diff --git a/Source/Core/UICommon/UICommon.vcxproj b/Source/Core/UICommon/UICommon.vcxproj index ca9a9e4dbc..cd542b24c1 100644 --- a/Source/Core/UICommon/UICommon.vcxproj +++ b/Source/Core/UICommon/UICommon.vcxproj @@ -44,11 +44,13 @@ + + - \ No newline at end of file +