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
48using namespace lldb;
49using namespace lldb_private;
50using namespace llvm::minidump;
51
52Status 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
100Status 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
133Status MinidumpFileBuilder::AddLLDBGeneratedStream() {
134 Status error;
135 StreamType type = StreamType::LLDBGenerated;
136 return AddDirectory(type, stream_size: 0);
137}
138
139Status 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
225Status 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
250llvm::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 &section : *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.
297Status 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
406uint16_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
418uint32_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
430uint64_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
442llvm::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
448llvm::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
454llvm::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
460void 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
477lldb_private::minidump::MinidumpContext_x86_64
478GetThreadContext_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
517minidump::RegisterContextMinidump_ARM64::Context
518GetThreadContext_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
543class 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
550public:
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
582Status 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
605Status 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
691Status 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
741lldb_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
771std::unique_ptr<llvm::MemoryBuffer>
772getFileStreamHelper(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
779Status 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
831Status 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
915Status MinidumpFileBuilder::DumpHeader() const {
916 // write header
917 llvm::minidump::Header 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
950offset_t MinidumpFileBuilder::GetCurrentDataEndOffset() const {
951 return m_data.GetByteSize() + m_saved_data_size;
952}
953
954Status 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
973Status 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
1033static uint64_t
1034GetLargestRangeSize(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
1041Status
1042MinidumpFileBuilder::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 list_header;
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
1112Status
1113MinidumpFileBuilder::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 list_header;
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
1213Status 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
1222Status 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
1252Status 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
1272void 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

source code of lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp