1 | //===-- MinidumpFileBuilder.cpp -------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "MinidumpFileBuilder.h" |
10 | |
11 | #include "Plugins/Process/minidump/RegisterContextMinidump_ARM64.h" |
12 | #include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h" |
13 | |
14 | #include "lldb/Core/Module.h" |
15 | #include "lldb/Core/ModuleList.h" |
16 | #include "lldb/Core/Section.h" |
17 | #include "lldb/Target/ABI.h" |
18 | #include "lldb/Target/MemoryRegionInfo.h" |
19 | #include "lldb/Target/Process.h" |
20 | #include "lldb/Target/RegisterContext.h" |
21 | #include "lldb/Target/StopInfo.h" |
22 | #include "lldb/Target/ThreadList.h" |
23 | #include "lldb/Utility/DataBufferHeap.h" |
24 | #include "lldb/Utility/DataExtractor.h" |
25 | #include "lldb/Utility/LLDBLog.h" |
26 | #include "lldb/Utility/Log.h" |
27 | #include "lldb/Utility/RangeMap.h" |
28 | #include "lldb/Utility/RegisterValue.h" |
29 | |
30 | #include "llvm/ADT/StringRef.h" |
31 | #include "llvm/BinaryFormat/Minidump.h" |
32 | #include "llvm/Support/ConvertUTF.h" |
33 | #include "llvm/Support/Endian.h" |
34 | #include "llvm/Support/Error.h" |
35 | #include "llvm/TargetParser/Triple.h" |
36 | |
37 | #include "Plugins/Process/minidump/MinidumpTypes.h" |
38 | #include "lldb/lldb-enumerations.h" |
39 | #include "lldb/lldb-forward.h" |
40 | #include "lldb/lldb-types.h" |
41 | |
42 | #include <algorithm> |
43 | #include <cinttypes> |
44 | #include <cstddef> |
45 | #include <cstdint> |
46 | #include <utility> |
47 | |
48 | using namespace lldb; |
49 | using namespace lldb_private; |
50 | using namespace llvm::minidump; |
51 | |
52 | Status MinidumpFileBuilder::AddHeaderAndCalculateDirectories() { |
53 | // First set the offset on the file, and on the bytes saved |
54 | m_saved_data_size = HEADER_SIZE; |
55 | // We know we will have at least Misc, SystemInfo, Modules, and ThreadList |
56 | // (corresponding memory list for stacks), an additional memory list for |
57 | // non-stacks, and a stream to mark this minidump was generated by LLDB. |
58 | lldb_private::Target &target = m_process_sp->GetTarget(); |
59 | m_expected_directories = 6; |
60 | // Check if OS is linux and reserve directory space for all linux specific |
61 | // breakpad extension directories. |
62 | if (target.GetArchitecture().GetTriple().getOS() == |
63 | llvm::Triple::OSType::Linux) |
64 | m_expected_directories += 9; |
65 | |
66 | // Go through all of the threads and check for exceptions. |
67 | std::vector<lldb::ThreadSP> threads = |
68 | m_process_sp->CalculateCoreFileThreadList(core_options: m_save_core_options); |
69 | for (const ThreadSP &thread_sp : threads) { |
70 | StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); |
71 | if (stop_info_sp) { |
72 | const StopReason &stop_reason = stop_info_sp->GetStopReason(); |
73 | if (stop_reason != lldb::eStopReasonInvalid) |
74 | m_expected_directories++; |
75 | } |
76 | } |
77 | |
78 | // Add a generous buffer of directories, these are quite small |
79 | // and forks may add new directories upstream LLDB hadn't accounted for |
80 | // when we started pre-calculating directory size, so this should account for |
81 | // that |
82 | m_expected_directories += 100; |
83 | |
84 | m_saved_data_size += |
85 | m_expected_directories * sizeof(llvm::minidump::Directory); |
86 | Status error; |
87 | offset_t new_offset = m_core_file->SeekFromStart(offset: m_saved_data_size); |
88 | if (new_offset != m_saved_data_size) |
89 | error = Status::FromErrorStringWithFormat( |
90 | format: "Failed to fill in header and directory " |
91 | "sections. Written / Expected (%" PRIx64 " / %" PRIx64 ")" , |
92 | new_offset, m_saved_data_size); |
93 | |
94 | if (error.Fail()) |
95 | return error; |
96 | |
97 | return AddLLDBGeneratedStream(); |
98 | } |
99 | |
100 | Status MinidumpFileBuilder::AddDirectory(StreamType type, |
101 | uint64_t stream_size) { |
102 | // We explicitly cast type, an 32b enum, to uint32_t to avoid warnings. |
103 | Status error; |
104 | if (GetCurrentDataEndOffset() > UINT32_MAX) { |
105 | error = Status::FromErrorStringWithFormat( |
106 | format: "Unable to add directory for stream type " |
107 | "%x, offset is greater then 32 bit limit." , |
108 | (uint32_t)type); |
109 | return error; |
110 | } |
111 | |
112 | if (m_directories.size() + 1 > m_expected_directories) { |
113 | error = Status::FromErrorStringWithFormat( |
114 | format: "Unable to add directory for stream type %x, exceeded expected number " |
115 | "of directories %zu." , |
116 | (uint32_t)type, m_expected_directories); |
117 | return error; |
118 | } |
119 | |
120 | LocationDescriptor loc; |
121 | loc.DataSize = static_cast<llvm::support::ulittle32_t>(stream_size); |
122 | // Stream will begin at the current end of data section |
123 | loc.RVA = static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset()); |
124 | |
125 | Directory dir; |
126 | dir.Type = static_cast<llvm::support::little_t<StreamType>>(type); |
127 | dir.Location = loc; |
128 | |
129 | m_directories.push_back(x: dir); |
130 | return error; |
131 | } |
132 | |
133 | Status MinidumpFileBuilder::AddLLDBGeneratedStream() { |
134 | Status error; |
135 | StreamType type = StreamType::LLDBGenerated; |
136 | return AddDirectory(type, stream_size: 0); |
137 | } |
138 | |
139 | Status MinidumpFileBuilder::AddSystemInfo() { |
140 | Status error; |
141 | const llvm::Triple &target_triple = |
142 | m_process_sp->GetTarget().GetArchitecture().GetTriple(); |
143 | error = |
144 | AddDirectory(type: StreamType::SystemInfo, stream_size: sizeof(llvm::minidump::SystemInfo)); |
145 | if (error.Fail()) |
146 | return error; |
147 | |
148 | llvm::minidump::ProcessorArchitecture arch; |
149 | switch (target_triple.getArch()) { |
150 | case llvm::Triple::ArchType::x86_64: |
151 | arch = ProcessorArchitecture::AMD64; |
152 | break; |
153 | case llvm::Triple::ArchType::x86: |
154 | arch = ProcessorArchitecture::X86; |
155 | break; |
156 | case llvm::Triple::ArchType::arm: |
157 | arch = ProcessorArchitecture::ARM; |
158 | break; |
159 | case llvm::Triple::ArchType::aarch64: |
160 | arch = ProcessorArchitecture::ARM64; |
161 | break; |
162 | case llvm::Triple::ArchType::mips64: |
163 | case llvm::Triple::ArchType::mips64el: |
164 | case llvm::Triple::ArchType::mips: |
165 | case llvm::Triple::ArchType::mipsel: |
166 | arch = ProcessorArchitecture::MIPS; |
167 | break; |
168 | case llvm::Triple::ArchType::ppc64: |
169 | case llvm::Triple::ArchType::ppc: |
170 | case llvm::Triple::ArchType::ppc64le: |
171 | arch = ProcessorArchitecture::PPC; |
172 | break; |
173 | default: |
174 | error = Status::FromErrorStringWithFormat( |
175 | format: "Architecture %s not supported." , |
176 | target_triple.getArchName().str().c_str()); |
177 | return error; |
178 | }; |
179 | |
180 | llvm::support::little_t<OSPlatform> platform_id; |
181 | switch (target_triple.getOS()) { |
182 | case llvm::Triple::OSType::Linux: |
183 | if (target_triple.getEnvironment() == |
184 | llvm::Triple::EnvironmentType::Android) |
185 | platform_id = OSPlatform::Android; |
186 | else |
187 | platform_id = OSPlatform::Linux; |
188 | break; |
189 | case llvm::Triple::OSType::Win32: |
190 | platform_id = OSPlatform::Win32NT; |
191 | break; |
192 | case llvm::Triple::OSType::MacOSX: |
193 | platform_id = OSPlatform::MacOSX; |
194 | break; |
195 | case llvm::Triple::OSType::IOS: |
196 | platform_id = OSPlatform::IOS; |
197 | break; |
198 | default: |
199 | error = Status::FromErrorStringWithFormat( |
200 | format: "OS %s not supported." , target_triple.getOSName().str().c_str()); |
201 | return error; |
202 | }; |
203 | |
204 | llvm::minidump::SystemInfo sys_info; |
205 | sys_info.ProcessorArch = |
206 | static_cast<llvm::support::little_t<ProcessorArchitecture>>(arch); |
207 | // Global offset to beginning of a csd_string in a data section |
208 | sys_info.CSDVersionRVA = static_cast<llvm::support::ulittle32_t>( |
209 | GetCurrentDataEndOffset() + sizeof(llvm::minidump::SystemInfo)); |
210 | sys_info.PlatformId = platform_id; |
211 | m_data.AppendData(src: &sys_info, src_len: sizeof(llvm::minidump::SystemInfo)); |
212 | |
213 | std::string csd_string; |
214 | |
215 | error = WriteString(to_write: csd_string, buffer: &m_data); |
216 | if (error.Fail()) { |
217 | error = |
218 | Status::FromErrorString(str: "Unable to convert the csd string to UTF16." ); |
219 | return error; |
220 | } |
221 | |
222 | return error; |
223 | } |
224 | |
225 | Status WriteString(const std::string &to_write, |
226 | lldb_private::DataBufferHeap *buffer) { |
227 | Status error; |
228 | // let the StringRef eat also null termination char |
229 | llvm::StringRef to_write_ref(to_write.c_str(), to_write.size() + 1); |
230 | llvm::SmallVector<llvm::UTF16, 128> to_write_utf16; |
231 | |
232 | bool converted = convertUTF8ToUTF16String(SrcUTF8: to_write_ref, DstUTF16&: to_write_utf16); |
233 | if (!converted) { |
234 | error = Status::FromErrorStringWithFormat( |
235 | format: "Unable to convert the string to UTF16. Failed to convert %s" , |
236 | to_write.c_str()); |
237 | return error; |
238 | } |
239 | |
240 | // size of the UTF16 string should be written without the null termination |
241 | // character that is stored in 2 bytes |
242 | llvm::support::ulittle32_t to_write_size(to_write_utf16.size_in_bytes() - 2); |
243 | |
244 | buffer->AppendData(src: &to_write_size, src_len: sizeof(llvm::support::ulittle32_t)); |
245 | buffer->AppendData(src: to_write_utf16.data(), src_len: to_write_utf16.size_in_bytes()); |
246 | |
247 | return error; |
248 | } |
249 | |
250 | llvm::Expected<uint64_t> getModuleFileSize(Target &target, |
251 | const ModuleSP &mod) { |
252 | // JIT module has the same vm and file size. |
253 | uint64_t SizeOfImage = 0; |
254 | if (mod->GetObjectFile()->CalculateType() == ObjectFile::Type::eTypeJIT) { |
255 | for (const auto §ion : *mod->GetObjectFile()->GetSectionList()) { |
256 | SizeOfImage += section->GetByteSize(); |
257 | } |
258 | return SizeOfImage; |
259 | } |
260 | SectionSP sect_sp = mod->GetObjectFile()->GetBaseAddress().GetSection(); |
261 | |
262 | if (!sect_sp) { |
263 | return llvm::createStringError(EC: std::errc::operation_not_supported, |
264 | Fmt: "Couldn't obtain the section information." ); |
265 | } |
266 | lldb::addr_t sect_addr = sect_sp->GetLoadBaseAddress(target: &target); |
267 | // Use memory size since zero fill sections, like ".bss", will be smaller on |
268 | // disk. |
269 | lldb::addr_t sect_size = sect_sp->GetByteSize(); |
270 | // This will usually be zero, but make sure to calculate the BaseOfImage |
271 | // offset. |
272 | const lldb::addr_t base_sect_offset = |
273 | mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(target: &target) - |
274 | sect_addr; |
275 | SizeOfImage = sect_size - base_sect_offset; |
276 | lldb::addr_t next_sect_addr = sect_addr + sect_size; |
277 | Address sect_so_addr; |
278 | target.ResolveLoadAddress(load_addr: next_sect_addr, so_addr&: sect_so_addr); |
279 | lldb::SectionSP next_sect_sp = sect_so_addr.GetSection(); |
280 | while (next_sect_sp && |
281 | next_sect_sp->GetLoadBaseAddress(target: &target) == next_sect_addr) { |
282 | sect_size = sect_sp->GetByteSize(); |
283 | SizeOfImage += sect_size; |
284 | next_sect_addr += sect_size; |
285 | target.ResolveLoadAddress(load_addr: next_sect_addr, so_addr&: sect_so_addr); |
286 | next_sect_sp = sect_so_addr.GetSection(); |
287 | } |
288 | |
289 | return SizeOfImage; |
290 | } |
291 | |
292 | // ModuleList stream consists of a number of modules, followed by an array |
293 | // of llvm::minidump::Module's structures. Every structure informs about a |
294 | // single module. Additional data of variable length, such as module's names, |
295 | // are stored just after the ModuleList stream. The llvm::minidump::Module |
296 | // structures point to this helper data by global offset. |
297 | Status MinidumpFileBuilder::AddModuleList() { |
298 | constexpr size_t minidump_module_size = sizeof(llvm::minidump::Module); |
299 | Status error; |
300 | |
301 | lldb_private::Target &target = m_process_sp->GetTarget(); |
302 | const ModuleList &modules = target.GetImages(); |
303 | llvm::support::ulittle32_t modules_count = |
304 | static_cast<llvm::support::ulittle32_t>(modules.GetSize()); |
305 | |
306 | // This helps us with getting the correct global offset in minidump |
307 | // file later, when we will be setting up offsets from the |
308 | // the llvm::minidump::Module's structures into helper data |
309 | size_t size_before = GetCurrentDataEndOffset(); |
310 | |
311 | // This is the size of the main part of the ModuleList stream. |
312 | // It consists of a module number and corresponding number of |
313 | // structs describing individual modules |
314 | size_t module_stream_size = |
315 | sizeof(llvm::support::ulittle32_t) + modules_count * minidump_module_size; |
316 | |
317 | // Adding directory describing this stream. |
318 | error = AddDirectory(type: StreamType::ModuleList, stream_size: module_stream_size); |
319 | if (error.Fail()) |
320 | return error; |
321 | |
322 | m_data.AppendData(src: &modules_count, src_len: sizeof(llvm::support::ulittle32_t)); |
323 | |
324 | // Temporary storage for the helper data (of variable length) |
325 | // as these cannot be dumped to m_data before dumping entire |
326 | // array of module structures. |
327 | DataBufferHeap helper_data; |
328 | |
329 | for (size_t i = 0; i < modules_count; ++i) { |
330 | ModuleSP mod = modules.GetModuleAtIndex(idx: i); |
331 | std::string module_name = mod->GetSpecificationDescription(); |
332 | auto maybe_mod_size = getModuleFileSize(target, mod); |
333 | if (!maybe_mod_size) { |
334 | llvm::Error mod_size_err = maybe_mod_size.takeError(); |
335 | llvm::handleAllErrors(E: std::move(mod_size_err), |
336 | Handlers: [&](const llvm::ErrorInfoBase &E) { |
337 | error = Status::FromErrorStringWithFormat( |
338 | format: "Unable to get the size of module %s: %s." , |
339 | module_name.c_str(), E.message().c_str()); |
340 | }); |
341 | return error; |
342 | } |
343 | |
344 | uint64_t mod_size = std::move(*maybe_mod_size); |
345 | |
346 | llvm::support::ulittle32_t signature = |
347 | static_cast<llvm::support::ulittle32_t>( |
348 | static_cast<uint32_t>(minidump::CvSignature::ElfBuildId)); |
349 | auto uuid = mod->GetUUID().GetBytes(); |
350 | |
351 | VSFixedFileInfo info; |
352 | info.Signature = static_cast<llvm::support::ulittle32_t>(0u); |
353 | info.StructVersion = static_cast<llvm::support::ulittle32_t>(0u); |
354 | info.FileVersionHigh = static_cast<llvm::support::ulittle32_t>(0u); |
355 | info.FileVersionLow = static_cast<llvm::support::ulittle32_t>(0u); |
356 | info.ProductVersionHigh = static_cast<llvm::support::ulittle32_t>(0u); |
357 | info.ProductVersionLow = static_cast<llvm::support::ulittle32_t>(0u); |
358 | info.FileFlagsMask = static_cast<llvm::support::ulittle32_t>(0u); |
359 | info.FileFlags = static_cast<llvm::support::ulittle32_t>(0u); |
360 | info.FileOS = static_cast<llvm::support::ulittle32_t>(0u); |
361 | info.FileType = static_cast<llvm::support::ulittle32_t>(0u); |
362 | info.FileSubtype = static_cast<llvm::support::ulittle32_t>(0u); |
363 | info.FileDateHigh = static_cast<llvm::support::ulittle32_t>(0u); |
364 | info.FileDateLow = static_cast<llvm::support::ulittle32_t>(0u); |
365 | |
366 | LocationDescriptor ld; |
367 | ld.DataSize = static_cast<llvm::support::ulittle32_t>(0u); |
368 | ld.RVA = static_cast<llvm::support::ulittle32_t>(0u); |
369 | |
370 | // Setting up LocationDescriptor for uuid string. The global offset into |
371 | // minidump file is calculated. |
372 | LocationDescriptor ld_cv; |
373 | ld_cv.DataSize = static_cast<llvm::support::ulittle32_t>( |
374 | sizeof(llvm::support::ulittle32_t) + uuid.size()); |
375 | ld_cv.RVA = static_cast<llvm::support::ulittle32_t>( |
376 | size_before + module_stream_size + helper_data.GetByteSize()); |
377 | |
378 | helper_data.AppendData(src: &signature, src_len: sizeof(llvm::support::ulittle32_t)); |
379 | helper_data.AppendData(src: uuid.begin(), src_len: uuid.size()); |
380 | |
381 | llvm::minidump::Module m; |
382 | m.BaseOfImage = static_cast<llvm::support::ulittle64_t>( |
383 | mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(target: &target)); |
384 | m.SizeOfImage = static_cast<llvm::support::ulittle32_t>(mod_size); |
385 | m.Checksum = static_cast<llvm::support::ulittle32_t>(0); |
386 | m.TimeDateStamp = |
387 | static_cast<llvm::support::ulittle32_t>(std::time(timer: nullptr)); |
388 | m.ModuleNameRVA = static_cast<llvm::support::ulittle32_t>( |
389 | size_before + module_stream_size + helper_data.GetByteSize()); |
390 | m.VersionInfo = info; |
391 | m.CvRecord = ld_cv; |
392 | m.MiscRecord = ld; |
393 | |
394 | error = WriteString(to_write: module_name, buffer: &helper_data); |
395 | |
396 | if (error.Fail()) |
397 | return error; |
398 | |
399 | m_data.AppendData(src: &m, src_len: sizeof(llvm::minidump::Module)); |
400 | } |
401 | |
402 | m_data.AppendData(src: helper_data.GetBytes(), src_len: helper_data.GetByteSize()); |
403 | return error; |
404 | } |
405 | |
406 | uint16_t read_register_u16_raw(RegisterContext *reg_ctx, |
407 | llvm::StringRef reg_name) { |
408 | const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); |
409 | if (!reg_info) |
410 | return 0; |
411 | lldb_private::RegisterValue reg_value; |
412 | bool success = reg_ctx->ReadRegister(reg_info, reg_value); |
413 | if (!success) |
414 | return 0; |
415 | return reg_value.GetAsUInt16(); |
416 | } |
417 | |
418 | uint32_t read_register_u32_raw(RegisterContext *reg_ctx, |
419 | llvm::StringRef reg_name) { |
420 | const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); |
421 | if (!reg_info) |
422 | return 0; |
423 | lldb_private::RegisterValue reg_value; |
424 | bool success = reg_ctx->ReadRegister(reg_info, reg_value); |
425 | if (!success) |
426 | return 0; |
427 | return reg_value.GetAsUInt32(); |
428 | } |
429 | |
430 | uint64_t read_register_u64_raw(RegisterContext *reg_ctx, |
431 | llvm::StringRef reg_name) { |
432 | const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); |
433 | if (!reg_info) |
434 | return 0; |
435 | lldb_private::RegisterValue reg_value; |
436 | bool success = reg_ctx->ReadRegister(reg_info, reg_value); |
437 | if (!success) |
438 | return 0; |
439 | return reg_value.GetAsUInt64(); |
440 | } |
441 | |
442 | llvm::support::ulittle16_t read_register_u16(RegisterContext *reg_ctx, |
443 | llvm::StringRef reg_name) { |
444 | return static_cast<llvm::support::ulittle16_t>( |
445 | read_register_u16_raw(reg_ctx, reg_name)); |
446 | } |
447 | |
448 | llvm::support::ulittle32_t read_register_u32(RegisterContext *reg_ctx, |
449 | llvm::StringRef reg_name) { |
450 | return static_cast<llvm::support::ulittle32_t>( |
451 | read_register_u32_raw(reg_ctx, reg_name)); |
452 | } |
453 | |
454 | llvm::support::ulittle64_t read_register_u64(RegisterContext *reg_ctx, |
455 | llvm::StringRef reg_name) { |
456 | return static_cast<llvm::support::ulittle64_t>( |
457 | read_register_u64_raw(reg_ctx, reg_name)); |
458 | } |
459 | |
460 | void read_register_u128(RegisterContext *reg_ctx, llvm::StringRef reg_name, |
461 | uint8_t *dst) { |
462 | const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); |
463 | if (reg_info) { |
464 | lldb_private::RegisterValue reg_value; |
465 | if (reg_ctx->ReadRegister(reg_info, reg_value)) { |
466 | Status error; |
467 | uint32_t bytes_copied = reg_value.GetAsMemoryData( |
468 | reg_info: *reg_info, dst, dst_len: 16, dst_byte_order: lldb::ByteOrder::eByteOrderLittle, error); |
469 | if (bytes_copied == 16) |
470 | return; |
471 | } |
472 | } |
473 | // If anything goes wrong, then zero out the register value. |
474 | memset(s: dst, c: 0, n: 16); |
475 | } |
476 | |
477 | lldb_private::minidump::MinidumpContext_x86_64 |
478 | GetThreadContext_x86_64(RegisterContext *reg_ctx) { |
479 | lldb_private::minidump::MinidumpContext_x86_64 thread_context = {}; |
480 | thread_context.p1_home = {}; |
481 | thread_context.context_flags = static_cast<uint32_t>( |
482 | lldb_private::minidump::MinidumpContext_x86_64_Flags::x86_64_Flag | |
483 | lldb_private::minidump::MinidumpContext_x86_64_Flags::Control | |
484 | lldb_private::minidump::MinidumpContext_x86_64_Flags::Segments | |
485 | lldb_private::minidump::MinidumpContext_x86_64_Flags::Integer | |
486 | lldb_private::minidump::MinidumpContext_x86_64_Flags::LLDBSpecific); |
487 | thread_context.rax = read_register_u64(reg_ctx, reg_name: "rax" ); |
488 | thread_context.rbx = read_register_u64(reg_ctx, reg_name: "rbx" ); |
489 | thread_context.rcx = read_register_u64(reg_ctx, reg_name: "rcx" ); |
490 | thread_context.rdx = read_register_u64(reg_ctx, reg_name: "rdx" ); |
491 | thread_context.rdi = read_register_u64(reg_ctx, reg_name: "rdi" ); |
492 | thread_context.rsi = read_register_u64(reg_ctx, reg_name: "rsi" ); |
493 | thread_context.rbp = read_register_u64(reg_ctx, reg_name: "rbp" ); |
494 | thread_context.rsp = read_register_u64(reg_ctx, reg_name: "rsp" ); |
495 | thread_context.r8 = read_register_u64(reg_ctx, reg_name: "r8" ); |
496 | thread_context.r9 = read_register_u64(reg_ctx, reg_name: "r9" ); |
497 | thread_context.r10 = read_register_u64(reg_ctx, reg_name: "r10" ); |
498 | thread_context.r11 = read_register_u64(reg_ctx, reg_name: "r11" ); |
499 | thread_context.r12 = read_register_u64(reg_ctx, reg_name: "r12" ); |
500 | thread_context.r13 = read_register_u64(reg_ctx, reg_name: "r13" ); |
501 | thread_context.r14 = read_register_u64(reg_ctx, reg_name: "r14" ); |
502 | thread_context.r15 = read_register_u64(reg_ctx, reg_name: "r15" ); |
503 | thread_context.rip = read_register_u64(reg_ctx, reg_name: "rip" ); |
504 | // To make our code agnostic to whatever type the register value identifies |
505 | // itself as, we read as a u64 and truncate to u32/u16 ourselves. |
506 | thread_context.eflags = read_register_u64(reg_ctx, reg_name: "rflags" ); |
507 | thread_context.cs = read_register_u64(reg_ctx, reg_name: "cs" ); |
508 | thread_context.fs = read_register_u64(reg_ctx, reg_name: "fs" ); |
509 | thread_context.gs = read_register_u64(reg_ctx, reg_name: "gs" ); |
510 | thread_context.ss = read_register_u64(reg_ctx, reg_name: "ss" ); |
511 | thread_context.ds = read_register_u64(reg_ctx, reg_name: "ds" ); |
512 | thread_context.fs_base = read_register_u64(reg_ctx, reg_name: "fs_base" ); |
513 | thread_context.gs_base = read_register_u64(reg_ctx, reg_name: "gs_base" ); |
514 | return thread_context; |
515 | } |
516 | |
517 | minidump::RegisterContextMinidump_ARM64::Context |
518 | GetThreadContext_ARM64(RegisterContext *reg_ctx) { |
519 | minidump::RegisterContextMinidump_ARM64::Context thread_context = {}; |
520 | thread_context.context_flags = static_cast<uint32_t>( |
521 | minidump::RegisterContextMinidump_ARM64::Flags::ARM64_Flag | |
522 | minidump::RegisterContextMinidump_ARM64::Flags::Integer | |
523 | minidump::RegisterContextMinidump_ARM64::Flags::FloatingPoint); |
524 | char reg_name[16]; |
525 | for (uint32_t i = 0; i < 31; ++i) { |
526 | snprintf(s: reg_name, maxlen: sizeof(reg_name), format: "x%u" , i); |
527 | thread_context.x[i] = read_register_u64(reg_ctx, reg_name); |
528 | } |
529 | // Work around a bug in debugserver where "sp" on arm64 doesn't have the alt |
530 | // name set to "x31" |
531 | thread_context.x[31] = read_register_u64(reg_ctx, reg_name: "sp" ); |
532 | thread_context.pc = read_register_u64(reg_ctx, reg_name: "pc" ); |
533 | thread_context.cpsr = read_register_u32(reg_ctx, reg_name: "cpsr" ); |
534 | thread_context.fpsr = read_register_u32(reg_ctx, reg_name: "fpsr" ); |
535 | thread_context.fpcr = read_register_u32(reg_ctx, reg_name: "fpcr" ); |
536 | for (uint32_t i = 0; i < 32; ++i) { |
537 | snprintf(s: reg_name, maxlen: sizeof(reg_name), format: "v%u" , i); |
538 | read_register_u128(reg_ctx, reg_name, dst: &thread_context.v[i * 16]); |
539 | } |
540 | return thread_context; |
541 | } |
542 | |
543 | class ArchThreadContexts { |
544 | llvm::Triple::ArchType m_arch; |
545 | union { |
546 | lldb_private::minidump::MinidumpContext_x86_64 x86_64; |
547 | lldb_private::minidump::RegisterContextMinidump_ARM64::Context arm64; |
548 | }; |
549 | |
550 | public: |
551 | ArchThreadContexts(llvm::Triple::ArchType arch) : m_arch(arch) {} |
552 | |
553 | bool prepareRegisterContext(RegisterContext *reg_ctx) { |
554 | switch (m_arch) { |
555 | case llvm::Triple::ArchType::x86_64: |
556 | x86_64 = GetThreadContext_x86_64(reg_ctx); |
557 | return true; |
558 | case llvm::Triple::ArchType::aarch64: |
559 | arm64 = GetThreadContext_ARM64(reg_ctx); |
560 | return true; |
561 | default: |
562 | break; |
563 | } |
564 | return false; |
565 | } |
566 | |
567 | const void *data() const { return &x86_64; } |
568 | |
569 | size_t size() const { |
570 | switch (m_arch) { |
571 | case llvm::Triple::ArchType::x86_64: |
572 | return sizeof(x86_64); |
573 | case llvm::Triple::ArchType::aarch64: |
574 | return sizeof(arm64); |
575 | default: |
576 | break; |
577 | } |
578 | return 0; |
579 | } |
580 | }; |
581 | |
582 | Status MinidumpFileBuilder::FixThreadStacks() { |
583 | Status error; |
584 | // If we have anything in the heap flush it. |
585 | FlushBufferToDisk(); |
586 | m_core_file->SeekFromStart(offset: m_thread_list_start); |
587 | for (auto &pair : m_thread_by_range_end) { |
588 | // The thread objects will get a new memory descriptor added |
589 | // When we are emitting the memory list and then we write it here |
590 | const llvm::minidump::Thread &thread = pair.second; |
591 | size_t bytes_to_write = sizeof(llvm::minidump::Thread); |
592 | size_t bytes_written = bytes_to_write; |
593 | error = m_core_file->Write(buf: &thread, num_bytes&: bytes_written); |
594 | if (error.Fail() || bytes_to_write != bytes_written) { |
595 | error = Status::FromErrorStringWithFormat( |
596 | format: "Wrote incorrect number of bytes to minidump file. (written %zd/%zd)" , |
597 | bytes_written, bytes_to_write); |
598 | return error; |
599 | } |
600 | } |
601 | |
602 | return error; |
603 | } |
604 | |
605 | Status MinidumpFileBuilder::AddThreadList() { |
606 | constexpr size_t minidump_thread_size = sizeof(llvm::minidump::Thread); |
607 | std::vector<ThreadSP> thread_list = |
608 | m_process_sp->CalculateCoreFileThreadList(core_options: m_save_core_options); |
609 | |
610 | // size of the entire thread stream consists of: |
611 | // number of threads and threads array |
612 | size_t thread_stream_size = sizeof(llvm::support::ulittle32_t) + |
613 | thread_list.size() * minidump_thread_size; |
614 | // save for the ability to set up RVA |
615 | size_t size_before = GetCurrentDataEndOffset(); |
616 | Status error; |
617 | error = AddDirectory(type: StreamType::ThreadList, stream_size: thread_stream_size); |
618 | if (error.Fail()) |
619 | return error; |
620 | |
621 | llvm::support::ulittle32_t thread_count = |
622 | static_cast<llvm::support::ulittle32_t>(thread_list.size()); |
623 | m_data.AppendData(src: &thread_count, src_len: sizeof(llvm::support::ulittle32_t)); |
624 | |
625 | // Take the offset after the thread count. |
626 | m_thread_list_start = GetCurrentDataEndOffset(); |
627 | DataBufferHeap helper_data; |
628 | |
629 | Log *log = GetLog(mask: LLDBLog::Object); |
630 | for (const ThreadSP &thread_sp : thread_list) { |
631 | RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); |
632 | |
633 | if (!reg_ctx_sp) { |
634 | error = Status::FromErrorString(str: "Unable to get the register context." ); |
635 | return error; |
636 | } |
637 | RegisterContext *reg_ctx = reg_ctx_sp.get(); |
638 | Target &target = m_process_sp->GetTarget(); |
639 | const ArchSpec &arch = target.GetArchitecture(); |
640 | ArchThreadContexts thread_context(arch.GetMachine()); |
641 | if (!thread_context.prepareRegisterContext(reg_ctx)) { |
642 | error = Status::FromErrorStringWithFormat( |
643 | format: "architecture %s not supported." , |
644 | arch.GetTriple().getArchName().str().c_str()); |
645 | return error; |
646 | } |
647 | |
648 | uint64_t sp = reg_ctx->GetSP(); |
649 | MemoryRegionInfo sp_region; |
650 | m_process_sp->GetMemoryRegionInfo(load_addr: sp, range_info&: sp_region); |
651 | |
652 | // Emit a blank descriptor |
653 | MemoryDescriptor stack; |
654 | LocationDescriptor empty_label; |
655 | empty_label.DataSize = 0; |
656 | empty_label.RVA = 0; |
657 | stack.Memory = empty_label; |
658 | stack.StartOfMemoryRange = 0; |
659 | LocationDescriptor thread_context_memory_locator; |
660 | thread_context_memory_locator.DataSize = |
661 | static_cast<llvm::support::ulittle32_t>(thread_context.size()); |
662 | thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>( |
663 | size_before + thread_stream_size + helper_data.GetByteSize()); |
664 | // Cache thie thread context memory so we can reuse for exceptions. |
665 | m_tid_to_reg_ctx[thread_sp->GetID()] = thread_context_memory_locator; |
666 | |
667 | LLDB_LOGF(log, "AddThreadList for thread %d: thread_context %zu bytes" , |
668 | thread_sp->GetIndexID(), thread_context.size()); |
669 | helper_data.AppendData(src: thread_context.data(), src_len: thread_context.size()); |
670 | |
671 | llvm::minidump::Thread t; |
672 | t.ThreadId = static_cast<llvm::support::ulittle32_t>(thread_sp->GetID()); |
673 | t.SuspendCount = static_cast<llvm::support::ulittle32_t>( |
674 | (thread_sp->GetState() == StateType::eStateSuspended) ? 1 : 0); |
675 | t.PriorityClass = static_cast<llvm::support::ulittle32_t>(0); |
676 | t.Priority = static_cast<llvm::support::ulittle32_t>(0); |
677 | t.EnvironmentBlock = static_cast<llvm::support::ulittle64_t>(0); |
678 | t.Stack = stack, t.Context = thread_context_memory_locator; |
679 | |
680 | // We save off the stack object so we can circle back and clean it up. |
681 | m_thread_by_range_end[sp_region.GetRange().GetRangeEnd()] = t; |
682 | m_data.AppendData(src: &t, src_len: sizeof(llvm::minidump::Thread)); |
683 | } |
684 | |
685 | LLDB_LOGF(log, "AddThreadList(): total helper_data %" PRIx64 " bytes" , |
686 | helper_data.GetByteSize()); |
687 | m_data.AppendData(src: helper_data.GetBytes(), src_len: helper_data.GetByteSize()); |
688 | return Status(); |
689 | } |
690 | |
691 | Status MinidumpFileBuilder::AddExceptions() { |
692 | std::vector<ThreadSP> thread_list = |
693 | m_process_sp->CalculateCoreFileThreadList(core_options: m_save_core_options); |
694 | Status error; |
695 | for (const ThreadSP &thread_sp : thread_list) { |
696 | StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); |
697 | // If we don't have a stop info, or if it's invalid, skip. |
698 | if (!stop_info_sp || |
699 | stop_info_sp->GetStopReason() == lldb::eStopReasonInvalid) |
700 | continue; |
701 | |
702 | constexpr size_t minidump_exception_size = |
703 | sizeof(llvm::minidump::ExceptionStream); |
704 | error = AddDirectory(type: StreamType::Exception, stream_size: minidump_exception_size); |
705 | if (error.Fail()) |
706 | return error; |
707 | |
708 | RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); |
709 | Exception exp_record = {}; |
710 | exp_record.ExceptionCode = |
711 | static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue()); |
712 | exp_record.ExceptionFlags = |
713 | static_cast<llvm::support::ulittle32_t>(Exception::LLDB_FLAG); |
714 | exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0); |
715 | exp_record.ExceptionAddress = reg_ctx_sp->GetPC(); |
716 | exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(1); |
717 | std::string description = stop_info_sp->GetDescription(); |
718 | // We have 120 bytes to work with and it's unlikely description will |
719 | // overflow, but we gotta check. |
720 | memcpy(dest: &exp_record.ExceptionInformation, src: description.c_str(), |
721 | n: std::min(a: description.size(), b: Exception::MaxParameterBytes)); |
722 | exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0); |
723 | ExceptionStream exp_stream; |
724 | exp_stream.ThreadId = |
725 | static_cast<llvm::support::ulittle32_t>(thread_sp->GetID()); |
726 | exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0); |
727 | exp_stream.ExceptionRecord = exp_record; |
728 | auto Iter = m_tid_to_reg_ctx.find(x: thread_sp->GetID()); |
729 | if (Iter != m_tid_to_reg_ctx.end()) { |
730 | exp_stream.ThreadContext = Iter->second; |
731 | } else { |
732 | exp_stream.ThreadContext.DataSize = 0; |
733 | exp_stream.ThreadContext.RVA = 0; |
734 | } |
735 | m_data.AppendData(src: &exp_stream, src_len: minidump_exception_size); |
736 | } |
737 | |
738 | return error; |
739 | } |
740 | |
741 | lldb_private::Status MinidumpFileBuilder::AddMiscInfo() { |
742 | Status error; |
743 | error = AddDirectory(type: StreamType::MiscInfo, |
744 | stream_size: sizeof(lldb_private::minidump::MinidumpMiscInfo)); |
745 | if (error.Fail()) |
746 | return error; |
747 | |
748 | lldb_private::minidump::MinidumpMiscInfo misc_info; |
749 | misc_info.size = static_cast<llvm::support::ulittle32_t>( |
750 | sizeof(lldb_private::minidump::MinidumpMiscInfo)); |
751 | // Default set flags1 to 0, in case that we will not be able to |
752 | // get any information |
753 | misc_info.flags1 = static_cast<llvm::support::ulittle32_t>(0); |
754 | |
755 | lldb_private::ProcessInstanceInfo process_info; |
756 | m_process_sp->GetProcessInfo(info&: process_info); |
757 | if (process_info.ProcessIDIsValid()) { |
758 | // Set flags1 to reflect that PID is filled in |
759 | misc_info.flags1 = |
760 | static_cast<llvm::support::ulittle32_t>(static_cast<uint32_t>( |
761 | lldb_private::minidump::MinidumpMiscInfoFlags::ProcessID)); |
762 | misc_info.process_id = |
763 | static_cast<llvm::support::ulittle32_t>(process_info.GetProcessID()); |
764 | } |
765 | |
766 | m_data.AppendData(src: &misc_info, |
767 | src_len: sizeof(lldb_private::minidump::MinidumpMiscInfo)); |
768 | return error; |
769 | } |
770 | |
771 | std::unique_ptr<llvm::MemoryBuffer> |
772 | getFileStreamHelper(const std::string &path) { |
773 | auto maybe_stream = llvm::MemoryBuffer::getFileAsStream(Filename: path); |
774 | if (!maybe_stream) |
775 | return nullptr; |
776 | return std::move(maybe_stream.get()); |
777 | } |
778 | |
779 | Status MinidumpFileBuilder::AddLinuxFileStreams() { |
780 | Status error; |
781 | // No-op if we are not on linux. |
782 | if (m_process_sp->GetTarget().GetArchitecture().GetTriple().getOS() != |
783 | llvm::Triple::Linux) |
784 | return error; |
785 | |
786 | std::vector<std::pair<StreamType, std::string>> files_with_stream_types = { |
787 | {StreamType::LinuxCPUInfo, "/proc/cpuinfo" }, |
788 | {StreamType::LinuxLSBRelease, "/etc/lsb-release" }, |
789 | }; |
790 | |
791 | lldb_private::ProcessInstanceInfo process_info; |
792 | m_process_sp->GetProcessInfo(info&: process_info); |
793 | if (process_info.ProcessIDIsValid()) { |
794 | lldb::pid_t pid = process_info.GetProcessID(); |
795 | std::string pid_str = std::to_string(val: pid); |
796 | files_with_stream_types.push_back( |
797 | x: {StreamType::LinuxProcStatus, "/proc/" + pid_str + "/status" }); |
798 | files_with_stream_types.push_back( |
799 | x: {StreamType::LinuxCMDLine, "/proc/" + pid_str + "/cmdline" }); |
800 | files_with_stream_types.push_back( |
801 | x: {StreamType::LinuxEnviron, "/proc/" + pid_str + "/environ" }); |
802 | files_with_stream_types.push_back( |
803 | x: {StreamType::LinuxAuxv, "/proc/" + pid_str + "/auxv" }); |
804 | files_with_stream_types.push_back( |
805 | x: {StreamType::LinuxMaps, "/proc/" + pid_str + "/maps" }); |
806 | files_with_stream_types.push_back( |
807 | x: {StreamType::LinuxProcStat, "/proc/" + pid_str + "/stat" }); |
808 | files_with_stream_types.push_back( |
809 | x: {StreamType::LinuxProcFD, "/proc/" + pid_str + "/fd" }); |
810 | } |
811 | |
812 | for (const auto &entry : files_with_stream_types) { |
813 | StreamType stream = entry.first; |
814 | std::string path = entry.second; |
815 | auto memory_buffer = getFileStreamHelper(path); |
816 | |
817 | if (memory_buffer) { |
818 | size_t size = memory_buffer->getBufferSize(); |
819 | if (size == 0) |
820 | continue; |
821 | error = AddDirectory(type: stream, stream_size: size); |
822 | if (error.Fail()) |
823 | return error; |
824 | m_data.AppendData(src: memory_buffer->getBufferStart(), src_len: size); |
825 | } |
826 | } |
827 | |
828 | return error; |
829 | } |
830 | |
831 | Status MinidumpFileBuilder::AddMemoryList() { |
832 | Status error; |
833 | |
834 | // We first save the thread stacks to ensure they fit in the first UINT32_MAX |
835 | // bytes of the core file. Thread structures in minidump files can only use |
836 | // 32 bit memory descriptiors, so we emit them first to ensure the memory is |
837 | // in accessible with a 32 bit offset. |
838 | std::vector<CoreFileMemoryRange> ranges_32; |
839 | std::vector<CoreFileMemoryRange> ranges_64; |
840 | CoreFileMemoryRanges all_core_memory_ranges; |
841 | error = m_process_sp->CalculateCoreFileSaveRanges(core_options: m_save_core_options, |
842 | ranges&: all_core_memory_ranges); |
843 | |
844 | if (error.Fail()) |
845 | return error; |
846 | |
847 | lldb_private::Progress progress("Saving Minidump File" , "" , |
848 | all_core_memory_ranges.GetSize()); |
849 | std::vector<CoreFileMemoryRange> all_core_memory_vec; |
850 | // Extract all the data into just a vector of data. So we can mutate this in |
851 | // place. |
852 | for (const auto &core_range : all_core_memory_ranges) |
853 | all_core_memory_vec.push_back(x: core_range.data); |
854 | |
855 | // Start by saving all of the stacks and ensuring they fit under the 32b |
856 | // limit. |
857 | uint64_t total_size = GetCurrentDataEndOffset(); |
858 | auto iterator = all_core_memory_vec.begin(); |
859 | while (iterator != all_core_memory_vec.end()) { |
860 | if (m_thread_by_range_end.count(x: iterator->range.end()) > 0) { |
861 | // We don't save stacks twice. |
862 | ranges_32.push_back(x: *iterator); |
863 | total_size += |
864 | iterator->range.size() + sizeof(llvm::minidump::MemoryDescriptor); |
865 | iterator = all_core_memory_vec.erase(position: iterator); |
866 | } else { |
867 | iterator++; |
868 | } |
869 | } |
870 | |
871 | if (total_size >= UINT32_MAX) { |
872 | error = Status::FromErrorStringWithFormat( |
873 | format: "Unable to write minidump. Stack memory " |
874 | "exceeds 32b limit. (Num Stacks %zu)" , |
875 | ranges_32.size()); |
876 | return error; |
877 | } |
878 | |
879 | // After saving the stacks, we start packing as much as we can into 32b. |
880 | // We apply a generous padding here so that the Directory, MemoryList and |
881 | // Memory64List sections all begin in 32b addressable space. |
882 | // Then anything overflow extends into 64b addressable space. |
883 | // all_core_memory_vec will either contain all stack regions at this point, |
884 | // or be empty if it's a stack only minidump. |
885 | if (!all_core_memory_vec.empty()) |
886 | total_size += 256 + (all_core_memory_vec.size() * |
887 | sizeof(llvm::minidump::MemoryDescriptor_64)); |
888 | |
889 | for (const auto &core_range : all_core_memory_vec) { |
890 | const addr_t range_size = core_range.range.size(); |
891 | // We don't need to check for stacks here because we already removed them |
892 | // from all_core_memory_ranges. |
893 | if (total_size + range_size < UINT32_MAX) { |
894 | ranges_32.push_back(x: core_range); |
895 | total_size += range_size; |
896 | } else { |
897 | ranges_64.push_back(x: core_range); |
898 | } |
899 | } |
900 | |
901 | error = AddMemoryList_32(ranges&: ranges_32, progress); |
902 | if (error.Fail()) |
903 | return error; |
904 | |
905 | // Add the remaining memory as a 64b range. |
906 | if (!ranges_64.empty()) { |
907 | error = AddMemoryList_64(ranges&: ranges_64, progress); |
908 | if (error.Fail()) |
909 | return error; |
910 | } |
911 | |
912 | return FixThreadStacks(); |
913 | } |
914 | |
915 | Status MinidumpFileBuilder::() const { |
916 | // write header |
917 | llvm::minidump::Header ; |
918 | header.Signature = static_cast<llvm::support::ulittle32_t>( |
919 | llvm::minidump::Header::MagicSignature); |
920 | header.Version = static_cast<llvm::support::ulittle32_t>( |
921 | llvm::minidump::Header::MagicVersion); |
922 | header.NumberOfStreams = |
923 | static_cast<llvm::support::ulittle32_t>(m_directories.size()); |
924 | // We write the directories right after the header. |
925 | header.StreamDirectoryRVA = |
926 | static_cast<llvm::support::ulittle32_t>(HEADER_SIZE); |
927 | header.Checksum = static_cast<llvm::support::ulittle32_t>( |
928 | 0u); // not used in most of the writers |
929 | header.TimeDateStamp = |
930 | static_cast<llvm::support::ulittle32_t>(std::time(timer: nullptr)); |
931 | header.Flags = |
932 | static_cast<llvm::support::ulittle64_t>(0u); // minidump normal flag |
933 | |
934 | Status error; |
935 | size_t bytes_written; |
936 | |
937 | m_core_file->SeekFromStart(offset: 0); |
938 | bytes_written = HEADER_SIZE; |
939 | error = m_core_file->Write(buf: &header, num_bytes&: bytes_written); |
940 | if (error.Fail() || bytes_written != HEADER_SIZE) { |
941 | if (bytes_written != HEADER_SIZE) |
942 | error = Status::FromErrorStringWithFormat( |
943 | format: "Unable to write the minidump header (written %zd/%zd)" , |
944 | bytes_written, HEADER_SIZE); |
945 | return error; |
946 | } |
947 | return error; |
948 | } |
949 | |
950 | offset_t MinidumpFileBuilder::GetCurrentDataEndOffset() const { |
951 | return m_data.GetByteSize() + m_saved_data_size; |
952 | } |
953 | |
954 | Status MinidumpFileBuilder::DumpDirectories() const { |
955 | Status error; |
956 | size_t bytes_written; |
957 | m_core_file->SeekFromStart(offset: HEADER_SIZE); |
958 | for (const Directory &dir : m_directories) { |
959 | bytes_written = DIRECTORY_SIZE; |
960 | error = m_core_file->Write(buf: &dir, num_bytes&: bytes_written); |
961 | if (error.Fail() || bytes_written != DIRECTORY_SIZE) { |
962 | if (bytes_written != DIRECTORY_SIZE) |
963 | error = Status::FromErrorStringWithFormat( |
964 | format: "unable to write the directory (written %zd/%zd)" , bytes_written, |
965 | DIRECTORY_SIZE); |
966 | return error; |
967 | } |
968 | } |
969 | |
970 | return error; |
971 | } |
972 | |
973 | Status MinidumpFileBuilder::ReadWriteMemoryInChunks( |
974 | lldb_private::DataBufferHeap &data_buffer, |
975 | const lldb_private::CoreFileMemoryRange &range, uint64_t &bytes_read) { |
976 | |
977 | const lldb::addr_t addr = range.range.start(); |
978 | const lldb::addr_t size = range.range.size(); |
979 | Log *log = GetLog(mask: LLDBLog::Object); |
980 | Status addDataError; |
981 | Process::ReadMemoryChunkCallback callback = |
982 | [&](Status &error, lldb::addr_t current_addr, const void *buf, |
983 | uint64_t bytes_read) -> lldb_private::IterationAction { |
984 | if (error.Fail() || bytes_read == 0) { |
985 | LLDB_LOGF(log, |
986 | "Failed to read memory region at: 0x%" PRIx64 |
987 | ". Bytes read: %" PRIx64 ", error: %s" , |
988 | current_addr, bytes_read, error.AsCString()); |
989 | |
990 | // If we failed in a memory read, we would normally want to skip |
991 | // this entire region. If we had already written to the minidump |
992 | // file, we can't easily rewind that state. |
993 | // |
994 | // So if we do encounter an error while reading, we return |
995 | // immediately, any prior bytes read will still be included but |
996 | // any bytes partially read before the error are ignored. |
997 | return lldb_private::IterationAction::Stop; |
998 | } |
999 | |
1000 | // Write to the minidump file with the chunk potentially flushing to |
1001 | // disk. |
1002 | // This error will be captured by the outer scope and is considered fatal. |
1003 | // If we get an error writing to disk we can't easily guarauntee that we |
1004 | // won't corrupt the minidump. |
1005 | addDataError = AddData(data: buf, size: bytes_read); |
1006 | if (addDataError.Fail()) |
1007 | return lldb_private::IterationAction::Stop; |
1008 | |
1009 | // If we have a partial read, report it, but only if the partial read |
1010 | // didn't finish reading the entire region. |
1011 | if (bytes_read != data_buffer.GetByteSize() && |
1012 | current_addr + bytes_read != size) { |
1013 | LLDB_LOGF(log, |
1014 | "Memory region at: %" PRIx64 " partiall read 0x%" PRIx64 |
1015 | " bytes out of %" PRIx64 " bytes." , |
1016 | current_addr, bytes_read, |
1017 | data_buffer.GetByteSize() - bytes_read); |
1018 | |
1019 | // If we've read some bytes, we stop trying to read more and return |
1020 | // this best effort attempt |
1021 | return lldb_private::IterationAction::Stop; |
1022 | } |
1023 | |
1024 | // No problems, keep going! |
1025 | return lldb_private::IterationAction::Continue; |
1026 | }; |
1027 | |
1028 | bytes_read = m_process_sp->ReadMemoryInChunks( |
1029 | vm_addr: addr, buf: data_buffer.GetBytes(), chunk_size: data_buffer.GetByteSize(), total_size: size, callback); |
1030 | return addDataError; |
1031 | } |
1032 | |
1033 | static uint64_t |
1034 | GetLargestRangeSize(const std::vector<CoreFileMemoryRange> &ranges) { |
1035 | uint64_t max_size = 0; |
1036 | for (const auto &core_range : ranges) |
1037 | max_size = std::max(a: max_size, b: core_range.range.size()); |
1038 | return max_size; |
1039 | } |
1040 | |
1041 | Status |
1042 | MinidumpFileBuilder::AddMemoryList_32(std::vector<CoreFileMemoryRange> &ranges, |
1043 | Progress &progress) { |
1044 | std::vector<MemoryDescriptor> descriptors; |
1045 | Status error; |
1046 | if (ranges.size() == 0) |
1047 | return error; |
1048 | |
1049 | Log *log = GetLog(mask: LLDBLog::Object); |
1050 | size_t region_index = 0; |
1051 | lldb_private::DataBufferHeap data_buffer( |
1052 | std::min(a: GetLargestRangeSize(ranges), b: MAX_WRITE_CHUNK_SIZE), 0); |
1053 | for (const auto &core_range : ranges) { |
1054 | // Take the offset before we write. |
1055 | const offset_t offset_for_data = GetCurrentDataEndOffset(); |
1056 | const addr_t addr = core_range.range.start(); |
1057 | const addr_t size = core_range.range.size(); |
1058 | const addr_t end = core_range.range.end(); |
1059 | |
1060 | LLDB_LOGF(log, |
1061 | "AddMemoryList %zu/%zu reading memory for region " |
1062 | "(%" PRIx64 " bytes) [%" PRIx64 ", %" PRIx64 ")" , |
1063 | region_index, ranges.size(), size, addr, addr + size); |
1064 | ++region_index; |
1065 | |
1066 | progress.Increment(amount: 1, updated_detail: "Adding Memory Range " + core_range.Dump()); |
1067 | uint64_t bytes_read = 0; |
1068 | error = ReadWriteMemoryInChunks(data_buffer, range: core_range, bytes_read); |
1069 | if (error.Fail()) |
1070 | return error; |
1071 | |
1072 | // If we completely failed to read this range |
1073 | // we can drop the memory range |
1074 | if (bytes_read == 0) |
1075 | continue; |
1076 | |
1077 | MemoryDescriptor descriptor; |
1078 | descriptor.StartOfMemoryRange = |
1079 | static_cast<llvm::support::ulittle64_t>(addr); |
1080 | descriptor.Memory.DataSize = |
1081 | static_cast<llvm::support::ulittle32_t>(bytes_read); |
1082 | descriptor.Memory.RVA = |
1083 | static_cast<llvm::support::ulittle32_t>(offset_for_data); |
1084 | descriptors.push_back(x: descriptor); |
1085 | if (m_thread_by_range_end.count(x: end) > 0) |
1086 | m_thread_by_range_end[end].Stack = descriptor; |
1087 | } |
1088 | |
1089 | // Add a directory that references this list |
1090 | // With a size of the number of ranges as a 32 bit num |
1091 | // And then the size of all the ranges |
1092 | error = AddDirectory(type: StreamType::MemoryList, |
1093 | stream_size: sizeof(llvm::minidump::MemoryListHeader) + |
1094 | descriptors.size() * |
1095 | sizeof(llvm::minidump::MemoryDescriptor)); |
1096 | if (error.Fail()) |
1097 | return error; |
1098 | |
1099 | llvm::minidump::MemoryListHeader ; |
1100 | llvm::support::ulittle32_t memory_ranges_num = |
1101 | static_cast<llvm::support::ulittle32_t>(descriptors.size()); |
1102 | list_header.NumberOfMemoryRanges = memory_ranges_num; |
1103 | m_data.AppendData(src: &list_header, src_len: sizeof(llvm::minidump::MemoryListHeader)); |
1104 | // For 32b we can get away with writing off the descriptors after the data. |
1105 | // This means no cleanup loop needed. |
1106 | m_data.AppendData(src: descriptors.data(), |
1107 | src_len: descriptors.size() * sizeof(MemoryDescriptor)); |
1108 | |
1109 | return error; |
1110 | } |
1111 | |
1112 | Status |
1113 | MinidumpFileBuilder::AddMemoryList_64(std::vector<CoreFileMemoryRange> &ranges, |
1114 | Progress &progress) { |
1115 | Status error; |
1116 | if (ranges.empty()) |
1117 | return error; |
1118 | |
1119 | error = AddDirectory(type: StreamType::Memory64List, |
1120 | stream_size: (sizeof(llvm::support::ulittle64_t) * 2) + |
1121 | ranges.size() * |
1122 | sizeof(llvm::minidump::MemoryDescriptor_64)); |
1123 | if (error.Fail()) |
1124 | return error; |
1125 | |
1126 | llvm::minidump::Memory64ListHeader ; |
1127 | llvm::support::ulittle64_t memory_ranges_num = |
1128 | static_cast<llvm::support::ulittle64_t>(ranges.size()); |
1129 | list_header.NumberOfMemoryRanges = memory_ranges_num; |
1130 | // Capture the starting offset for all the descriptors so we can clean them up |
1131 | // if needed. |
1132 | offset_t starting_offset = |
1133 | GetCurrentDataEndOffset() + sizeof(llvm::support::ulittle64_t); |
1134 | // The base_rva needs to start after the directories, which is right after |
1135 | // this 8 byte variable. |
1136 | offset_t base_rva = |
1137 | starting_offset + |
1138 | (ranges.size() * sizeof(llvm::minidump::MemoryDescriptor_64)); |
1139 | llvm::support::ulittle64_t memory_ranges_base_rva = |
1140 | static_cast<llvm::support::ulittle64_t>(base_rva); |
1141 | list_header.BaseRVA = memory_ranges_base_rva; |
1142 | m_data.AppendData(src: &list_header, src_len: sizeof(llvm::minidump::Memory64ListHeader)); |
1143 | |
1144 | lldb_private::DataBufferHeap data_buffer( |
1145 | std::min(a: GetLargestRangeSize(ranges), b: MAX_WRITE_CHUNK_SIZE), 0); |
1146 | bool cleanup_required = false; |
1147 | std::vector<MemoryDescriptor_64> descriptors; |
1148 | // Enumerate the ranges and create the memory descriptors so we can append |
1149 | // them first |
1150 | for (const auto core_range : ranges) { |
1151 | // Add the space required to store the memory descriptor |
1152 | MemoryDescriptor_64 memory_desc; |
1153 | memory_desc.StartOfMemoryRange = |
1154 | static_cast<llvm::support::ulittle64_t>(core_range.range.start()); |
1155 | memory_desc.DataSize = |
1156 | static_cast<llvm::support::ulittle64_t>(core_range.range.size()); |
1157 | descriptors.push_back(x: memory_desc); |
1158 | // Now write this memory descriptor to the buffer. |
1159 | m_data.AppendData(src: &memory_desc, src_len: sizeof(MemoryDescriptor_64)); |
1160 | } |
1161 | |
1162 | Log *log = GetLog(mask: LLDBLog::Object); |
1163 | size_t region_index = 0; |
1164 | for (const auto &core_range : ranges) { |
1165 | const addr_t addr = core_range.range.start(); |
1166 | const addr_t size = core_range.range.size(); |
1167 | |
1168 | LLDB_LOGF(log, |
1169 | "AddMemoryList_64 %zu/%zu reading memory for region " |
1170 | "(%" PRIx64 "bytes) " |
1171 | "[%" PRIx64 ", %" PRIx64 ")" , |
1172 | region_index, ranges.size(), size, addr, addr + size); |
1173 | |
1174 | progress.Increment(amount: 1, updated_detail: "Adding Memory Range " + core_range.Dump()); |
1175 | uint64_t bytes_read = 0; |
1176 | error = ReadWriteMemoryInChunks(data_buffer, range: core_range, bytes_read); |
1177 | if (error.Fail()) |
1178 | return error; |
1179 | |
1180 | if (bytes_read == 0) { |
1181 | cleanup_required = true; |
1182 | descriptors[region_index].DataSize = 0; |
1183 | } |
1184 | if (bytes_read != size) { |
1185 | cleanup_required = true; |
1186 | descriptors[region_index].DataSize = bytes_read; |
1187 | } |
1188 | |
1189 | ++region_index; |
1190 | } |
1191 | |
1192 | // Early return if there is no cleanup needed. |
1193 | if (!cleanup_required) { |
1194 | return error; |
1195 | } else { |
1196 | // Flush to disk we can make the fixes in place. |
1197 | FlushBufferToDisk(); |
1198 | // Fixup the descriptors that were not read correctly. |
1199 | m_core_file->SeekFromStart(offset: starting_offset); |
1200 | size_t bytes_written = sizeof(MemoryDescriptor_64) * descriptors.size(); |
1201 | error = m_core_file->Write(buf: descriptors.data(), num_bytes&: bytes_written); |
1202 | if (error.Fail() || |
1203 | bytes_written != sizeof(MemoryDescriptor_64) * descriptors.size()) { |
1204 | error = Status::FromErrorStringWithFormat( |
1205 | format: "unable to write the memory descriptors (written %zd/%zd)" , |
1206 | bytes_written, sizeof(MemoryDescriptor_64) * descriptors.size()); |
1207 | } |
1208 | |
1209 | return error; |
1210 | } |
1211 | } |
1212 | |
1213 | Status MinidumpFileBuilder::AddData(const void *data, uint64_t size) { |
1214 | // Append the data to the buffer, if the buffer spills over, flush it to disk |
1215 | m_data.AppendData(src: data, src_len: size); |
1216 | if (m_data.GetByteSize() > MAX_WRITE_CHUNK_SIZE) |
1217 | return FlushBufferToDisk(); |
1218 | |
1219 | return Status(); |
1220 | } |
1221 | |
1222 | Status MinidumpFileBuilder::FlushBufferToDisk() { |
1223 | Status error; |
1224 | // Set the stream to it's end. |
1225 | m_core_file->SeekFromStart(offset: m_saved_data_size); |
1226 | addr_t starting_size = m_data.GetByteSize(); |
1227 | addr_t remaining_bytes = starting_size; |
1228 | offset_t offset = 0; |
1229 | |
1230 | while (remaining_bytes > 0) { |
1231 | size_t bytes_written = remaining_bytes; |
1232 | // We don't care how many bytes we wrote unless we got an error |
1233 | // so just decrement the remaining bytes. |
1234 | error = m_core_file->Write(buf: m_data.GetBytes() + offset, num_bytes&: bytes_written); |
1235 | if (error.Fail()) { |
1236 | error = Status::FromErrorStringWithFormat( |
1237 | format: "Wrote incorrect number of bytes to minidump file. (written %" PRIx64 |
1238 | "/%" PRIx64 ")" , |
1239 | starting_size - remaining_bytes, starting_size); |
1240 | return error; |
1241 | } |
1242 | |
1243 | offset += bytes_written; |
1244 | remaining_bytes -= bytes_written; |
1245 | } |
1246 | |
1247 | m_saved_data_size += starting_size; |
1248 | m_data.Clear(); |
1249 | return error; |
1250 | } |
1251 | |
1252 | Status MinidumpFileBuilder::DumpFile() { |
1253 | Status error; |
1254 | // If anything is left unsaved, dump it. |
1255 | error = FlushBufferToDisk(); |
1256 | if (error.Fail()) |
1257 | return error; |
1258 | |
1259 | // Overwrite the header which we filled in earlier. |
1260 | error = DumpHeader(); |
1261 | if (error.Fail()) |
1262 | return error; |
1263 | |
1264 | // Overwrite the space saved for directories |
1265 | error = DumpDirectories(); |
1266 | if (error.Fail()) |
1267 | return error; |
1268 | |
1269 | return error; |
1270 | } |
1271 | |
1272 | void MinidumpFileBuilder::DeleteFile() noexcept { |
1273 | Log *log = GetLog(mask: LLDBLog::Object); |
1274 | |
1275 | if (m_core_file) { |
1276 | Status error = m_core_file->Close(); |
1277 | if (error.Fail()) |
1278 | LLDB_LOGF(log, "Failed to close minidump file: %s" , error.AsCString()); |
1279 | |
1280 | m_core_file.reset(); |
1281 | } |
1282 | } |
1283 | |