#include "Core/MIPS/MIPSTracer.h"
#include <cstring>
#include "Core/MIPS/MIPSTables.h"
#include "Core/MemMap.h"
#include "Common/File/FileUtil.h"
bool TraceBlockStorage::save_block(const u32* instructions, u32 size) {
const auto indexes_count = size / 4;
if (cur_index + 1 + indexes_count >= raw_instructions.size()) {
return false;
}
*cur_data_ptr = size;
++cur_data_ptr;
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()) {
storage_index = it->second;
}
else {
auto mips_instructions_ptr = (const u32*)Memory::GetPointerUnchecked(virt_addr);
storage_index = storage.cur_index;
if (!storage.save_block(mips_instructions_ptr, size)) {
WARN_LOG(Log::JIT, "The MIPSTracer ran out of storage for the blocks, cannot proceed!");
stop_tracing();
return;
}
hash_to_storage_index.emplace(hash, storage_index);
}
trace_info.push_back({ virt_addr, storage_index });
u32 index = (u32)(trace_info.size() - 1);
auto ir_ptr = (IRInst*)blocks.GetBlockInstructionPtr(*block);
ir_ptr[1].constant = index;
}
bool MIPSTracer::flush_to_file() {
if (logging_path.empty()) {
WARN_LOG(Log::JIT, "The path is empty, cannot flush the trace!");
return false;
}
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];
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 {
INFO_LOG(Log::JIT, "=============== MIPSTracer storage ===============");
INFO_LOG(Log::JIT, "Current index = %d, storage size = %d", storage.cur_index, (int)storage.raw_instructions.size());
if (executed_blocks.overflow) {
INFO_LOG(Log::JIT, "=============== MIPSTracer cyclic buffer (overflow) ===============");
INFO_LOG(Log::JIT, "Trace size = %d, starts from index %d", (int)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);
}
INFO_LOG(Log::JIT, "=============== MIPSTracer hashes ===============");
INFO_LOG(Log::JIT, "Number of unique hashes = %d", (int)hash_to_storage_index.size());
INFO_LOG(Log::JIT, "=============== MIPSTracer basic block list ===============");
INFO_LOG(Log::JIT, "Number of processed basic blocks = %d", (int)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;