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