summaryrefslogtreecommitdiff
path: root/src/core/arm/debug.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/arm/debug.cpp')
-rw-r--r--src/core/arm/debug.cpp351
1 files changed, 351 insertions, 0 deletions
diff --git a/src/core/arm/debug.cpp b/src/core/arm/debug.cpp
new file mode 100644
index 000000000..1fe37b8ee
--- /dev/null
+++ b/src/core/arm/debug.cpp
@@ -0,0 +1,351 @@
1// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "common/demangle.h"
5#include "core/arm/debug.h"
6#include "core/arm/symbols.h"
7#include "core/hle/kernel/k_process.h"
8#include "core/hle/kernel/k_thread.h"
9#include "core/memory.h"
10
11namespace Core {
12
13namespace {
14
15std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory,
16 const Kernel::KThread& thread) {
17 // Read thread type from TLS
18 const VAddr tls_thread_type{memory.Read64(thread.GetTlsAddress() + 0x1f8)};
19 const VAddr argument_thread_type{thread.GetArgument()};
20
21 if (argument_thread_type && tls_thread_type != argument_thread_type) {
22 // Probably not created by nnsdk, no name available.
23 return std::nullopt;
24 }
25
26 if (!tls_thread_type) {
27 return std::nullopt;
28 }
29
30 const u16 version{memory.Read16(tls_thread_type + 0x46)};
31 VAddr name_pointer{};
32 if (version == 1) {
33 name_pointer = memory.Read64(tls_thread_type + 0x1a0);
34 } else {
35 name_pointer = memory.Read64(tls_thread_type + 0x1a8);
36 }
37
38 if (!name_pointer) {
39 // No name provided.
40 return std::nullopt;
41 }
42
43 return memory.ReadCString(name_pointer, 256);
44}
45
46std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory,
47 const Kernel::KThread& thread) {
48 // Read thread type from TLS
49 const VAddr tls_thread_type{memory.Read32(thread.GetTlsAddress() + 0x1fc)};
50 const VAddr argument_thread_type{thread.GetArgument()};
51
52 if (argument_thread_type && tls_thread_type != argument_thread_type) {
53 // Probably not created by nnsdk, no name available.
54 return std::nullopt;
55 }
56
57 if (!tls_thread_type) {
58 return std::nullopt;
59 }
60
61 const u16 version{memory.Read16(tls_thread_type + 0x26)};
62 VAddr name_pointer{};
63 if (version == 1) {
64 name_pointer = memory.Read32(tls_thread_type + 0xe4);
65 } else {
66 name_pointer = memory.Read32(tls_thread_type + 0xe8);
67 }
68
69 if (!name_pointer) {
70 // No name provided.
71 return std::nullopt;
72 }
73
74 return memory.ReadCString(name_pointer, 256);
75}
76
77constexpr std::array<u64, 2> SegmentBases{
78 0x60000000ULL,
79 0x7100000000ULL,
80};
81
82void SymbolicateBacktrace(const Kernel::KProcess* process, std::vector<BacktraceEntry>& out) {
83 auto modules = FindModules(process);
84
85 const bool is_64 = process->Is64Bit();
86
87 std::map<std::string, Symbols::Symbols> symbols;
88 for (const auto& module : modules) {
89 symbols.insert_or_assign(module.second,
90 Symbols::GetSymbols(module.first, process->GetMemory(), is_64));
91 }
92
93 for (auto& entry : out) {
94 VAddr base = 0;
95 for (auto iter = modules.rbegin(); iter != modules.rend(); ++iter) {
96 const auto& module{*iter};
97 if (entry.original_address >= module.first) {
98 entry.module = module.second;
99 base = module.first;
100 break;
101 }
102 }
103
104 entry.offset = entry.original_address - base;
105 entry.address = SegmentBases[is_64] + entry.offset;
106
107 if (entry.module.empty()) {
108 entry.module = "unknown";
109 }
110
111 const auto symbol_set = symbols.find(entry.module);
112 if (symbol_set != symbols.end()) {
113 const auto symbol = Symbols::GetSymbolName(symbol_set->second, entry.offset);
114 if (symbol) {
115 entry.name = Common::DemangleSymbol(*symbol);
116 }
117 }
118 }
119}
120
121std::vector<BacktraceEntry> GetAArch64Backtrace(const Kernel::KProcess* process,
122 const Kernel::Svc::ThreadContext& ctx) {
123 std::vector<BacktraceEntry> out;
124 auto& memory = process->GetMemory();
125 auto pc = ctx.pc, lr = ctx.lr, fp = ctx.fp;
126
127 out.push_back({"", 0, pc, 0, ""});
128
129 // fp (= x29) points to the previous frame record.
130 // Frame records are two words long:
131 // fp+0 : pointer to previous frame record
132 // fp+8 : value of lr for frame
133 for (size_t i = 0; i < 256; i++) {
134 out.push_back({"", 0, lr, 0, ""});
135 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 16)) {
136 break;
137 }
138 lr = memory.Read64(fp + 8);
139 fp = memory.Read64(fp);
140 }
141
142 SymbolicateBacktrace(process, out);
143
144 return out;
145}
146
147std::vector<BacktraceEntry> GetAArch32Backtrace(const Kernel::KProcess* process,
148 const Kernel::Svc::ThreadContext& ctx) {
149 std::vector<BacktraceEntry> out;
150 auto& memory = process->GetMemory();
151 auto pc = ctx.pc, lr = ctx.lr, fp = ctx.fp;
152
153 out.push_back({"", 0, pc, 0, ""});
154
155 // fp (= r11) points to the last frame record.
156 // Frame records are two words long:
157 // fp+0 : pointer to previous frame record
158 // fp+4 : value of lr for frame
159 for (size_t i = 0; i < 256; i++) {
160 out.push_back({"", 0, lr, 0, ""});
161 if (!fp || (fp % 4 != 0) || !memory.IsValidVirtualAddressRange(fp, 8)) {
162 break;
163 }
164 lr = memory.Read32(fp + 4);
165 fp = memory.Read32(fp);
166 }
167
168 SymbolicateBacktrace(process, out);
169
170 return out;
171}
172
173} // namespace
174
175std::optional<std::string> GetThreadName(const Kernel::KThread* thread) {
176 const auto* process = thread->GetOwnerProcess();
177 if (process->Is64Bit()) {
178 return GetNameFromThreadType64(process->GetMemory(), *thread);
179 } else {
180 return GetNameFromThreadType32(process->GetMemory(), *thread);
181 }
182}
183
184std::string_view GetThreadWaitReason(const Kernel::KThread* thread) {
185 switch (thread->GetWaitReasonForDebugging()) {
186 case Kernel::ThreadWaitReasonForDebugging::Sleep:
187 return "Sleep";
188 case Kernel::ThreadWaitReasonForDebugging::IPC:
189 return "IPC";
190 case Kernel::ThreadWaitReasonForDebugging::Synchronization:
191 return "Synchronization";
192 case Kernel::ThreadWaitReasonForDebugging::ConditionVar:
193 return "ConditionVar";
194 case Kernel::ThreadWaitReasonForDebugging::Arbitration:
195 return "Arbitration";
196 case Kernel::ThreadWaitReasonForDebugging::Suspended:
197 return "Suspended";
198 default:
199 return "Unknown";
200 }
201}
202
203std::string GetThreadState(const Kernel::KThread* thread) {
204 switch (thread->GetState()) {
205 case Kernel::ThreadState::Initialized:
206 return "Initialized";
207 case Kernel::ThreadState::Waiting:
208 return fmt::format("Waiting ({})", GetThreadWaitReason(thread));
209 case Kernel::ThreadState::Runnable:
210 return "Runnable";
211 case Kernel::ThreadState::Terminated:
212 return "Terminated";
213 default:
214 return "Unknown";
215 }
216}
217
218Kernel::KProcessAddress GetModuleEnd(const Kernel::KProcess* process,
219 Kernel::KProcessAddress base) {
220 Kernel::KMemoryInfo mem_info;
221 Kernel::Svc::MemoryInfo svc_mem_info;
222 Kernel::Svc::PageInfo page_info;
223 VAddr cur_addr{GetInteger(base)};
224 auto& page_table = process->GetPageTable();
225
226 // Expect: r-x Code (.text)
227 R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
228 svc_mem_info = mem_info.GetSvcMemoryInfo();
229 cur_addr = svc_mem_info.base_address + svc_mem_info.size;
230 if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
231 svc_mem_info.permission != Kernel::Svc::MemoryPermission::ReadExecute) {
232 return cur_addr - 1;
233 }
234
235 // Expect: r-- Code (.rodata)
236 R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
237 svc_mem_info = mem_info.GetSvcMemoryInfo();
238 cur_addr = svc_mem_info.base_address + svc_mem_info.size;
239 if (svc_mem_info.state != Kernel::Svc::MemoryState::Code ||
240 svc_mem_info.permission != Kernel::Svc::MemoryPermission::Read) {
241 return cur_addr - 1;
242 }
243
244 // Expect: rw- CodeData (.data)
245 R_ASSERT(page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
246 svc_mem_info = mem_info.GetSvcMemoryInfo();
247 cur_addr = svc_mem_info.base_address + svc_mem_info.size;
248 return cur_addr - 1;
249}
250
251Loader::AppLoader::Modules FindModules(const Kernel::KProcess* process) {
252 Loader::AppLoader::Modules modules;
253
254 auto& page_table = process->GetPageTable();
255 auto& memory = process->GetMemory();
256 VAddr cur_addr = 0;
257
258 // Look for executable sections in Code or AliasCode regions.
259 while (true) {
260 Kernel::KMemoryInfo mem_info{};
261 Kernel::Svc::PageInfo page_info{};
262 R_ASSERT(
263 page_table.QueryInfo(std::addressof(mem_info), std::addressof(page_info), cur_addr));
264 auto svc_mem_info = mem_info.GetSvcMemoryInfo();
265
266 if (svc_mem_info.permission == Kernel::Svc::MemoryPermission::ReadExecute &&
267 (svc_mem_info.state == Kernel::Svc::MemoryState::Code ||
268 svc_mem_info.state == Kernel::Svc::MemoryState::AliasCode)) {
269 // Try to read the module name from its path.
270 constexpr s32 PathLengthMax = 0x200;
271 struct {
272 u32 zero;
273 s32 path_length;
274 std::array<char, PathLengthMax> path;
275 } module_path;
276
277 if (memory.ReadBlock(svc_mem_info.base_address + svc_mem_info.size, &module_path,
278 sizeof(module_path))) {
279 if (module_path.zero == 0 && module_path.path_length > 0) {
280 // Truncate module name.
281 module_path.path[PathLengthMax - 1] = '\0';
282
283 // Ignore leading directories.
284 char* path_pointer = module_path.path.data();
285
286 for (s32 i = 0; i < std::min(PathLengthMax, module_path.path_length) &&
287 module_path.path[i] != '\0';
288 i++) {
289 if (module_path.path[i] == '/' || module_path.path[i] == '\\') {
290 path_pointer = module_path.path.data() + i + 1;
291 }
292 }
293
294 // Insert output.
295 modules.emplace(svc_mem_info.base_address, path_pointer);
296 }
297 }
298 }
299
300 // Check if we're done.
301 const uintptr_t next_address = svc_mem_info.base_address + svc_mem_info.size;
302 if (next_address <= cur_addr) {
303 break;
304 }
305
306 cur_addr = next_address;
307 }
308
309 return modules;
310}
311
312Kernel::KProcessAddress FindMainModuleEntrypoint(const Kernel::KProcess* process) {
313 // Do we have any loaded executable sections?
314 auto modules = FindModules(process);
315
316 if (modules.size() >= 2) {
317 // If we have two or more, the first one is rtld and the second is main.
318 return std::next(modules.begin())->first;
319 } else if (!modules.empty()) {
320 // If we only have one, this is the main module.
321 return modules.begin()->first;
322 }
323
324 // As a last resort, use the start of the code region.
325 return GetInteger(process->GetPageTable().GetCodeRegionStart());
326}
327
328void InvalidateInstructionCacheRange(const Kernel::KProcess* process, u64 address, u64 size) {
329 for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
330 auto* interface = process->GetArmInterface(i);
331 if (interface) {
332 interface->InvalidateCacheRange(address, size);
333 }
334 }
335}
336
337std::vector<BacktraceEntry> GetBacktraceFromContext(const Kernel::KProcess* process,
338 const Kernel::Svc::ThreadContext& ctx) {
339 if (process->Is64Bit()) {
340 return GetAArch64Backtrace(process, ctx);
341 } else {
342 return GetAArch32Backtrace(process, ctx);
343 }
344}
345
346std::vector<BacktraceEntry> GetBacktrace(const Kernel::KThread* thread) {
347 Kernel::Svc::ThreadContext ctx = thread->GetContext();
348 return GetBacktraceFromContext(thread->GetOwnerProcess(), ctx);
349}
350
351} // namespace Core