CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/MIPS/MIPSTracer.cpp
Views: 1401
1
// Copyright (c) 2024- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include "Core/MIPS/MIPSTracer.h"
19
20
#include <cstring> // for std::memcpy
21
#include "Core/MIPS/MIPSTables.h" // for MIPSDisAsm
22
#include "Core/MemMap.h" // for Memory::GetPointerUnchecked
23
#include "Common/File/FileUtil.h" // for the File::OpenCFile
24
25
26
bool TraceBlockStorage::save_block(const u32* instructions, u32 size) {
27
// 'size' is measured in bytes
28
const auto indexes_count = size / 4;
29
30
if (cur_index + 1 + indexes_count >= raw_instructions.size()) {
31
return false;
32
}
33
34
// Save the size first
35
*cur_data_ptr = size;
36
++cur_data_ptr;
37
38
// Now save the MIPS instructions
39
std::memcpy(cur_data_ptr, instructions, size);
40
cur_data_ptr += indexes_count;
41
42
cur_index += 1 + indexes_count;
43
return true;
44
}
45
46
void TraceBlockStorage::initialize(u32 capacity) {
47
raw_instructions.resize(capacity);
48
cur_index = 0;
49
cur_data_ptr = raw_instructions.data();
50
INFO_LOG(Log::JIT, "TraceBlockStorage initialized: capacity=0x%x", capacity);
51
}
52
53
void TraceBlockStorage::clear() {
54
raw_instructions.clear();
55
cur_index = 0;
56
cur_data_ptr = nullptr;
57
INFO_LOG(Log::JIT, "TraceBlockStorage cleared");
58
}
59
60
void MIPSTracer::prepare_block(const MIPSComp::IRBlock* block, MIPSComp::IRBlockCache& blocks) {
61
u32 virt_addr, size;
62
block->GetRange(&virt_addr, &size);
63
64
u64 hash = block->GetHash();
65
auto it = hash_to_storage_index.find(hash);
66
67
u32 storage_index;
68
if (it != hash_to_storage_index.end()) {
69
// We've seen this one before => it's saved in our storage
70
storage_index = it->second;
71
}
72
else {
73
// We haven't seen a block like that before, let's save it
74
auto mips_instructions_ptr = (const u32*)Memory::GetPointerUnchecked(virt_addr);
75
76
storage_index = storage.cur_index;
77
if (!storage.save_block(mips_instructions_ptr, size)) {
78
// We ran out of storage!
79
WARN_LOG(Log::JIT, "The MIPSTracer ran out of storage for the blocks, cannot proceed!");
80
stop_tracing();
81
return;
82
}
83
// Successfully inserted the block at index 'storage_index'!
84
85
hash_to_storage_index.emplace(hash, storage_index);
86
}
87
88
// NB!
89
// If for some reason the blocks get invalidated while tracing, PPSSPP will be forced to recompile
90
// the same code again => the 'trace_info' will be filled with duplicates, because we can't detect that...
91
// If we store the TraceBlockInfo instances in an unordered_map, we won't be able to reference the entries
92
// by using the 4 byte IRInst field 'constant' (the iterators won't fit there).
93
// And, of course, doing a linear search in the vector is not worth the conserved space.
94
trace_info.push_back({ virt_addr, storage_index });
95
96
97
u32 index = (u32)(trace_info.size() - 1);
98
auto ir_ptr = (IRInst*)blocks.GetBlockInstructionPtr(*block);
99
ir_ptr[1].constant = index;
100
}
101
102
bool MIPSTracer::flush_to_file() {
103
if (logging_path.empty()) {
104
WARN_LOG(Log::JIT, "The path is empty, cannot flush the trace!");
105
return false;
106
}
107
108
INFO_LOG(Log::JIT, "Flushing the trace to a file...");
109
output = File::OpenCFile(logging_path, "w");
110
111
if (!output) {
112
WARN_LOG(Log::JIT, "MIPSTracer failed to open the file '%s'", logging_path.c_str());
113
return false;
114
}
115
auto trace = executed_blocks.get_content();
116
for (auto index : trace) {
117
auto& block_info = trace_info[index];
118
flush_block_to_file(block_info);
119
}
120
121
INFO_LOG(Log::JIT, "Trace flushed, closing the file...");
122
std::fclose(output);
123
124
clear();
125
return true;
126
}
127
128
void MIPSTracer::flush_block_to_file(const TraceBlockInfo& block_info) {
129
char buffer[512];
130
131
// The log format is '{prefix}{disassembled line}', where 'prefix' is '0x{8 hex digits of the address}: '
132
const auto prefix_size = 2 + 8 + 2;
133
134
u32 addr = block_info.virt_address;
135
u32 index = block_info.storage_index;
136
137
u32 size = storage[index];
138
++index;
139
140
u32 end_addr = addr + size;
141
142
143
for (; addr < end_addr; addr += 4, ++index) {
144
snprintf(buffer, sizeof(buffer), "0x%08x: ", addr);
145
MIPSDisAsm(storage.read_asm(index), addr, buffer + prefix_size, sizeof(buffer) - prefix_size, true);
146
147
std::fprintf(output, "%s\n", buffer);
148
}
149
}
150
151
void MIPSTracer::start_tracing() {
152
if (!tracing_enabled) {
153
INFO_LOG(Log::JIT, "MIPSTracer enabled");
154
tracing_enabled = true;
155
}
156
}
157
158
void MIPSTracer::stop_tracing() {
159
if (tracing_enabled) {
160
INFO_LOG(Log::JIT, "MIPSTracer disabled");
161
tracing_enabled = false;
162
163
#ifdef _DEBUG
164
print_stats();
165
#endif
166
}
167
}
168
169
inline void MIPSTracer::print_stats() const {
170
// First, the storage
171
INFO_LOG(Log::JIT, "=============== MIPSTracer storage ===============");
172
INFO_LOG(Log::JIT, "Current index = %d, storage size = %d", storage.cur_index, (int)storage.raw_instructions.size());
173
174
// Then the cyclic buffer
175
if (executed_blocks.overflow) {
176
INFO_LOG(Log::JIT, "=============== MIPSTracer cyclic buffer (overflow) ===============");
177
INFO_LOG(Log::JIT, "Trace size = %d, starts from index %d", (int)executed_blocks.buffer.size(), executed_blocks.current_index);
178
}
179
else {
180
INFO_LOG(Log::JIT, "=============== MIPSTracer cyclic buffer (no overflow) ===============");
181
INFO_LOG(Log::JIT, "Trace size = %d, starts from index 0", executed_blocks.current_index);
182
}
183
// Next, the hash-to-index mapping
184
INFO_LOG(Log::JIT, "=============== MIPSTracer hashes ===============");
185
INFO_LOG(Log::JIT, "Number of unique hashes = %d", (int)hash_to_storage_index.size());
186
187
// Finally, the basic block list
188
INFO_LOG(Log::JIT, "=============== MIPSTracer basic block list ===============");
189
INFO_LOG(Log::JIT, "Number of processed basic blocks = %d", (int)trace_info.size());
190
191
INFO_LOG(Log::JIT, "=============== MIPSTracer stats end ===============");
192
}
193
194
void MIPSTracer::initialize(u32 storage_capacity, u32 max_trace_size) {
195
executed_blocks.resize(max_trace_size);
196
hash_to_storage_index.reserve(max_trace_size);
197
storage.initialize(storage_capacity);
198
trace_info.reserve(max_trace_size);
199
INFO_LOG(Log::JIT, "MIPSTracer initialized: storage_capacity=0x%x, max_trace_size=%d", storage_capacity, max_trace_size);
200
}
201
202
void MIPSTracer::clear() {
203
executed_blocks.clear();
204
hash_to_storage_index.clear();
205
storage.clear();
206
trace_info.clear();
207
INFO_LOG(Log::JIT, "MIPSTracer cleared");
208
}
209
210
MIPSTracer mipsTracer;
211
212
213
214