From a6be0517dcc96b2e1c41a17a76d3d21270c44ec5 Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Thu, 5 Sep 2024 22:18:09 +0300 Subject: [PATCH 01/16] New IR instruction added --- Core/MIPS/IR/IRInst.cpp | 2 ++ Core/MIPS/IR/IRInst.h | 3 +++ Core/MIPS/IR/IRInterpreter.cpp | 3 +++ 3 files changed, 8 insertions(+) diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index b591cfbd591e..79837eb1a814 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::LogBlockHash, "logBlockHash", ""} }; const IRMeta *metaIndex[256]; diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index a7762d08824a..c5187936fc04 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. + LogBlockHash, + Nop, Bad, }; diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 5af7b98dd602..ac4b646249ff 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -1236,6 +1236,9 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) { return mips->pc; } break; + case IROp::LogBlockHash: + // Do nothing for now + break; case IROp::Nop: // TODO: This shouldn't crash, but for now we should not emit nops, so... case IROp::Bad: From 00cd142365d5d99a42a48fa08ef5aa7874729a0c Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Fri, 6 Sep 2024 23:11:39 +0300 Subject: [PATCH 02/16] Comment fix + new QOL method --- Core/MIPS/IR/IRJit.cpp | 3 ++- Core/MIPS/IR/IRJit.h | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index f1d6724f4561..2b5c4bbc7546 100644 --- a/Core/MIPS/IR/IRJit.cpp +++ b/Core/MIPS/IR/IRJit.cpp @@ -172,7 +172,8 @@ bool IRJit::CompileBlock(u32 em_address, std::vector &instructions, u32 } if (!CompileNativeBlock(&blocks_, block_num, preload)) return false; - // Overwrites the first instruction, and also updates stats. + + // 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); From 34f113207de0fdc3fbb0fd3a0173754dbb649d86 Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Sat, 7 Sep 2024 01:03:23 +0300 Subject: [PATCH 03/16] Added the MIPSTracer files to the project + name fix --- CMakeLists.txt | 2 ++ Core/Core.vcxproj | 2 ++ Core/Core.vcxproj.filters | 6 ++++++ Core/MIPS/IR/IRInst.cpp | 2 +- Core/MIPSTracer.cpp | 0 Core/MIPSTracer.h | 1 + UWP/CoreUWP/CoreUWP.vcxproj | 2 ++ UWP/CoreUWP/CoreUWP.vcxproj.filters | 6 ++++++ android/jni/Android.mk | 1 + libretro/Makefile.common | 1 + 10 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Core/MIPSTracer.cpp create mode 100644 Core/MIPSTracer.h 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.vcxproj b/Core/Core.vcxproj index af66592e0114..43542f527840 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -583,6 +583,7 @@ + @@ -1193,6 +1194,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 2fd6262e0155..39c329f0660b 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1315,6 +1315,9 @@ HLE\Libraries + + MIPS + @@ -2106,6 +2109,9 @@ HLE\Libraries + + MIPS + diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index 79837eb1a814..6b7b1b43873e 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -190,7 +190,7 @@ static const IRMeta irMeta[] = { { IROp::ApplyRoundingMode, "ApplyRoundingMode", "" }, { IROp::UpdateRoundingMode, "UpdateRoundingMode", "" }, - {IROp::LogBlockHash, "logBlockHash", ""} + {IROp::LogBlockHash, "LogBlockHash", ""} }; const IRMeta *metaIndex[256]; diff --git a/Core/MIPSTracer.cpp b/Core/MIPSTracer.cpp new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/Core/MIPSTracer.h b/Core/MIPSTracer.h new file mode 100644 index 000000000000..6f70f09beec2 --- /dev/null +++ b/Core/MIPSTracer.h @@ -0,0 +1 @@ +#pragma once 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 \ From 854579a97b3dfb0de77146d24296715e351184f0 Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Sun, 8 Sep 2024 00:30:46 +0300 Subject: [PATCH 04/16] Initial code for the tracer + fixed the file locations --- Core/Core.vcxproj | 4 +- Core/Core.vcxproj.filters | 12 ++-- Core/MIPS/MIPSTracer.cpp | 41 +++++++++++ Core/MIPS/MIPSTracer.h | 138 ++++++++++++++++++++++++++++++++++++++ Core/MIPSTracer.cpp | 0 Core/MIPSTracer.h | 1 - 6 files changed, 187 insertions(+), 9 deletions(-) create mode 100644 Core/MIPS/MIPSTracer.cpp create mode 100644 Core/MIPS/MIPSTracer.h delete mode 100644 Core/MIPSTracer.cpp delete mode 100644 Core/MIPSTracer.h diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 43542f527840..b73d036dafa4 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -583,7 +583,6 @@ - @@ -993,6 +992,7 @@ + true true @@ -1194,7 +1194,6 @@ - @@ -1408,6 +1407,7 @@ + true true diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 39c329f0660b..e00f5b24ba78 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -123,6 +123,9 @@ MIPS + + MIPS + MIPS @@ -1315,9 +1318,6 @@ HLE\Libraries - - MIPS - @@ -1350,6 +1350,9 @@ MIPS + + MIPS + MIPS @@ -2109,9 +2112,6 @@ HLE\Libraries - - MIPS - diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp new file mode 100644 index 000000000000..328e10645ac8 --- /dev/null +++ b/Core/MIPS/MIPSTracer.cpp @@ -0,0 +1,41 @@ +// 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 + + +bool TraceBlockStorage::push_block(u32* instructions, u32 size) { + if (cur_offset + size >= raw_instructions.size()) { + return false; + } + std::memcpy(cur_data_ptr, instructions, size); + cur_offset += size; + cur_data_ptr += size; + return true; +} + +void TraceBlockStorage::initialize(u32 capacity) { + raw_instructions.resize(capacity); + cur_offset = 0; + cur_data_ptr = raw_instructions.data(); +} + + +MIPSTracer mipsTracer; diff --git a/Core/MIPS/MIPSTracer.h b/Core/MIPS/MIPSTracer.h new file mode 100644 index 000000000000..15706c1c6658 --- /dev/null +++ b/Core/MIPS/MIPSTracer.h @@ -0,0 +1,138 @@ +// 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 + +#include "Common/CommonTypes.h" +#include "Core/Opcode.h" + + + +struct TraceBlockInfo { + u32 virt_address; + u32 size; + u32 storage_index; +}; + +struct TraceBlockStorage { + std::vector raw_instructions; + u32 cur_offset; + u32* cur_data_ptr; + + TraceBlockStorage(u32 capacity): + raw_instructions(capacity, 0), + cur_offset(0), + cur_data_ptr(raw_instructions.data()) + {} + + TraceBlockStorage(): raw_instructions(), cur_offset(0), cur_data_ptr(nullptr) {} + + bool push_block(u32* instructions, u32 size); + + void initialize(u32 capacity); + + Memory::Opcode operator[](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); +} + +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_index; + + TraceBlockStorage storage; + + std::string logging_path; + + MIPSTracer(): trace_info(), executed_blocks(), hash_to_index(), storage(), logging_path() {} +}; + +extern MIPSTracer mipsTracer; diff --git a/Core/MIPSTracer.cpp b/Core/MIPSTracer.cpp deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/Core/MIPSTracer.h b/Core/MIPSTracer.h deleted file mode 100644 index 6f70f09beec2..000000000000 --- a/Core/MIPSTracer.h +++ /dev/null @@ -1 +0,0 @@ -#pragma once From e3b09bea5900c4a6cb69fa5750658e78019e526b Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Sun, 8 Sep 2024 15:23:13 +0300 Subject: [PATCH 05/16] Ported the MIPSLogger's UI + basic integration of MIPSTracer --- Core/MIPS/IR/IRFrontend.cpp | 12 ++++- Core/MIPS/IR/IRInterpreter.cpp | 1 + Core/MIPS/IR/IRJit.cpp | 9 +++- Core/MIPS/MIPSTracer.cpp | 45 ++++++++++++++++++- Core/MIPS/MIPSTracer.h | 15 ++++++- UI/GameSettingsScreen.cpp | 80 ++++++++++++++++++++++++++++++++++ UI/GameSettingsScreen.h | 7 +++ 7 files changed, 165 insertions(+), 4 deletions(-) diff --git a/Core/MIPS/IR/IRFrontend.cpp b/Core/MIPS/IR/IRFrontend.cpp index 024609bf7018..ba2f6ac59572 100644 --- a/Core/MIPS/IR/IRFrontend.cpp +++ b/Core/MIPS/IR/IRFrontend.cpp @@ -28,6 +28,8 @@ #include "Core/MIPS/IR/IRRegCache.h" #include "Core/MIPS/IR/IRPassSimplify.h" #include "Core/MIPS/IR/IRInterpreter.h" +#include "Core/MIPS/MIPSTracer.h" + namespace MIPSComp { @@ -299,7 +301,15 @@ 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()); + block_instructions.push_back({ IROp::LogBlockHash, 0, 0, 0, 0 }); + std::copy(block_instructions.begin(), block_instructions.end(), std::back_inserter(instructions)); + } if (logBlocks > 0 && dontLogBlocks == 0) { char temp2[256]; diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index ac4b646249ff..7f59b07533b4 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... diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index 2b5c4bbc7546..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,19 @@ 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; + 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) diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index 328e10645ac8..09d74f0f4ae6 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -19,9 +19,10 @@ #include // for std::memcpy #include "Core/MIPS/MIPSTables.h" // for MIPSDisAsm +#include "Core/MemMap.h" // for Memory::GetPointerUnchecked -bool TraceBlockStorage::push_block(u32* instructions, u32 size) { +bool TraceBlockStorage::push_block(const u32* instructions, u32 size) { if (cur_offset + size >= raw_instructions.size()) { return false; } @@ -37,5 +38,47 @@ void TraceBlockStorage::initialize(u32 capacity) { cur_data_ptr = raw_instructions.data(); } +void MIPSTracer::prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks) { + auto hash = block->GetHash(); + auto it = mipsTracer.hash_to_index.find(hash); + + if (it != mipsTracer.hash_to_index.end()) { + u32 index = it->second; + auto ir_ptr = (IRInst*)blocks.GetBlockInstructionPtr(*block); + ir_ptr->constant = index; + return; + } + + // 1) Copy the block instructions into our storage + u32 virt_addr, size; + block->GetRange(virt_addr, size); + u32 storage_index = mipsTracer.storage.cur_offset; + + auto mips_instructions_ptr = (const u32*)Memory::GetPointerUnchecked(virt_addr); + + if (!mipsTracer.storage.push_block(mips_instructions_ptr, size)) { + // We ran out of storage! TODO: report that to the user + mipsTracer.tracing_enabled = false; + return; + } + // Successfully inserted the block at index 'possible_index'! + + mipsTracer.trace_info.push_back({ virt_addr, size, storage_index }); + + // 2) Save the hash and the index + + u32 index = mipsTracer.trace_info.size() - 1; + hash_to_index.emplace(hash, index); + + auto ir_ptr = (IRInst*)blocks.GetBlockInstructionPtr(*block); + ir_ptr->constant = index; +} + +bool MIPSTracer::flush_to_file() { + // Do nothing for now + return true; +} MIPSTracer mipsTracer; + + diff --git a/Core/MIPS/MIPSTracer.h b/Core/MIPS/MIPSTracer.h index 15706c1c6658..6a237cde5225 100644 --- a/Core/MIPS/MIPSTracer.h +++ b/Core/MIPS/MIPSTracer.h @@ -24,6 +24,8 @@ #include "Common/CommonTypes.h" #include "Core/Opcode.h" +#include "Core/MIPS/IR/IRJit.h" + @@ -46,7 +48,7 @@ struct TraceBlockStorage { TraceBlockStorage(): raw_instructions(), cur_offset(0), cur_data_ptr(nullptr) {} - bool push_block(u32* instructions, u32 size); + bool push_block(const u32* instructions, u32 size); void initialize(u32 capacity); @@ -131,6 +133,17 @@ struct MIPSTracer { TraceBlockStorage storage; std::string logging_path; + bool tracing_enabled = false; + + void prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks); + void setLoggingPath(std::string path) { + logging_path = path; + } + std::string getLoggingPath() const { + return logging_path; + } + + bool flush_to_file(); MIPSTracer(): trace_info(), executed_blocks(), hash_to_index(), storage(), logging_path() {} }; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 229354f53c7e..f5a0807a12cf 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" +#include "Core/MIPS/MIPSTracer.h" + #if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) #include "UI/DarwinFileSystemServices.h" #endif @@ -1887,6 +1890,45 @@ 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")); + 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([]() { +#if PPSSPP_PLATFORM(WINDOWS) + bool temp = g_Config.iCpuCore == static_cast(CPUCore::IR_INTERPRETER) && PSP_IsInited(); + return temp && Core_IsStepping() && coreState != CORE_POWERDOWN; +#else + return false; +#endif + }); + + 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 PPSSPP_PLATFORM(WINDOWS) + if (!PSP_IsInited()) + return false; + return true; +#else + return false; +#endif + }); + + MIPSTracerPath_ = mipsTracer.getLoggingPath(); + MIPSTracerPath = list->Add(new InfoItem(dev->T("Current log file"), MIPSTracerPath_)); + + Button *test = list->Add(new Button(dev->T("Flush the trace"))); + test->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerFlushTrace); + test->SetEnabledFunc([]() { +#if PPSSPP_PLATFORM(WINDOWS) + return true; +#else + return false; +#endif + }); + Draw::DrawContext *draw = screenManager()->getDrawContext(); list->Add(new ItemHeader(dev->T("Ubershaders"))); @@ -2076,6 +2118,44 @@ UI::EventReturn DeveloperToolsScreen::OnRemoteDebugger(UI::EventParams &e) { return UI::EVENT_DONE; } +UI::EventReturn DeveloperToolsScreen::OnMIPSTracerEnabled(UI::EventParams &e) { + if (MIPSTracerEnabled_) { + // mipsTracer.tracing_enabled = true; + } + else { + mipsTracer.tracing_enabled = false; + } + return UI::EVENT_DONE; +} + +UI::EventReturn DeveloperToolsScreen::OnMIPSTracerPathChanged(UI::EventParams &e) { + // auto dev = GetI18NCategory(I18NCat::DEVELOPER); + /*System_BrowseForFile(dev->T("Select the log file"), BrowseFileType::ANY, [](const std::string &value, int) { + + });*/ + +#if PPSSPP_PLATFORM(WINDOWS) + std::string fn; + if (W32Util::BrowseForFileName(false, nullptr, L"Select the log file", 0, L"Text files\0*.*\0\0", L"txt", fn)) { + mipsTracer.setLoggingPath(fn); + MIPSTracerPath_ = std::move(fn); + MIPSTracerPath->SetRightText(MIPSTracerPath_); + } +#endif + return UI::EVENT_DONE; +} + +UI::EventReturn DeveloperToolsScreen::OnMIPSTracerFlushTrace(UI::EventParams &e) { + // Let's ban the non-Windows platforms with force +#if PPSSPP_PLATFORM(WINDOWS) + bool success = mipsTracer.flush_to_file(); + if (!success) { + // TODO: report this to the user + } +#endif + 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..ceae61debf1b 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -150,6 +150,9 @@ 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 OnGPUDriverTest(UI::EventParams &e); UI::EventReturn OnFramedumpTest(UI::EventParams &e); UI::EventReturn OnMemstickTest(UI::EventParams &e); @@ -164,6 +167,10 @@ class DeveloperToolsScreen : public UIDialogScreenWithGameBackground { MAYBE, }; HasIni hasTexturesIni_ = HasIni::MAYBE; + + bool MIPSTracerEnabled_ = false; + std::string MIPSTracerPath_ = ""; + UI::InfoItem* MIPSTracerPath = nullptr; }; class HostnameSelectScreen : public PopupScreen { From 25f6b01d8691486776c1df0e45ffc6710622ee1a Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Sun, 8 Sep 2024 18:45:27 +0300 Subject: [PATCH 06/16] Added the initialization code + UI bindings + logs --- Core/Core.cpp | 4 ++++ Core/MIPS/IR/IRInterpreter.cpp | 4 +++- Core/MIPS/MIPSTracer.cpp | 41 ++++++++++++++++++++++++++++++++-- Core/MIPS/MIPSTracer.h | 10 +++++++++ UI/GameSettingsScreen.cpp | 40 ++++++++++++++++++++++++++++----- 5 files changed, 90 insertions(+), 9 deletions(-) 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/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 7f59b07533b4..b6b2c2fa5d2c 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -1238,7 +1238,9 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) { } break; case IROp::LogBlockHash: - // Do nothing for now + 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... diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index 09d74f0f4ae6..1a8d327ee7e4 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -36,6 +36,14 @@ void TraceBlockStorage::initialize(u32 capacity) { raw_instructions.resize(capacity); cur_offset = 0; cur_data_ptr = raw_instructions.data(); + INFO_LOG(Log::JIT, "TraceBlockStorage initialized: capacity=0x%x", capacity); +} + +void TraceBlockStorage::clear() { + raw_instructions.clear(); + cur_offset = 0; + cur_data_ptr = nullptr; + INFO_LOG(Log::JIT, "TraceBlockStorage cleared"); } void MIPSTracer::prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks) { @@ -57,8 +65,9 @@ void MIPSTracer::prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& auto mips_instructions_ptr = (const u32*)Memory::GetPointerUnchecked(virt_addr); if (!mipsTracer.storage.push_block(mips_instructions_ptr, size)) { - // We ran out of storage! TODO: report that to the user - mipsTracer.tracing_enabled = false; + // 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 'possible_index'! @@ -75,10 +84,38 @@ void MIPSTracer::prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& } bool MIPSTracer::flush_to_file() { + INFO_LOG(Log::JIT, "MIPSTracer ordered to flush the trace to a file..."); // Do nothing for now + clear(); return true; } +void MIPSTracer::start_tracing() { + tracing_enabled = true; + INFO_LOG(Log::JIT, "MIPSTracer enabled"); +} + +void MIPSTracer::stop_tracing() { + tracing_enabled = false; + INFO_LOG(Log::JIT, "MIPSTracer disabled"); +} + +void MIPSTracer::initialize(u32 storage_capacity, u32 max_trace_size) { + executed_blocks.resize(max_trace_size); + hash_to_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_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 index 6a237cde5225..496899c685a7 100644 --- a/Core/MIPS/MIPSTracer.h +++ b/Core/MIPS/MIPSTracer.h @@ -25,6 +25,7 @@ #include "Common/CommonTypes.h" #include "Core/Opcode.h" #include "Core/MIPS/IR/IRJit.h" +#include "Common/Log.h" @@ -51,6 +52,7 @@ struct TraceBlockStorage { bool push_block(const u32* instructions, u32 size); void initialize(u32 capacity); + void clear(); Memory::Opcode operator[](u32 index) { return Memory::Opcode(raw_instructions[index]); @@ -135,6 +137,12 @@ struct MIPSTracer { std::string logging_path; bool tracing_enabled = false; + int in_storage_capacity = 0; + int in_max_trace_size = 0; + + void start_tracing(); + void stop_tracing(); + void prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks); void setLoggingPath(std::string path) { logging_path = path; @@ -144,6 +152,8 @@ struct MIPSTracer { } bool flush_to_file(); + void initialize(u32 storage_capacity, u32 max_trace_size); + void clear(); MIPSTracer(): trace_info(), executed_blocks(), hash_to_index(), storage(), logging_path() {} }; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index f5a0807a12cf..3db01a9f059d 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1919,9 +1919,31 @@ void DeveloperToolsScreen::CreateViews() { MIPSTracerPath_ = mipsTracer.getLoggingPath(); MIPSTracerPath = list->Add(new InfoItem(dev->T("Current log file"), MIPSTracerPath_)); - Button *test = list->Add(new Button(dev->T("Flush the trace"))); - test->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerFlushTrace); - test->SetEnabledFunc([]() { + 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); + FlushTrace->SetEnabledFunc([]() { #if PPSSPP_PLATFORM(WINDOWS) return true; #else @@ -2119,11 +2141,17 @@ UI::EventReturn DeveloperToolsScreen::OnRemoteDebugger(UI::EventParams &e) { } UI::EventReturn DeveloperToolsScreen::OnMIPSTracerEnabled(UI::EventParams &e) { + mipsTracer.clear(); + if (MIPSTracerEnabled_) { - // mipsTracer.tracing_enabled = true; + u32 capacity = mipsTracer.in_storage_capacity; + u32 trace_size = mipsTracer.in_max_trace_size; + + mipsTracer.initialize(capacity, trace_size); + // mipsTracer.start_tracing(); } else { - mipsTracer.tracing_enabled = false; + mipsTracer.stop_tracing(); } return UI::EVENT_DONE; } @@ -2150,7 +2178,7 @@ UI::EventReturn DeveloperToolsScreen::OnMIPSTracerFlushTrace(UI::EventParams &e) #if PPSSPP_PLATFORM(WINDOWS) bool success = mipsTracer.flush_to_file(); if (!success) { - // TODO: report this to the user + WARN_LOG(Log::JIT, "Error: cannot flush the trace to the specified file!"); } #endif return UI::EVENT_DONE; From a26afb4c2f583b2f0ec44e86902422367202a23c Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Sun, 8 Sep 2024 20:33:14 +0300 Subject: [PATCH 07/16] Implemented the trace reconstruction + bugs fixed --- Core/MIPS/IR/IRFrontend.cpp | 6 ++++-- Core/MIPS/MIPSTracer.cpp | 37 ++++++++++++++++++++++++++++++++++--- Core/MIPS/MIPSTracer.h | 4 ++++ UI/GameSettingsScreen.cpp | 2 +- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/Core/MIPS/IR/IRFrontend.cpp b/Core/MIPS/IR/IRFrontend.cpp index ba2f6ac59572..eb8c2fc0dd32 100644 --- a/Core/MIPS/IR/IRFrontend.cpp +++ b/Core/MIPS/IR/IRFrontend.cpp @@ -307,8 +307,10 @@ void IRFrontend::DoJit(u32 em_address, std::vector &instructions, u32 &m else { std::vector block_instructions = code->GetInstructions(); instructions.reserve(block_instructions.capacity()); - block_instructions.push_back({ IROp::LogBlockHash, 0, 0, 0, 0 }); - std::copy(block_instructions.begin(), block_instructions.end(), std::back_inserter(instructions)); + // The first instruction is "Downcount" + instructions.push_back(block_instructions.front()); + instructions.push_back({ IROp::LogBlockHash, 0, 0, 0, 0 }); + std::copy(block_instructions.begin() + 1, block_instructions.end(), std::back_inserter(instructions)); } if (logBlocks > 0 && dontLogBlocks == 0) { diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index 1a8d327ee7e4..d530806024d5 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -53,7 +53,8 @@ void MIPSTracer::prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& if (it != mipsTracer.hash_to_index.end()) { u32 index = it->second; auto ir_ptr = (IRInst*)blocks.GetBlockInstructionPtr(*block); - ir_ptr->constant = index; + + ir_ptr[1].constant = index; return; } @@ -80,16 +81,46 @@ void MIPSTracer::prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& hash_to_index.emplace(hash, index); auto ir_ptr = (IRInst*)blocks.GetBlockInstructionPtr(*block); - ir_ptr->constant = index; + ir_ptr[1].constant = index; } bool MIPSTracer::flush_to_file() { INFO_LOG(Log::JIT, "MIPSTracer ordered to flush the trace to a file..."); - // Do nothing for now + + output.open(logging_path, std::ios::out); + if (!output) { + WARN_LOG(Log::JIT, "MIPSTracer failed to open the file '%s'", logging_path); + 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..."); + output.close(); clear(); return true; } +void MIPSTracer::flush_block_to_file(TraceBlockInfo& block_info) { + static char buffer[1024]; + + // The log format is '0x{8 hex digits of the address}: {disassembled line}' + const auto prefix_size = 2 + 8 + 2; + + u32 addr = block_info.virt_address; + u32 index = block_info.storage_index; + u32 end_addr = addr + block_info.size; + + for (; addr < end_addr; addr += 4, ++index) { + snprintf(buffer, sizeof(buffer), "0x%08x: ", addr); + MIPSDisAsm(storage[index], addr, buffer + prefix_size, sizeof(buffer) - prefix_size, true); + output << std::string(buffer) << "\n"; + } +} + void MIPSTracer::start_tracing() { tracing_enabled = true; INFO_LOG(Log::JIT, "MIPSTracer enabled"); diff --git a/Core/MIPS/MIPSTracer.h b/Core/MIPS/MIPSTracer.h index 496899c685a7..4dcbdefcec0e 100644 --- a/Core/MIPS/MIPSTracer.h +++ b/Core/MIPS/MIPSTracer.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "Common/CommonTypes.h" #include "Core/Opcode.h" @@ -135,6 +136,7 @@ struct MIPSTracer { TraceBlockStorage storage; std::string logging_path; + std::ofstream output; bool tracing_enabled = false; int in_storage_capacity = 0; @@ -152,6 +154,8 @@ struct MIPSTracer { } bool flush_to_file(); + void flush_block_to_file(TraceBlockInfo& block); + void initialize(u32 storage_capacity, u32 max_trace_size); void clear(); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 3db01a9f059d..69e558fe2244 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -2148,7 +2148,7 @@ UI::EventReturn DeveloperToolsScreen::OnMIPSTracerEnabled(UI::EventParams &e) { u32 trace_size = mipsTracer.in_max_trace_size; mipsTracer.initialize(capacity, trace_size); - // mipsTracer.start_tracing(); + mipsTracer.start_tracing(); } else { mipsTracer.stop_tracing(); From ff5877e99394a2aca6d68564f35f972b1977e864 Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Tue, 10 Sep 2024 15:16:08 +0300 Subject: [PATCH 08/16] Renamed the IR instruction, new UI button added --- Core/MIPS/IR/IRFrontend.cpp | 2 +- Core/MIPS/IR/IRInst.h | 2 +- Core/MIPS/IR/IRInterpreter.cpp | 2 +- Core/MIPS/MIPSTracer.cpp | 10 ++++++++-- UI/GameSettingsScreen.cpp | 9 +++++++++ UI/GameSettingsScreen.h | 1 + 6 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Core/MIPS/IR/IRFrontend.cpp b/Core/MIPS/IR/IRFrontend.cpp index eb8c2fc0dd32..083da746dd4b 100644 --- a/Core/MIPS/IR/IRFrontend.cpp +++ b/Core/MIPS/IR/IRFrontend.cpp @@ -309,7 +309,7 @@ void IRFrontend::DoJit(u32 em_address, std::vector &instructions, u32 &m instructions.reserve(block_instructions.capacity()); // The first instruction is "Downcount" instructions.push_back(block_instructions.front()); - instructions.push_back({ IROp::LogBlockHash, 0, 0, 0, 0 }); + instructions.push_back({ IROp::LogIRBlock, 0, 0, 0, 0 }); std::copy(block_instructions.begin() + 1, block_instructions.end(), std::back_inserter(instructions)); } diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index c5187936fc04..0dbfa6790520 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -238,7 +238,7 @@ enum class IROp : uint8_t { ValidateAddress128, // Tracing support. - LogBlockHash, + LogIRBlock, Nop, Bad, diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index b6b2c2fa5d2c..0d482b485d9a 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -1237,7 +1237,7 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) { return mips->pc; } break; - case IROp::LogBlockHash: + case IROp::LogIRBlock: if (mipsTracer.tracing_enabled) { mipsTracer.executed_blocks.push_back(inst->constant); } diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index d530806024d5..14a628d89cc2 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -117,18 +117,24 @@ void MIPSTracer::flush_block_to_file(TraceBlockInfo& block_info) { for (; addr < end_addr; addr += 4, ++index) { snprintf(buffer, sizeof(buffer), "0x%08x: ", addr); MIPSDisAsm(storage[index], addr, buffer + prefix_size, sizeof(buffer) - prefix_size, true); + + // TODO: check if removing the std::string makes this faster output << std::string(buffer) << "\n"; } } void MIPSTracer::start_tracing() { + if (!tracing_enabled) { + INFO_LOG(Log::JIT, "MIPSTracer enabled"); + } tracing_enabled = true; - INFO_LOG(Log::JIT, "MIPSTracer enabled"); } void MIPSTracer::stop_tracing() { + if (tracing_enabled) { + INFO_LOG(Log::JIT, "MIPSTracer disabled"); + } tracing_enabled = false; - INFO_LOG(Log::JIT, "MIPSTracer disabled"); } void MIPSTracer::initialize(u32 storage_capacity, u32 max_trace_size) { diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 69e558fe2244..bce651b4e2d9 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1951,6 +1951,9 @@ void DeveloperToolsScreen::CreateViews() { #endif }); + Button *InvalidateJitCache = list->Add(new Button(dev->T("Clear the JIT cache"))); + InvalidateJitCache->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearJitCache); + Draw::DrawContext *draw = screenManager()->getDrawContext(); list->Add(new ItemHeader(dev->T("Ubershaders"))); @@ -2184,6 +2187,12 @@ UI::EventReturn DeveloperToolsScreen::OnMIPSTracerFlushTrace(UI::EventParams &e) return UI::EVENT_DONE; } +UI::EventReturn DeveloperToolsScreen::OnMIPSTracerClearJitCache(UI::EventParams &e) { + INFO_LOG(Log::JIT, "Ordered to clear the jit cache"); + System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT); + return UI::EVENT_DONE; +} + void DeveloperToolsScreen::update() { UIDialogScreenWithBackground::update(); allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index ceae61debf1b..dc1205a64963 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -153,6 +153,7 @@ class DeveloperToolsScreen : public UIDialogScreenWithGameBackground { 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 OnGPUDriverTest(UI::EventParams &e); UI::EventReturn OnFramedumpTest(UI::EventParams &e); UI::EventReturn OnMemstickTest(UI::EventParams &e); From f23b04fb4abe784bce97c6e9a327043fbc20fe0d Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Wed, 11 Sep 2024 22:53:14 +0300 Subject: [PATCH 09/16] Logic errors fixed + refactoring --- Core/MIPS/IR/IRInst.cpp | 2 +- Core/MIPS/MIPSTracer.cpp | 124 +++++++++++++++++++++++++++------------ Core/MIPS/MIPSTracer.h | 22 +++---- 3 files changed, 99 insertions(+), 49 deletions(-) diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index 6b7b1b43873e..ff235546d3d0 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -190,7 +190,7 @@ static const IRMeta irMeta[] = { { IROp::ApplyRoundingMode, "ApplyRoundingMode", "" }, { IROp::UpdateRoundingMode, "UpdateRoundingMode", "" }, - {IROp::LogBlockHash, "LogBlockHash", ""} + {IROp::LogIRBlock, "LogIRBlock", ""} }; const IRMeta *metaIndex[256]; diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index 14a628d89cc2..19e1093f5bb7 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -22,64 +22,78 @@ #include "Core/MemMap.h" // for Memory::GetPointerUnchecked -bool TraceBlockStorage::push_block(const u32* instructions, u32 size) { - if (cur_offset + size >= raw_instructions.size()) { +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_offset += size; - cur_data_ptr += size; + cur_data_ptr += indexes_count; + + cur_index += 1 + indexes_count; return true; } void TraceBlockStorage::initialize(u32 capacity) { raw_instructions.resize(capacity); - cur_offset = 0; + 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_offset = 0; + cur_index = 0; cur_data_ptr = nullptr; INFO_LOG(Log::JIT, "TraceBlockStorage cleared"); } void MIPSTracer::prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks) { - auto hash = block->GetHash(); - auto it = mipsTracer.hash_to_index.find(hash); - - if (it != mipsTracer.hash_to_index.end()) { - u32 index = it->second; - auto ir_ptr = (IRInst*)blocks.GetBlockInstructionPtr(*block); - - ir_ptr[1].constant = index; - return; - } - - // 1) Copy the block instructions into our storage u32 virt_addr, size; block->GetRange(virt_addr, size); - u32 storage_index = mipsTracer.storage.cur_offset; - auto mips_instructions_ptr = (const u32*)Memory::GetPointerUnchecked(virt_addr); + u64 hash = block->GetHash(); + auto it = hash_to_storage_index.find(hash); - if (!mipsTracer.storage.push_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; + 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); } - // Successfully inserted the block at index 'possible_index'! - - mipsTracer.trace_info.push_back({ virt_addr, size, storage_index }); - // 2) Save the hash and the 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 = mipsTracer.trace_info.size() - 1; - hash_to_index.emplace(hash, index); + u32 index = trace_info.size() - 1; auto ir_ptr = (IRInst*)blocks.GetBlockInstructionPtr(*block); ir_ptr[1].constant = index; } @@ -107,16 +121,21 @@ bool MIPSTracer::flush_to_file() { void MIPSTracer::flush_block_to_file(TraceBlockInfo& block_info) { static char buffer[1024]; - // The log format is '0x{8 hex digits of the address}: {disassembled line}' + // 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 end_addr = addr + block_info.size; + + 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[index], addr, buffer + prefix_size, sizeof(buffer) - prefix_size, true); + MIPSDisAsm(storage.read_asm(index), addr, buffer + prefix_size, sizeof(buffer) - prefix_size, true); // TODO: check if removing the std::string makes this faster output << std::string(buffer) << "\n"; @@ -126,20 +145,49 @@ void MIPSTracer::flush_block_to_file(TraceBlockInfo& block_info) { void MIPSTracer::start_tracing() { if (!tracing_enabled) { INFO_LOG(Log::JIT, "MIPSTracer enabled"); + tracing_enabled = true; } - 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); } - tracing_enabled = false; + // 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_index.reserve(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); @@ -147,7 +195,7 @@ void MIPSTracer::initialize(u32 storage_capacity, u32 max_trace_size) { void MIPSTracer::clear() { executed_blocks.clear(); - hash_to_index.clear(); + hash_to_storage_index.clear(); storage.clear(); trace_info.clear(); INFO_LOG(Log::JIT, "MIPSTracer cleared"); diff --git a/Core/MIPS/MIPSTracer.h b/Core/MIPS/MIPSTracer.h index 4dcbdefcec0e..79e0d2bd1569 100644 --- a/Core/MIPS/MIPSTracer.h +++ b/Core/MIPS/MIPSTracer.h @@ -29,33 +29,33 @@ #include "Common/Log.h" - - struct TraceBlockInfo { u32 virt_address; - u32 size; u32 storage_index; }; struct TraceBlockStorage { std::vector raw_instructions; - u32 cur_offset; + u32 cur_index; u32* cur_data_ptr; TraceBlockStorage(u32 capacity): raw_instructions(capacity, 0), - cur_offset(0), + cur_index(0), cur_data_ptr(raw_instructions.data()) {} - TraceBlockStorage(): raw_instructions(), cur_offset(0), cur_data_ptr(nullptr) {} + TraceBlockStorage(): raw_instructions(), cur_index(0), cur_data_ptr(nullptr) {} - bool push_block(const u32* instructions, u32 size); + bool save_block(const u32* instructions, u32 size); void initialize(u32 capacity); void clear(); - Memory::Opcode operator[](u32 index) { + u32 operator[](u32 index) { + return raw_instructions[index]; + } + Memory::Opcode read_asm(u32 index) { return Memory::Opcode(raw_instructions[index]); } }; @@ -131,7 +131,7 @@ struct MIPSTracer { // 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_index; + std::unordered_map hash_to_storage_index; TraceBlockStorage storage; @@ -159,7 +159,9 @@ struct MIPSTracer { void initialize(u32 storage_capacity, u32 max_trace_size); void clear(); - MIPSTracer(): trace_info(), executed_blocks(), hash_to_index(), storage(), logging_path() {} + inline void print_stats() const; + + MIPSTracer(): trace_info(), executed_blocks(), hash_to_storage_index(), storage(), logging_path() {} }; extern MIPSTracer mipsTracer; From ab7af0cc190b61d1013684c726262cb4f903daec Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Thu, 12 Sep 2024 22:11:00 +0300 Subject: [PATCH 10/16] Minor cleanup, new UI button --- Core/MIPS/MIPSTracer.cpp | 4 ++-- Core/MIPS/MIPSTracer.h | 4 ++-- UI/GameSettingsScreen.cpp | 13 ++++++++++--- UI/GameSettingsScreen.h | 1 + 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index 19e1093f5bb7..dad3c8eb91ff 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -58,7 +58,7 @@ void TraceBlockStorage::clear() { void MIPSTracer::prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks) { u32 virt_addr, size; - block->GetRange(virt_addr, size); + block->GetRange(&virt_addr, &size); u64 hash = block->GetHash(); auto it = hash_to_storage_index.find(hash); @@ -99,7 +99,7 @@ void MIPSTracer::prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& } bool MIPSTracer::flush_to_file() { - INFO_LOG(Log::JIT, "MIPSTracer ordered to flush the trace to a file..."); + INFO_LOG(Log::JIT, "Flushing the trace to a file..."); output.open(logging_path, std::ios::out); if (!output) { diff --git a/Core/MIPS/MIPSTracer.h b/Core/MIPS/MIPSTracer.h index 79e0d2bd1569..c3a03c99df79 100644 --- a/Core/MIPS/MIPSTracer.h +++ b/Core/MIPS/MIPSTracer.h @@ -139,8 +139,8 @@ struct MIPSTracer { std::ofstream output; bool tracing_enabled = false; - int in_storage_capacity = 0; - int in_max_trace_size = 0; + int in_storage_capacity = 0x10'0000; + int in_max_trace_size = 0x10'0000; void start_tracing(); void stop_tracing(); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index bce651b4e2d9..bc62de156d4c 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1954,6 +1954,9 @@ void DeveloperToolsScreen::CreateViews() { 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::OnMIPSTracerClearJitCache); + Draw::DrawContext *draw = screenManager()->getDrawContext(); list->Add(new ItemHeader(dev->T("Ubershaders"))); @@ -2144,8 +2147,6 @@ UI::EventReturn DeveloperToolsScreen::OnRemoteDebugger(UI::EventParams &e) { } UI::EventReturn DeveloperToolsScreen::OnMIPSTracerEnabled(UI::EventParams &e) { - mipsTracer.clear(); - if (MIPSTracerEnabled_) { u32 capacity = mipsTracer.in_storage_capacity; u32 trace_size = mipsTracer.in_max_trace_size; @@ -2188,11 +2189,17 @@ UI::EventReturn DeveloperToolsScreen::OnMIPSTracerFlushTrace(UI::EventParams &e) } UI::EventReturn DeveloperToolsScreen::OnMIPSTracerClearJitCache(UI::EventParams &e) { - INFO_LOG(Log::JIT, "Ordered to clear the jit cache"); + 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 dc1205a64963..87ad8eca4475 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -154,6 +154,7 @@ class DeveloperToolsScreen : public UIDialogScreenWithGameBackground { 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); From 01b3b59d06f057cf196bcfec59906a407d54a001 Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Thu, 12 Sep 2024 23:21:00 +0300 Subject: [PATCH 11/16] Bugs fixed + enabled MIPSTracer on Mac, desktop Linux and OpenBDS --- Core/MIPS/MIPSTracer.cpp | 2 +- UI/GameSettingsScreen.cpp | 40 +++++++++------------------------------ 2 files changed, 10 insertions(+), 32 deletions(-) diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index dad3c8eb91ff..079edeeb3a4a 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -103,7 +103,7 @@ bool MIPSTracer::flush_to_file() { output.open(logging_path, std::ios::out); if (!output) { - WARN_LOG(Log::JIT, "MIPSTracer failed to open the file '%s'", logging_path); + WARN_LOG(Log::JIT, "MIPSTracer failed to open the file '%s'", logging_path.c_str()); return false; } auto trace = executed_blocks.get_content(); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index bc62de156d4c..687f65c22dec 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1890,30 +1890,23 @@ 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([]() { -#if PPSSPP_PLATFORM(WINDOWS) bool temp = g_Config.iCpuCore == static_cast(CPUCore::IR_INTERPRETER) && PSP_IsInited(); return temp && Core_IsStepping() && coreState != CORE_POWERDOWN; -#else - return false; -#endif }); 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 PPSSPP_PLATFORM(WINDOWS) if (!PSP_IsInited()) return false; return true; -#else - return false; -#endif }); MIPSTracerPath_ = mipsTracer.getLoggingPath(); @@ -1943,19 +1936,13 @@ void DeveloperToolsScreen::CreateViews() { Button *FlushTrace = list->Add(new Button(dev->T("Flush the trace"))); FlushTrace->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerFlushTrace); - FlushTrace->SetEnabledFunc([]() { -#if PPSSPP_PLATFORM(WINDOWS) - return true; -#else - return false; -#endif - }); 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::OnMIPSTracerClearJitCache); + ClearMIPSTracer->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearTracer); +#endif Draw::DrawContext *draw = screenManager()->getDrawContext(); @@ -2161,30 +2148,21 @@ UI::EventReturn DeveloperToolsScreen::OnMIPSTracerEnabled(UI::EventParams &e) { } UI::EventReturn DeveloperToolsScreen::OnMIPSTracerPathChanged(UI::EventParams &e) { - // auto dev = GetI18NCategory(I18NCat::DEVELOPER); - /*System_BrowseForFile(dev->T("Select the log file"), BrowseFileType::ANY, [](const std::string &value, int) { - - });*/ - -#if PPSSPP_PLATFORM(WINDOWS) - std::string fn; - if (W32Util::BrowseForFileName(false, nullptr, L"Select the log file", 0, L"Text files\0*.*\0\0", L"txt", fn)) { - mipsTracer.setLoggingPath(fn); - MIPSTracerPath_ = std::move(fn); + auto dev = GetI18NCategory(I18NCat::DEVELOPER); + System_BrowseForFile(GetRequesterToken(), dev->T("Select the log file"), BrowseFileType::ANY, + [this](const std::string &value, int) { + mipsTracer.setLoggingPath(value); + MIPSTracerPath_ = value; MIPSTracerPath->SetRightText(MIPSTracerPath_); - } -#endif + }); return UI::EVENT_DONE; } UI::EventReturn DeveloperToolsScreen::OnMIPSTracerFlushTrace(UI::EventParams &e) { - // Let's ban the non-Windows platforms with force -#if PPSSPP_PLATFORM(WINDOWS) bool success = mipsTracer.flush_to_file(); if (!success) { WARN_LOG(Log::JIT, "Error: cannot flush the trace to the specified file!"); } -#endif return UI::EVENT_DONE; } From c1ad3db1d85a683be0c0a6a79c421fdb88bb95e5 Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Fri, 13 Sep 2024 03:52:30 +0300 Subject: [PATCH 12/16] Switched to PPSSPP's C file-stream API --- Core/MIPS/MIPSTracer.cpp | 12 ++++++------ Core/MIPS/MIPSTracer.h | 10 ++++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index 079edeeb3a4a..8789c998fcba 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -100,8 +100,8 @@ void MIPSTracer::prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& bool MIPSTracer::flush_to_file() { INFO_LOG(Log::JIT, "Flushing the trace to a file..."); - - output.open(logging_path, std::ios::out); + 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; @@ -113,13 +113,14 @@ bool MIPSTracer::flush_to_file() { } INFO_LOG(Log::JIT, "Trace flushed, closing the file..."); - output.close(); + std::fclose(output); + clear(); return true; } void MIPSTracer::flush_block_to_file(TraceBlockInfo& block_info) { - static char buffer[1024]; + 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; @@ -137,8 +138,7 @@ void MIPSTracer::flush_block_to_file(TraceBlockInfo& block_info) { snprintf(buffer, sizeof(buffer), "0x%08x: ", addr); MIPSDisAsm(storage.read_asm(index), addr, buffer + prefix_size, sizeof(buffer) - prefix_size, true); - // TODO: check if removing the std::string makes this faster - output << std::string(buffer) << "\n"; + std::fprintf(output, "%s\n", buffer); } } diff --git a/Core/MIPS/MIPSTracer.h b/Core/MIPS/MIPSTracer.h index c3a03c99df79..4aa612f0e08c 100644 --- a/Core/MIPS/MIPSTracer.h +++ b/Core/MIPS/MIPSTracer.h @@ -27,6 +27,8 @@ #include "Core/Opcode.h" #include "Core/MIPS/IR/IRJit.h" #include "Common/Log.h" +#include "Common/File/Path.h" +#include "Common/File/FileUtil.h" struct TraceBlockInfo { @@ -135,8 +137,8 @@ struct MIPSTracer { TraceBlockStorage storage; - std::string logging_path; - std::ofstream output; + Path logging_path; + FILE* output; bool tracing_enabled = false; int in_storage_capacity = 0x10'0000; @@ -147,10 +149,10 @@ struct MIPSTracer { void prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks); void setLoggingPath(std::string path) { - logging_path = path; + logging_path = Path(path); } std::string getLoggingPath() const { - return logging_path; + return logging_path.ToString(); } bool flush_to_file(); From 21d0f596ec020b3aa3311cf379a87b7f753af4d9 Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Sat, 14 Sep 2024 19:57:39 +0300 Subject: [PATCH 13/16] Cleaned up the includes --- Core/MIPS/MIPSTracer.cpp | 1 + Core/MIPS/MIPSTracer.h | 3 --- UI/GameSettingsScreen.cpp | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index 8789c998fcba..a533a6d8e941 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -20,6 +20,7 @@ #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) { diff --git a/Core/MIPS/MIPSTracer.h b/Core/MIPS/MIPSTracer.h index 4aa612f0e08c..8bb6d75cab54 100644 --- a/Core/MIPS/MIPSTracer.h +++ b/Core/MIPS/MIPSTracer.h @@ -20,15 +20,12 @@ #include #include #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" -#include "Common/File/FileUtil.h" struct TraceBlockInfo { diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 687f65c22dec..197b12e9104f 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -79,7 +79,7 @@ #include "GPU/GPUInterface.h" #include "GPU/Common/FramebufferManagerCommon.h" -#include "Core/Core.h" +#include "Core/Core.h" // for Core_IsStepping #include "Core/MIPS/MIPSTracer.h" #if PPSSPP_PLATFORM(MAC) || PPSSPP_PLATFORM(IOS) From 08b20ce9c8ec863526b9865d00806b68687e2414 Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Sat, 14 Sep 2024 20:16:17 +0300 Subject: [PATCH 14/16] Buildfix for std::back_inserter --- Core/MIPS/IR/IRFrontend.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/MIPS/IR/IRFrontend.cpp b/Core/MIPS/IR/IRFrontend.cpp index 083da746dd4b..6c8e59fa3864 100644 --- a/Core/MIPS/IR/IRFrontend.cpp +++ b/Core/MIPS/IR/IRFrontend.cpp @@ -30,6 +30,7 @@ #include "Core/MIPS/IR/IRInterpreter.h" #include "Core/MIPS/MIPSTracer.h" +#include namespace MIPSComp { From bd6c469543ce679a30724be9a2b9db1c1258c8f5 Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Mon, 16 Sep 2024 02:48:52 +0300 Subject: [PATCH 15/16] Code-review fixes --- Core/MIPS/IR/IRInst.cpp | 2 +- Core/MIPS/MIPSTracer.cpp | 4 ++-- Core/MIPS/MIPSTracer.h | 8 ++++---- UI/GameSettingsScreen.cpp | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index ff235546d3d0..13eb16a52f0f 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -190,7 +190,7 @@ static const IRMeta irMeta[] = { { IROp::ApplyRoundingMode, "ApplyRoundingMode", "" }, { IROp::UpdateRoundingMode, "UpdateRoundingMode", "" }, - {IROp::LogIRBlock, "LogIRBlock", ""} + { IROp::LogIRBlock, "LogIRBlock", "" }, }; const IRMeta *metaIndex[256]; diff --git a/Core/MIPS/MIPSTracer.cpp b/Core/MIPS/MIPSTracer.cpp index a533a6d8e941..20289a5d926b 100644 --- a/Core/MIPS/MIPSTracer.cpp +++ b/Core/MIPS/MIPSTracer.cpp @@ -57,7 +57,7 @@ void TraceBlockStorage::clear() { INFO_LOG(Log::JIT, "TraceBlockStorage cleared"); } -void MIPSTracer::prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks) { +void MIPSTracer::prepare_block(const MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks) { u32 virt_addr, size; block->GetRange(&virt_addr, &size); @@ -120,7 +120,7 @@ bool MIPSTracer::flush_to_file() { return true; } -void MIPSTracer::flush_block_to_file(TraceBlockInfo& block_info) { +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}: ' diff --git a/Core/MIPS/MIPSTracer.h b/Core/MIPS/MIPSTracer.h index 8bb6d75cab54..a3a7ee584edc 100644 --- a/Core/MIPS/MIPSTracer.h +++ b/Core/MIPS/MIPSTracer.h @@ -144,16 +144,16 @@ struct MIPSTracer { void start_tracing(); void stop_tracing(); - void prepare_block(MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks); - void setLoggingPath(std::string path) { + void prepare_block(const MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks); + void set_logging_path(std::string path) { logging_path = Path(path); } - std::string getLoggingPath() const { + std::string get_logging_path() const { return logging_path.ToString(); } bool flush_to_file(); - void flush_block_to_file(TraceBlockInfo& block); + void flush_block_to_file(const TraceBlockInfo& block); void initialize(u32 storage_capacity, u32 max_trace_size); void clear(); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 197b12e9104f..2545438e7109 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -1909,7 +1909,7 @@ void DeveloperToolsScreen::CreateViews() { return true; }); - MIPSTracerPath_ = mipsTracer.getLoggingPath(); + MIPSTracerPath_ = mipsTracer.get_logging_path(); MIPSTracerPath = list->Add(new InfoItem(dev->T("Current log file"), MIPSTracerPath_)); PopupSliderChoice* storage_capacity = list->Add( @@ -2151,7 +2151,7 @@ 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.setLoggingPath(value); + mipsTracer.set_logging_path(value); MIPSTracerPath_ = value; MIPSTracerPath->SetRightText(MIPSTracerPath_); }); From dcf98fe8a6919e97fd93cf8892f06925b1d142d1 Mon Sep 17 00:00:00 2001 From: Nemoumbra Date: Mon, 16 Sep 2024 22:17:08 +0300 Subject: [PATCH 16/16] Addressed the last block issue in a comment --- Core/MIPS/MIPSTracer.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Core/MIPS/MIPSTracer.h b/Core/MIPS/MIPSTracer.h index a3a7ee584edc..8ba1bef6e854 100644 --- a/Core/MIPS/MIPSTracer.h +++ b/Core/MIPS/MIPSTracer.h @@ -124,6 +124,12 @@ 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;