CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/MIPS/MIPSTracer.cpp
Views: 1401
// Copyright (c) 2024- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "Core/MIPS/MIPSTracer.h"1819#include <cstring> // for std::memcpy20#include "Core/MIPS/MIPSTables.h" // for MIPSDisAsm21#include "Core/MemMap.h" // for Memory::GetPointerUnchecked22#include "Common/File/FileUtil.h" // for the File::OpenCFile232425bool TraceBlockStorage::save_block(const u32* instructions, u32 size) {26// 'size' is measured in bytes27const auto indexes_count = size / 4;2829if (cur_index + 1 + indexes_count >= raw_instructions.size()) {30return false;31}3233// Save the size first34*cur_data_ptr = size;35++cur_data_ptr;3637// Now save the MIPS instructions38std::memcpy(cur_data_ptr, instructions, size);39cur_data_ptr += indexes_count;4041cur_index += 1 + indexes_count;42return true;43}4445void TraceBlockStorage::initialize(u32 capacity) {46raw_instructions.resize(capacity);47cur_index = 0;48cur_data_ptr = raw_instructions.data();49INFO_LOG(Log::JIT, "TraceBlockStorage initialized: capacity=0x%x", capacity);50}5152void TraceBlockStorage::clear() {53raw_instructions.clear();54cur_index = 0;55cur_data_ptr = nullptr;56INFO_LOG(Log::JIT, "TraceBlockStorage cleared");57}5859void MIPSTracer::prepare_block(const MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks) {60u32 virt_addr, size;61block->GetRange(&virt_addr, &size);6263u64 hash = block->GetHash();64auto it = hash_to_storage_index.find(hash);6566u32 storage_index;67if (it != hash_to_storage_index.end()) {68// We've seen this one before => it's saved in our storage69storage_index = it->second;70}71else {72// We haven't seen a block like that before, let's save it73auto mips_instructions_ptr = (const u32*)Memory::GetPointerUnchecked(virt_addr);7475storage_index = storage.cur_index;76if (!storage.save_block(mips_instructions_ptr, size)) {77// We ran out of storage!78WARN_LOG(Log::JIT, "The MIPSTracer ran out of storage for the blocks, cannot proceed!");79stop_tracing();80return;81}82// Successfully inserted the block at index 'storage_index'!8384hash_to_storage_index.emplace(hash, storage_index);85}8687// NB!88// If for some reason the blocks get invalidated while tracing, PPSSPP will be forced to recompile89// the same code again => the 'trace_info' will be filled with duplicates, because we can't detect that...90// If we store the TraceBlockInfo instances in an unordered_map, we won't be able to reference the entries91// by using the 4 byte IRInst field 'constant' (the iterators won't fit there).92// And, of course, doing a linear search in the vector is not worth the conserved space.93trace_info.push_back({ virt_addr, storage_index });949596u32 index = (u32)(trace_info.size() - 1);97auto ir_ptr = (IRInst*)blocks.GetBlockInstructionPtr(*block);98ir_ptr[1].constant = index;99}100101bool MIPSTracer::flush_to_file() {102if (logging_path.empty()) {103WARN_LOG(Log::JIT, "The path is empty, cannot flush the trace!");104return false;105}106107INFO_LOG(Log::JIT, "Flushing the trace to a file...");108output = File::OpenCFile(logging_path, "w");109110if (!output) {111WARN_LOG(Log::JIT, "MIPSTracer failed to open the file '%s'", logging_path.c_str());112return false;113}114auto trace = executed_blocks.get_content();115for (auto index : trace) {116auto& block_info = trace_info[index];117flush_block_to_file(block_info);118}119120INFO_LOG(Log::JIT, "Trace flushed, closing the file...");121std::fclose(output);122123clear();124return true;125}126127void MIPSTracer::flush_block_to_file(const TraceBlockInfo& block_info) {128char buffer[512];129130// The log format is '{prefix}{disassembled line}', where 'prefix' is '0x{8 hex digits of the address}: '131const auto prefix_size = 2 + 8 + 2;132133u32 addr = block_info.virt_address;134u32 index = block_info.storage_index;135136u32 size = storage[index];137++index;138139u32 end_addr = addr + size;140141142for (; addr < end_addr; addr += 4, ++index) {143snprintf(buffer, sizeof(buffer), "0x%08x: ", addr);144MIPSDisAsm(storage.read_asm(index), addr, buffer + prefix_size, sizeof(buffer) - prefix_size, true);145146std::fprintf(output, "%s\n", buffer);147}148}149150void MIPSTracer::start_tracing() {151if (!tracing_enabled) {152INFO_LOG(Log::JIT, "MIPSTracer enabled");153tracing_enabled = true;154}155}156157void MIPSTracer::stop_tracing() {158if (tracing_enabled) {159INFO_LOG(Log::JIT, "MIPSTracer disabled");160tracing_enabled = false;161162#ifdef _DEBUG163print_stats();164#endif165}166}167168inline void MIPSTracer::print_stats() const {169// First, the storage170INFO_LOG(Log::JIT, "=============== MIPSTracer storage ===============");171INFO_LOG(Log::JIT, "Current index = %d, storage size = %d", storage.cur_index, (int)storage.raw_instructions.size());172173// Then the cyclic buffer174if (executed_blocks.overflow) {175INFO_LOG(Log::JIT, "=============== MIPSTracer cyclic buffer (overflow) ===============");176INFO_LOG(Log::JIT, "Trace size = %d, starts from index %d", (int)executed_blocks.buffer.size(), executed_blocks.current_index);177}178else {179INFO_LOG(Log::JIT, "=============== MIPSTracer cyclic buffer (no overflow) ===============");180INFO_LOG(Log::JIT, "Trace size = %d, starts from index 0", executed_blocks.current_index);181}182// Next, the hash-to-index mapping183INFO_LOG(Log::JIT, "=============== MIPSTracer hashes ===============");184INFO_LOG(Log::JIT, "Number of unique hashes = %d", (int)hash_to_storage_index.size());185186// Finally, the basic block list187INFO_LOG(Log::JIT, "=============== MIPSTracer basic block list ===============");188INFO_LOG(Log::JIT, "Number of processed basic blocks = %d", (int)trace_info.size());189190INFO_LOG(Log::JIT, "=============== MIPSTracer stats end ===============");191}192193void MIPSTracer::initialize(u32 storage_capacity, u32 max_trace_size) {194executed_blocks.resize(max_trace_size);195hash_to_storage_index.reserve(max_trace_size);196storage.initialize(storage_capacity);197trace_info.reserve(max_trace_size);198INFO_LOG(Log::JIT, "MIPSTracer initialized: storage_capacity=0x%x, max_trace_size=%d", storage_capacity, max_trace_size);199}200201void MIPSTracer::clear() {202executed_blocks.clear();203hash_to_storage_index.clear();204storage.clear();205trace_info.clear();206INFO_LOG(Log::JIT, "MIPSTracer cleared");207}208209MIPSTracer mipsTracer;210211212213214