-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19457 from Nemoumbra/ir-mipslogger
Tracing support for the IR Interpreter
- Loading branch information
Showing
18 changed files
with
552 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <cstring> // 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; | ||
|
||
|
Oops, something went wrong.