diff --git a/CMakeLists.txt b/CMakeLists.txt index ad2be076bfc6..a7b642a3ecb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2342,6 +2342,8 @@ add_library(${CoreLibName} ${CoreLinkType} Core/MIPS/MIPSVFPUFallbacks.h Core/MIPS/MIPSAsm.cpp Core/MIPS/MIPSAsm.h + Core/MIPS/MIPSTracer.cpp + Core/MIPS/MIPSTracer.h Core/MemFault.cpp Core/MemFault.h Core/MemMap.cpp diff --git a/Core/Core.cpp b/Core/Core.cpp index 5993f44255fd..e30f5825d884 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -43,6 +43,7 @@ #include "Core/MIPS/MIPS.h" #include "Core/HLE/sceNetAdhoc.h" #include "GPU/Debugger/Stepping.h" +#include "Core/MIPS/MIPSTracer.h" #ifdef _WIN32 #include "Common/CommonWindows.h" @@ -334,6 +335,9 @@ bool Core_Run(GraphicsContext *ctx) { void Core_EnableStepping(bool step, const char *reason, u32 relatedAddress) { if (step) { + // Stop the tracer + mipsTracer.stop_tracing(); + Core_UpdateState(CORE_STEPPING); steppingCounter++; _assert_msg_(reason != nullptr, "No reason specified for break"); diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index af66592e0114..b73d036dafa4 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -992,6 +992,7 @@ + true true @@ -1406,6 +1407,7 @@ + true true diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 2fd6262e0155..e00f5b24ba78 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -123,6 +123,9 @@ MIPS + + MIPS + MIPS @@ -1347,6 +1350,9 @@ MIPS + + MIPS + MIPS diff --git a/Core/MIPS/IR/IRFrontend.cpp b/Core/MIPS/IR/IRFrontend.cpp index 024609bf7018..6c8e59fa3864 100644 --- a/Core/MIPS/IR/IRFrontend.cpp +++ b/Core/MIPS/IR/IRFrontend.cpp @@ -28,6 +28,9 @@ #include "Core/MIPS/IR/IRRegCache.h" #include "Core/MIPS/IR/IRPassSimplify.h" #include "Core/MIPS/IR/IRInterpreter.h" +#include "Core/MIPS/MIPSTracer.h" + +#include namespace MIPSComp { @@ -299,7 +302,17 @@ void IRFrontend::DoJit(u32 em_address, std::vector &instructions, u32 &m // logBlocks = 1; } - instructions = code->GetInstructions(); + if (!mipsTracer.tracing_enabled) { + instructions = code->GetInstructions(); + } + else { + std::vector block_instructions = code->GetInstructions(); + instructions.reserve(block_instructions.capacity()); + // The first instruction is "Downcount" + instructions.push_back(block_instructions.front()); + instructions.push_back({ IROp::LogIRBlock, 0, 0, 0, 0 }); + std::copy(block_instructions.begin() + 1, block_instructions.end(), std::back_inserter(instructions)); + } if (logBlocks > 0 && dontLogBlocks == 0) { char temp2[256]; diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index b591cfbd591e..13eb16a52f0f 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -189,6 +189,8 @@ static const IRMeta irMeta[] = { { IROp::RestoreRoundingMode, "RestoreRoundingMode", "" }, { IROp::ApplyRoundingMode, "ApplyRoundingMode", "" }, { IROp::UpdateRoundingMode, "UpdateRoundingMode", "" }, + + { IROp::LogIRBlock, "LogIRBlock", "" }, }; const IRMeta *metaIndex[256]; diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index a7762d08824a..0dbfa6790520 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -237,6 +237,9 @@ enum class IROp : uint8_t { ValidateAddress32, ValidateAddress128, + // Tracing support. + LogIRBlock, + Nop, Bad, }; diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 5af7b98dd602..0d482b485d9a 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -32,6 +32,7 @@ #include "Core/MIPS/IR/IRInst.h" #include "Core/MIPS/IR/IRInterpreter.h" #include "Core/System.h" +#include "Core/MIPS/MIPSTracer.h" #ifdef mips // Why do MIPS compilers define something so generic? Try to keep defined, at least... @@ -1236,6 +1237,11 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) { return mips->pc; } break; + case IROp::LogIRBlock: + if (mipsTracer.tracing_enabled) { + mipsTracer.executed_blocks.push_back(inst->constant); + } + break; case IROp::Nop: // TODO: This shouldn't crash, but for now we should not emit nops, so... case IROp::Bad: diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index f1d6724f4561..a06d76b65532 100644 --- a/Core/MIPS/IR/IRJit.cpp +++ b/Core/MIPS/IR/IRJit.cpp @@ -42,6 +42,8 @@ #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/Reporting.h" #include "Common/TimeUtil.h" +#include "Core/MIPS/MIPSTracer.h" + namespace MIPSComp { @@ -165,14 +167,20 @@ bool IRJit::CompileBlock(u32 em_address, std::vector &instructions, u32 } IRBlock *b = blocks_.GetBlock(block_num); - if (preload) { + if (preload || mipsTracer.tracing_enabled) { // Hash, then only update page stats, don't link yet. // TODO: Should we always hash? Then we can reuse blocks. b->UpdateHash(); } + if (!CompileNativeBlock(&blocks_, block_num, preload)) return false; - // Overwrites the first instruction, and also updates stats. + + if (mipsTracer.tracing_enabled) { + mipsTracer.prepare_block(b, blocks_); + } + + // Updates stats, also patches the first MIPS instruction into an emuhack if 'preload == false' blocks_.FinalizeBlock(block_num, preload); if (!preload) FinalizeNativeBlock(&blocks_, block_num); diff --git a/Core/MIPS/IR/IRJit.h b/Core/MIPS/IR/IRJit.h index 39d4f2a24578..c8ef2d5192c3 100644 --- a/Core/MIPS/IR/IRJit.h +++ b/Core/MIPS/IR/IRJit.h @@ -91,6 +91,9 @@ class IRBlock { u32 GetOriginalStart() const { return origAddr_; } + u64 GetHash() const { + return hash_; + } void Finalize(int number); void Destroy(int number); diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp new file mode 100644 index 000000000000..20289a5d926b --- /dev/null +++ b/Core/MIPS/MIPSTracer.cpp @@ -0,0 +1,207 @@ +// Copyright (c) 2024- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/MIPS/MIPSTracer.h" + +#include // for std::memcpy +#include "Core/MIPS/MIPSTables.h" // for MIPSDisAsm +#include "Core/MemMap.h" // for Memory::GetPointerUnchecked +#include "Common/File/FileUtil.h" // for the File::OpenCFile + + +bool TraceBlockStorage::save_block(const u32* instructions, u32 size) { + // 'size' is measured in bytes + const auto indexes_count = size / 4; + + if (cur_index + 1 + indexes_count >= raw_instructions.size()) { + return false; + } + + // Save the size first + *cur_data_ptr = size; + ++cur_data_ptr; + + // Now save the MIPS instructions + std::memcpy(cur_data_ptr, instructions, size); + cur_data_ptr += indexes_count; + + cur_index += 1 + indexes_count; + return true; +} + +void TraceBlockStorage::initialize(u32 capacity) { + raw_instructions.resize(capacity); + cur_index = 0; + cur_data_ptr = raw_instructions.data(); + INFO_LOG(Log::JIT, "TraceBlockStorage initialized: capacity=0x%x", capacity); +} + +void TraceBlockStorage::clear() { + raw_instructions.clear(); + cur_index = 0; + cur_data_ptr = nullptr; + INFO_LOG(Log::JIT, "TraceBlockStorage cleared"); +} + +void MIPSTracer::prepare_block(const MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks) { + u32 virt_addr, size; + block->GetRange(&virt_addr, &size); + + u64 hash = block->GetHash(); + auto it = hash_to_storage_index.find(hash); + + u32 storage_index; + if (it != hash_to_storage_index.end()) { + // We've seen this one before => it's saved in our storage + storage_index = it->second; + } + else { + // We haven't seen a block like that before, let's save it + auto mips_instructions_ptr = (const u32*)Memory::GetPointerUnchecked(virt_addr); + + storage_index = storage.cur_index; + if (!storage.save_block(mips_instructions_ptr, size)) { + // We ran out of storage! + WARN_LOG(Log::JIT, "The MIPSTracer ran out of storage for the blocks, cannot proceed!"); + stop_tracing(); + return; + } + // Successfully inserted the block at index 'storage_index'! + + hash_to_storage_index.emplace(hash, storage_index); + } + + // NB! + // If for some reason the blocks get invalidated while tracing, PPSSPP will be forced to recompile + // the same code again => the 'trace_info' will be filled with duplicates, because we can't detect that... + // If we store the TraceBlockInfo instances in an unordered_map, we won't be able to reference the entries + // by using the 4 byte IRInst field 'constant' (the iterators won't fit there). + // And, of course, doing a linear search in the vector is not worth the conserved space. + trace_info.push_back({ virt_addr, storage_index }); + + + u32 index = trace_info.size() - 1; + auto ir_ptr = (IRInst*)blocks.GetBlockInstructionPtr(*block); + ir_ptr[1].constant = index; +} + +bool MIPSTracer::flush_to_file() { + INFO_LOG(Log::JIT, "Flushing the trace to a file..."); + output = File::OpenCFile(logging_path, "w"); + + if (!output) { + WARN_LOG(Log::JIT, "MIPSTracer failed to open the file '%s'", logging_path.c_str()); + return false; + } + auto trace = executed_blocks.get_content(); + for (auto index : trace) { + auto& block_info = trace_info[index]; + flush_block_to_file(block_info); + } + + INFO_LOG(Log::JIT, "Trace flushed, closing the file..."); + std::fclose(output); + + clear(); + return true; +} + +void MIPSTracer::flush_block_to_file(const TraceBlockInfo& block_info) { + char buffer[512]; + + // The log format is '{prefix}{disassembled line}', where 'prefix' is '0x{8 hex digits of the address}: ' + const auto prefix_size = 2 + 8 + 2; + + u32 addr = block_info.virt_address; + u32 index = block_info.storage_index; + + u32 size = storage[index]; + ++index; + + u32 end_addr = addr + size; + + + for (; addr < end_addr; addr += 4, ++index) { + snprintf(buffer, sizeof(buffer), "0x%08x: ", addr); + MIPSDisAsm(storage.read_asm(index), addr, buffer + prefix_size, sizeof(buffer) - prefix_size, true); + + std::fprintf(output, "%s\n", buffer); + } +} + +void MIPSTracer::start_tracing() { + if (!tracing_enabled) { + INFO_LOG(Log::JIT, "MIPSTracer enabled"); + tracing_enabled = true; + } +} + +void MIPSTracer::stop_tracing() { + if (tracing_enabled) { + INFO_LOG(Log::JIT, "MIPSTracer disabled"); + tracing_enabled = false; + +#ifdef _DEBUG + print_stats(); +#endif + } +} + +inline void MIPSTracer::print_stats() const { + // First, the storage + INFO_LOG(Log::JIT, "=============== MIPSTracer storage ==============="); + INFO_LOG(Log::JIT, "Current index = %d, storage size = %d", storage.cur_index, storage.raw_instructions.size()); + + // Then the cyclic buffer + if (executed_blocks.overflow) { + INFO_LOG(Log::JIT, "=============== MIPSTracer cyclic buffer (overflow) ==============="); + INFO_LOG(Log::JIT, "Trace size = %d, starts from index %d", executed_blocks.buffer.size(), executed_blocks.current_index); + } + else { + INFO_LOG(Log::JIT, "=============== MIPSTracer cyclic buffer (no overflow) ==============="); + INFO_LOG(Log::JIT, "Trace size = %d, starts from index 0", executed_blocks.current_index); + } + // Next, the hash-to-index mapping + INFO_LOG(Log::JIT, "=============== MIPSTracer hashes ==============="); + INFO_LOG(Log::JIT, "Number of unique hashes = %d", hash_to_storage_index.size()); + + // Finally, the basic block list + INFO_LOG(Log::JIT, "=============== MIPSTracer basic block list ==============="); + INFO_LOG(Log::JIT, "Number of processed basic blocks = %d", trace_info.size()); + + INFO_LOG(Log::JIT, "=============== MIPSTracer stats end ==============="); +} + +void MIPSTracer::initialize(u32 storage_capacity, u32 max_trace_size) { + executed_blocks.resize(max_trace_size); + hash_to_storage_index.reserve(max_trace_size); + storage.initialize(storage_capacity); + trace_info.reserve(max_trace_size); + INFO_LOG(Log::JIT, "MIPSTracer initialized: storage_capacity=0x%x, max_trace_size=%d", storage_capacity, max_trace_size); +} + +void MIPSTracer::clear() { + executed_blocks.clear(); + hash_to_storage_index.clear(); + storage.clear(); + trace_info.clear(); + INFO_LOG(Log::JIT, "MIPSTracer cleared"); +} + +MIPSTracer mipsTracer; + + diff --git a/Core/MIPS/MIPSTracer.h b/Core/MIPS/MIPSTracer.h new file mode 100644 index 000000000000..8ba1bef6e854 --- /dev/null +++ b/Core/MIPS/MIPSTracer.h @@ -0,0 +1,172 @@ +// Copyright (c) 2024- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Core/Opcode.h" +#include "Core/MIPS/IR/IRJit.h" +#include "Common/Log.h" +#include "Common/File/Path.h" + + +struct TraceBlockInfo { + u32 virt_address; + u32 storage_index; +}; + +struct TraceBlockStorage { + std::vector raw_instructions; + u32 cur_index; + u32* cur_data_ptr; + + TraceBlockStorage(u32 capacity): + raw_instructions(capacity, 0), + cur_index(0), + cur_data_ptr(raw_instructions.data()) + {} + + TraceBlockStorage(): raw_instructions(), cur_index(0), cur_data_ptr(nullptr) {} + + bool save_block(const u32* instructions, u32 size); + + void initialize(u32 capacity); + void clear(); + + u32 operator[](u32 index) { + return raw_instructions[index]; + } + Memory::Opcode read_asm(u32 index) { + return Memory::Opcode(raw_instructions[index]); + } +}; + + +template +struct CyclicBuffer { + std::vector buffer; + u32 current_index; + bool overflow; + + explicit CyclicBuffer(u32 capacity) : buffer(capacity, T()), current_index(0), overflow(false) {} + + CyclicBuffer(): buffer(), current_index(0), overflow(false) {} + + void push_back(const T& value); + void push_back(T&& value); + + void clear(); + void resize(u32 new_capacity); + + std::vector get_content() const; +}; + +template +std::vector CyclicBuffer::get_content() const { + if (!overflow) { + return std::vector(buffer.begin(), buffer.begin() + current_index); + } + + std::vector ans; + ans.reserve(buffer.size()); + std::copy(buffer.begin() + current_index, buffer.end(), std::back_inserter(ans)); + std::copy(buffer.begin(), buffer.begin() + current_index, std::back_inserter(ans)); + return ans; +} + +template +void CyclicBuffer::push_back(const T& value) { + buffer[current_index] = value; + ++current_index; + if (current_index == buffer.size()) { + current_index = 0; + overflow = true; + } +} + +template +void CyclicBuffer::push_back(T&& value) { + buffer[current_index] = std::move(value); + ++current_index; + if (current_index == buffer.size()) { + current_index = 0; + overflow = true; + } +} + +template +void CyclicBuffer::clear() { + buffer.clear(); + current_index = 0; + overflow = false; +} + +template +void CyclicBuffer::resize(u32 new_capacity) { + buffer.resize(new_capacity); +} + + +// This system is meant for trace recording. +// A trace here stands for a sequence of instructions and their respective addresses in RAM. +// The register/memory changes (or thread switches) are not included! +// Note: the tracer stores the basic blocks inside, which causes the last block to be dumped as a whole, +// despite the fact that it may not have executed to its end by the time the tracer is stopped. +struct MIPSTracer { + std::vector trace_info; + + // The trace might be very big, in that case I don't mind losing the oldest entries. + CyclicBuffer executed_blocks; + + std::unordered_map hash_to_storage_index; + + TraceBlockStorage storage; + + Path logging_path; + FILE* output; + bool tracing_enabled = false; + + int in_storage_capacity = 0x10'0000; + int in_max_trace_size = 0x10'0000; + + void start_tracing(); + void stop_tracing(); + + void prepare_block(const MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks); + void set_logging_path(std::string path) { + logging_path = Path(path); + } + std::string get_logging_path() const { + return logging_path.ToString(); + } + + bool flush_to_file(); + void flush_block_to_file(const TraceBlockInfo& block); + + void initialize(u32 storage_capacity, u32 max_trace_size); + void clear(); + + inline void print_stats() const; + + MIPSTracer(): trace_info(), executed_blocks(), hash_to_storage_index(), storage(), logging_path() {} +}; + +extern MIPSTracer mipsTracer; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 229354f53c7e..2545438e7109 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -79,6 +79,9 @@ #include "GPU/GPUInterface.h" #include "GPU/Common/FramebufferManagerCommon.h" +#include "Core/Core.h" // for Core_IsStepping +#include "Core/MIPS/MIPSTracer.h" + #if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) #include "UI/DarwinFileSystemServices.h" #endif @@ -1887,6 +1890,60 @@ void DeveloperToolsScreen::CreateViews() { auto displayRefreshRate = list->Add(new PopupSliderChoice(&g_Config.iDisplayRefreshRate, 60, 1000, 60, dev->T("Display refresh rate"), 1, screenManager())); displayRefreshRate->SetFormat(dev->T("%d Hz")); +#if !PPSSPP_PLATFORM(ANDROID) && !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(SWITCH) + list->Add(new ItemHeader(dev->T("MIPSTracer"))); + + MIPSTracerEnabled_ = mipsTracer.tracing_enabled; + CheckBox *MIPSLoggerEnabled = new CheckBox(&MIPSTracerEnabled_, dev->T("MIPSTracer enabled")); + list->Add(MIPSLoggerEnabled)->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerEnabled); + MIPSLoggerEnabled->SetEnabledFunc([]() { + bool temp = g_Config.iCpuCore == static_cast(CPUCore::IR_INTERPRETER) && PSP_IsInited(); + return temp && Core_IsStepping() && coreState != CORE_POWERDOWN; + }); + + Choice *MIPSlogging_path = list->Add(new Choice(dev->T("Select the output logging file"))); + MIPSlogging_path->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerPathChanged); + MIPSlogging_path->SetEnabledFunc([]() { + if (!PSP_IsInited()) + return false; + return true; + }); + + MIPSTracerPath_ = mipsTracer.get_logging_path(); + MIPSTracerPath = list->Add(new InfoItem(dev->T("Current log file"), MIPSTracerPath_)); + + PopupSliderChoice* storage_capacity = list->Add( + new PopupSliderChoice( + &mipsTracer.in_storage_capacity, 0x4'0000, 0x40'0000, 0x10'0000, dev->T("Storage capacity"), 0x10000, screenManager() + ) + ); + storage_capacity->SetFormat("0x%x asm opcodes"); + storage_capacity->OnChange.Add([&](UI::EventParams &) { + INFO_LOG(Log::JIT, "User changed the tracer's storage capacity to 0x%x", mipsTracer.in_storage_capacity); + return UI::EVENT_CONTINUE; + }); + + PopupSliderChoice* trace_max_size = list->Add( + new PopupSliderChoice( + &mipsTracer.in_max_trace_size, 0x1'0000, 0x40'0000, 0x10'0000, dev->T("Max allowed trace size"), 0x10000, screenManager() + ) + ); + trace_max_size->SetFormat("%d basic blocks"); + trace_max_size->OnChange.Add([&](UI::EventParams &) { + INFO_LOG(Log::JIT, "User changed the tracer's max trace size to %d", mipsTracer.in_max_trace_size); + return UI::EVENT_CONTINUE; + }); + + Button *FlushTrace = list->Add(new Button(dev->T("Flush the trace"))); + FlushTrace->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerFlushTrace); + + Button *InvalidateJitCache = list->Add(new Button(dev->T("Clear the JIT cache"))); + InvalidateJitCache->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearJitCache); + + Button *ClearMIPSTracer = list->Add(new Button(dev->T("Clear the MIPSTracer"))); + ClearMIPSTracer->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearTracer); +#endif + Draw::DrawContext *draw = screenManager()->getDrawContext(); list->Add(new ItemHeader(dev->T("Ubershaders"))); @@ -2076,6 +2133,51 @@ UI::EventReturn DeveloperToolsScreen::OnRemoteDebugger(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn DeveloperToolsScreen::OnMIPSTracerEnabled(UI::EventParams &e) { + if (MIPSTracerEnabled_) { + u32 capacity = mipsTracer.in_storage_capacity; + u32 trace_size = mipsTracer.in_max_trace_size; + + mipsTracer.initialize(capacity, trace_size); + mipsTracer.start_tracing(); + } + else { + mipsTracer.stop_tracing(); + } + return UI::EVENT_DONE; +} + +UI::EventReturn DeveloperToolsScreen::OnMIPSTracerPathChanged(UI::EventParams &e) { + auto dev = GetI18NCategory(I18NCat::DEVELOPER); + System_BrowseForFile(GetRequesterToken(), dev->T("Select the log file"), BrowseFileType::ANY, + [this](const std::string &value, int) { + mipsTracer.set_logging_path(value); + MIPSTracerPath_ = value; + MIPSTracerPath->SetRightText(MIPSTracerPath_); + }); + return UI::EVENT_DONE; +} + +UI::EventReturn DeveloperToolsScreen::OnMIPSTracerFlushTrace(UI::EventParams &e) { + bool success = mipsTracer.flush_to_file(); + if (!success) { + WARN_LOG(Log::JIT, "Error: cannot flush the trace to the specified file!"); + } + return UI::EVENT_DONE; +} + +UI::EventReturn DeveloperToolsScreen::OnMIPSTracerClearJitCache(UI::EventParams &e) { + INFO_LOG(Log::JIT, "Clearing the jit cache..."); + System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT); + return UI::EVENT_DONE; +} + +UI::EventReturn DeveloperToolsScreen::OnMIPSTracerClearTracer(UI::EventParams &e) { + INFO_LOG(Log::JIT, "Clearing the MIPSTracer..."); + mipsTracer.clear(); + return UI::EVENT_DONE; +} + void DeveloperToolsScreen::update() { UIDialogScreenWithBackground::update(); allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 94be03002527..87ad8eca4475 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -150,6 +150,11 @@ class DeveloperToolsScreen : public UIDialogScreenWithGameBackground { UI::EventReturn OnJitAffectingSetting(UI::EventParams &e); UI::EventReturn OnJitDebugTools(UI::EventParams &e); UI::EventReturn OnRemoteDebugger(UI::EventParams &e); + UI::EventReturn OnMIPSTracerEnabled(UI::EventParams &e); + UI::EventReturn OnMIPSTracerPathChanged(UI::EventParams &e); + UI::EventReturn OnMIPSTracerFlushTrace(UI::EventParams &e); + UI::EventReturn OnMIPSTracerClearJitCache(UI::EventParams &e); + UI::EventReturn OnMIPSTracerClearTracer(UI::EventParams &e); UI::EventReturn OnGPUDriverTest(UI::EventParams &e); UI::EventReturn OnFramedumpTest(UI::EventParams &e); UI::EventReturn OnMemstickTest(UI::EventParams &e); @@ -164,6 +169,10 @@ class DeveloperToolsScreen : public UIDialogScreenWithGameBackground { MAYBE, }; HasIni hasTexturesIni_ = HasIni::MAYBE; + + bool MIPSTracerEnabled_ = false; + std::string MIPSTracerPath_ = ""; + UI::InfoItem* MIPSTracerPath = nullptr; }; class HostnameSelectScreen : public PopupScreen { diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 5a8eafb8dd0b..0a64ea0a2935 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -305,6 +305,7 @@ + @@ -583,6 +584,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 591347ecc226..0434c2142bdd 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -555,6 +555,9 @@ MIPS + + MIPS + Dialog @@ -1624,6 +1627,9 @@ MIPS + + MIPS + Dialog diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 8ad6ea1aacd9..8e45dc5eb4d0 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -447,6 +447,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/MIPS/MIPSVFPUFallbacks.cpp.arm \ $(SRC)/Core/MIPS/MIPSCodeUtils.cpp.arm \ $(SRC)/Core/MIPS/MIPSDebugInterface.cpp \ + $(SRC)/Core/MIPS/MIPSTracer.cpp \ $(SRC)/Core/MIPS/IR/IRAnalysis.cpp \ $(SRC)/Core/MIPS/IR/IRFrontend.cpp \ $(SRC)/Core/MIPS/IR/IRJit.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 74e06c3c387d..eb771382c418 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -758,6 +758,7 @@ SOURCES_CXX += \ $(COREDIR)/MIPS/MIPSStackWalk.cpp \ $(COREDIR)/MIPS/MIPSVFPUUtils.cpp \ $(COREDIR)/MIPS/MIPSVFPUFallbacks.cpp \ + $(COREDIR)/MIPS/MIPSTracer.cpp \ $(COREDIR)/MemFault.cpp \ $(COREDIR)/MemMap.cpp \ $(COREDIR)/MemMapFunctions.cpp \