aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp')
-rw-r--r--contrib/llvm-project/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp1207
1 files changed, 1207 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp
new file mode 100644
index 000000000000..de212c6b20da
--- /dev/null
+++ b/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp
@@ -0,0 +1,1207 @@
+//===-- MinidumpFileBuilder.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MinidumpFileBuilder.h"
+
+#include "Plugins/Process/minidump/RegisterContextMinidump_ARM64.h"
+#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/DataExtractor.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/RangeMap.h"
+#include "lldb/Utility/RegisterValue.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/BinaryFormat/Minidump.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Error.h"
+#include "llvm/TargetParser/Triple.h"
+
+#include "Plugins/Process/minidump/MinidumpTypes.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
+#include "lldb/lldb-types.h"
+
+#include <algorithm>
+#include <cinttypes>
+#include <climits>
+#include <cstddef>
+#include <cstdint>
+#include <functional>
+#include <iostream>
+#include <set>
+#include <utility>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace llvm::minidump;
+
+Status MinidumpFileBuilder::AddHeaderAndCalculateDirectories() {
+ // First set the offset on the file, and on the bytes saved
+ m_saved_data_size = HEADER_SIZE;
+ // We know we will have at least Misc, SystemInfo, Modules, and ThreadList
+ // (corresponding memory list for stacks) And an additional memory list for
+ // non-stacks.
+ lldb_private::Target &target = m_process_sp->GetTarget();
+ m_expected_directories = 6;
+ // Check if OS is linux and reserve directory space for all linux specific
+ // breakpad extension directories.
+ if (target.GetArchitecture().GetTriple().getOS() ==
+ llvm::Triple::OSType::Linux)
+ m_expected_directories += 9;
+
+ // Go through all of the threads and check for exceptions.
+ lldb_private::ThreadList thread_list = m_process_sp->GetThreadList();
+ const uint32_t num_threads = thread_list.GetSize();
+ for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
+ ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
+ StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
+ if (stop_info_sp) {
+ const StopReason &stop_reason = stop_info_sp->GetStopReason();
+ if (stop_reason == StopReason::eStopReasonException ||
+ stop_reason == StopReason::eStopReasonSignal)
+ m_expected_directories++;
+ }
+ }
+
+ m_saved_data_size +=
+ m_expected_directories * sizeof(llvm::minidump::Directory);
+ Status error;
+ offset_t new_offset = m_core_file->SeekFromStart(m_saved_data_size);
+ if (new_offset != m_saved_data_size)
+ error.SetErrorStringWithFormat("Failed to fill in header and directory "
+ "sections. Written / Expected (%" PRIx64
+ " / %" PRIx64 ")",
+ new_offset, m_saved_data_size);
+
+ return error;
+}
+
+Status MinidumpFileBuilder::AddDirectory(StreamType type,
+ uint64_t stream_size) {
+ // We explicitly cast type, an 32b enum, to uint32_t to avoid warnings.
+ Status error;
+ if (GetCurrentDataEndOffset() > UINT32_MAX) {
+ error.SetErrorStringWithFormat("Unable to add directory for stream type "
+ "%x, offset is greater then 32 bit limit.",
+ (uint32_t)type);
+ return error;
+ }
+
+ if (m_directories.size() + 1 > m_expected_directories) {
+ error.SetErrorStringWithFormat(
+ "Unable to add directory for stream type %x, exceeded expected number "
+ "of directories %zu.",
+ (uint32_t)type, m_expected_directories);
+ return error;
+ }
+
+ LocationDescriptor loc;
+ loc.DataSize = static_cast<llvm::support::ulittle32_t>(stream_size);
+ // Stream will begin at the current end of data section
+ loc.RVA = static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset());
+
+ Directory dir;
+ dir.Type = static_cast<llvm::support::little_t<StreamType>>(type);
+ dir.Location = loc;
+
+ m_directories.push_back(dir);
+ return error;
+}
+
+Status MinidumpFileBuilder::AddSystemInfo() {
+ Status error;
+ const llvm::Triple &target_triple =
+ m_process_sp->GetTarget().GetArchitecture().GetTriple();
+ error =
+ AddDirectory(StreamType::SystemInfo, sizeof(llvm::minidump::SystemInfo));
+ if (error.Fail())
+ return error;
+
+ llvm::minidump::ProcessorArchitecture arch;
+ switch (target_triple.getArch()) {
+ case llvm::Triple::ArchType::x86_64:
+ arch = ProcessorArchitecture::AMD64;
+ break;
+ case llvm::Triple::ArchType::x86:
+ arch = ProcessorArchitecture::X86;
+ break;
+ case llvm::Triple::ArchType::arm:
+ arch = ProcessorArchitecture::ARM;
+ break;
+ case llvm::Triple::ArchType::aarch64:
+ arch = ProcessorArchitecture::ARM64;
+ break;
+ case llvm::Triple::ArchType::mips64:
+ case llvm::Triple::ArchType::mips64el:
+ case llvm::Triple::ArchType::mips:
+ case llvm::Triple::ArchType::mipsel:
+ arch = ProcessorArchitecture::MIPS;
+ break;
+ case llvm::Triple::ArchType::ppc64:
+ case llvm::Triple::ArchType::ppc:
+ case llvm::Triple::ArchType::ppc64le:
+ arch = ProcessorArchitecture::PPC;
+ break;
+ default:
+ error.SetErrorStringWithFormat("Architecture %s not supported.",
+ target_triple.getArchName().str().c_str());
+ return error;
+ };
+
+ llvm::support::little_t<OSPlatform> platform_id;
+ switch (target_triple.getOS()) {
+ case llvm::Triple::OSType::Linux:
+ if (target_triple.getEnvironment() ==
+ llvm::Triple::EnvironmentType::Android)
+ platform_id = OSPlatform::Android;
+ else
+ platform_id = OSPlatform::Linux;
+ break;
+ case llvm::Triple::OSType::Win32:
+ platform_id = OSPlatform::Win32NT;
+ break;
+ case llvm::Triple::OSType::MacOSX:
+ platform_id = OSPlatform::MacOSX;
+ break;
+ case llvm::Triple::OSType::IOS:
+ platform_id = OSPlatform::IOS;
+ break;
+ default:
+ error.SetErrorStringWithFormat("OS %s not supported.",
+ target_triple.getOSName().str().c_str());
+ return error;
+ };
+
+ llvm::minidump::SystemInfo sys_info;
+ sys_info.ProcessorArch =
+ static_cast<llvm::support::little_t<ProcessorArchitecture>>(arch);
+ // Global offset to beginning of a csd_string in a data section
+ sys_info.CSDVersionRVA = static_cast<llvm::support::ulittle32_t>(
+ GetCurrentDataEndOffset() + sizeof(llvm::minidump::SystemInfo));
+ sys_info.PlatformId = platform_id;
+ m_data.AppendData(&sys_info, sizeof(llvm::minidump::SystemInfo));
+
+ std::string csd_string;
+
+ error = WriteString(csd_string, &m_data);
+ if (error.Fail()) {
+ error.SetErrorString("Unable to convert the csd string to UTF16.");
+ return error;
+ }
+
+ return error;
+}
+
+Status WriteString(const std::string &to_write,
+ lldb_private::DataBufferHeap *buffer) {
+ Status error;
+ // let the StringRef eat also null termination char
+ llvm::StringRef to_write_ref(to_write.c_str(), to_write.size() + 1);
+ llvm::SmallVector<llvm::UTF16, 128> to_write_utf16;
+
+ bool converted = convertUTF8ToUTF16String(to_write_ref, to_write_utf16);
+ if (!converted) {
+ error.SetErrorStringWithFormat(
+ "Unable to convert the string to UTF16. Failed to convert %s",
+ to_write.c_str());
+ return error;
+ }
+
+ // size of the UTF16 string should be written without the null termination
+ // character that is stored in 2 bytes
+ llvm::support::ulittle32_t to_write_size(to_write_utf16.size_in_bytes() - 2);
+
+ buffer->AppendData(&to_write_size, sizeof(llvm::support::ulittle32_t));
+ buffer->AppendData(to_write_utf16.data(), to_write_utf16.size_in_bytes());
+
+ return error;
+}
+
+llvm::Expected<uint64_t> getModuleFileSize(Target &target,
+ const ModuleSP &mod) {
+ // JIT module has the same vm and file size.
+ uint64_t SizeOfImage = 0;
+ if (mod->GetObjectFile()->CalculateType() == ObjectFile::Type::eTypeJIT) {
+ for (const auto &section : *mod->GetObjectFile()->GetSectionList()) {
+ SizeOfImage += section->GetByteSize();
+ }
+ return SizeOfImage;
+ }
+ SectionSP sect_sp = mod->GetObjectFile()->GetBaseAddress().GetSection();
+
+ if (!sect_sp) {
+ return llvm::createStringError(std::errc::operation_not_supported,
+ "Couldn't obtain the section information.");
+ }
+ lldb::addr_t sect_addr = sect_sp->GetLoadBaseAddress(&target);
+ // Use memory size since zero fill sections, like ".bss", will be smaller on
+ // disk.
+ lldb::addr_t sect_size = sect_sp->GetByteSize();
+ // This will usually be zero, but make sure to calculate the BaseOfImage
+ // offset.
+ const lldb::addr_t base_sect_offset =
+ mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target) -
+ sect_addr;
+ SizeOfImage = sect_size - base_sect_offset;
+ lldb::addr_t next_sect_addr = sect_addr + sect_size;
+ Address sect_so_addr;
+ target.ResolveLoadAddress(next_sect_addr, sect_so_addr);
+ lldb::SectionSP next_sect_sp = sect_so_addr.GetSection();
+ while (next_sect_sp &&
+ next_sect_sp->GetLoadBaseAddress(&target) == next_sect_addr) {
+ sect_size = sect_sp->GetByteSize();
+ SizeOfImage += sect_size;
+ next_sect_addr += sect_size;
+ target.ResolveLoadAddress(next_sect_addr, sect_so_addr);
+ next_sect_sp = sect_so_addr.GetSection();
+ }
+
+ return SizeOfImage;
+}
+
+// ModuleList stream consists of a number of modules, followed by an array
+// of llvm::minidump::Module's structures. Every structure informs about a
+// single module. Additional data of variable length, such as module's names,
+// are stored just after the ModuleList stream. The llvm::minidump::Module
+// structures point to this helper data by global offset.
+Status MinidumpFileBuilder::AddModuleList() {
+ constexpr size_t minidump_module_size = sizeof(llvm::minidump::Module);
+ Status error;
+
+ lldb_private::Target &target = m_process_sp->GetTarget();
+ const ModuleList &modules = target.GetImages();
+ llvm::support::ulittle32_t modules_count =
+ static_cast<llvm::support::ulittle32_t>(modules.GetSize());
+
+ // This helps us with getting the correct global offset in minidump
+ // file later, when we will be setting up offsets from the
+ // the llvm::minidump::Module's structures into helper data
+ size_t size_before = GetCurrentDataEndOffset();
+
+ // This is the size of the main part of the ModuleList stream.
+ // It consists of a module number and corresponding number of
+ // structs describing individual modules
+ size_t module_stream_size =
+ sizeof(llvm::support::ulittle32_t) + modules_count * minidump_module_size;
+
+ // Adding directory describing this stream.
+ error = AddDirectory(StreamType::ModuleList, module_stream_size);
+ if (error.Fail())
+ return error;
+
+ m_data.AppendData(&modules_count, sizeof(llvm::support::ulittle32_t));
+
+ // Temporary storage for the helper data (of variable length)
+ // as these cannot be dumped to m_data before dumping entire
+ // array of module structures.
+ DataBufferHeap helper_data;
+
+ for (size_t i = 0; i < modules_count; ++i) {
+ ModuleSP mod = modules.GetModuleAtIndex(i);
+ std::string module_name = mod->GetSpecificationDescription();
+ auto maybe_mod_size = getModuleFileSize(target, mod);
+ if (!maybe_mod_size) {
+ llvm::Error mod_size_err = maybe_mod_size.takeError();
+ llvm::handleAllErrors(std::move(mod_size_err),
+ [&](const llvm::ErrorInfoBase &E) {
+ error.SetErrorStringWithFormat(
+ "Unable to get the size of module %s: %s.",
+ module_name.c_str(), E.message().c_str());
+ });
+ return error;
+ }
+
+ uint64_t mod_size = std::move(*maybe_mod_size);
+
+ llvm::support::ulittle32_t signature =
+ static_cast<llvm::support::ulittle32_t>(
+ static_cast<uint32_t>(minidump::CvSignature::ElfBuildId));
+ auto uuid = mod->GetUUID().GetBytes();
+
+ VSFixedFileInfo info;
+ info.Signature = static_cast<llvm::support::ulittle32_t>(0u);
+ info.StructVersion = static_cast<llvm::support::ulittle32_t>(0u);
+ info.FileVersionHigh = static_cast<llvm::support::ulittle32_t>(0u);
+ info.FileVersionLow = static_cast<llvm::support::ulittle32_t>(0u);
+ info.ProductVersionHigh = static_cast<llvm::support::ulittle32_t>(0u);
+ info.ProductVersionLow = static_cast<llvm::support::ulittle32_t>(0u);
+ info.FileFlagsMask = static_cast<llvm::support::ulittle32_t>(0u);
+ info.FileFlags = static_cast<llvm::support::ulittle32_t>(0u);
+ info.FileOS = static_cast<llvm::support::ulittle32_t>(0u);
+ info.FileType = static_cast<llvm::support::ulittle32_t>(0u);
+ info.FileSubtype = static_cast<llvm::support::ulittle32_t>(0u);
+ info.FileDateHigh = static_cast<llvm::support::ulittle32_t>(0u);
+ info.FileDateLow = static_cast<llvm::support::ulittle32_t>(0u);
+
+ LocationDescriptor ld;
+ ld.DataSize = static_cast<llvm::support::ulittle32_t>(0u);
+ ld.RVA = static_cast<llvm::support::ulittle32_t>(0u);
+
+ // Setting up LocationDescriptor for uuid string. The global offset into
+ // minidump file is calculated.
+ LocationDescriptor ld_cv;
+ ld_cv.DataSize = static_cast<llvm::support::ulittle32_t>(
+ sizeof(llvm::support::ulittle32_t) + uuid.size());
+ ld_cv.RVA = static_cast<llvm::support::ulittle32_t>(
+ size_before + module_stream_size + helper_data.GetByteSize());
+
+ helper_data.AppendData(&signature, sizeof(llvm::support::ulittle32_t));
+ helper_data.AppendData(uuid.begin(), uuid.size());
+
+ llvm::minidump::Module m;
+ m.BaseOfImage = static_cast<llvm::support::ulittle64_t>(
+ mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target));
+ m.SizeOfImage = static_cast<llvm::support::ulittle32_t>(mod_size);
+ m.Checksum = static_cast<llvm::support::ulittle32_t>(0);
+ m.TimeDateStamp =
+ static_cast<llvm::support::ulittle32_t>(std::time(nullptr));
+ m.ModuleNameRVA = static_cast<llvm::support::ulittle32_t>(
+ size_before + module_stream_size + helper_data.GetByteSize());
+ m.VersionInfo = info;
+ m.CvRecord = ld_cv;
+ m.MiscRecord = ld;
+
+ error = WriteString(module_name, &helper_data);
+
+ if (error.Fail())
+ return error;
+
+ m_data.AppendData(&m, sizeof(llvm::minidump::Module));
+ }
+
+ m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
+ return error;
+}
+
+uint16_t read_register_u16_raw(RegisterContext *reg_ctx,
+ llvm::StringRef reg_name) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
+ if (!reg_info)
+ return 0;
+ lldb_private::RegisterValue reg_value;
+ bool success = reg_ctx->ReadRegister(reg_info, reg_value);
+ if (!success)
+ return 0;
+ return reg_value.GetAsUInt16();
+}
+
+uint32_t read_register_u32_raw(RegisterContext *reg_ctx,
+ llvm::StringRef reg_name) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
+ if (!reg_info)
+ return 0;
+ lldb_private::RegisterValue reg_value;
+ bool success = reg_ctx->ReadRegister(reg_info, reg_value);
+ if (!success)
+ return 0;
+ return reg_value.GetAsUInt32();
+}
+
+uint64_t read_register_u64_raw(RegisterContext *reg_ctx,
+ llvm::StringRef reg_name) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
+ if (!reg_info)
+ return 0;
+ lldb_private::RegisterValue reg_value;
+ bool success = reg_ctx->ReadRegister(reg_info, reg_value);
+ if (!success)
+ return 0;
+ return reg_value.GetAsUInt64();
+}
+
+llvm::support::ulittle16_t read_register_u16(RegisterContext *reg_ctx,
+ llvm::StringRef reg_name) {
+ return static_cast<llvm::support::ulittle16_t>(
+ read_register_u16_raw(reg_ctx, reg_name));
+}
+
+llvm::support::ulittle32_t read_register_u32(RegisterContext *reg_ctx,
+ llvm::StringRef reg_name) {
+ return static_cast<llvm::support::ulittle32_t>(
+ read_register_u32_raw(reg_ctx, reg_name));
+}
+
+llvm::support::ulittle64_t read_register_u64(RegisterContext *reg_ctx,
+ llvm::StringRef reg_name) {
+ return static_cast<llvm::support::ulittle64_t>(
+ read_register_u64_raw(reg_ctx, reg_name));
+}
+
+void read_register_u128(RegisterContext *reg_ctx, llvm::StringRef reg_name,
+ uint8_t *dst) {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);
+ if (reg_info) {
+ lldb_private::RegisterValue reg_value;
+ if (reg_ctx->ReadRegister(reg_info, reg_value)) {
+ Status error;
+ uint32_t bytes_copied = reg_value.GetAsMemoryData(
+ *reg_info, dst, 16, lldb::ByteOrder::eByteOrderLittle, error);
+ if (bytes_copied == 16)
+ return;
+ }
+ }
+ // If anything goes wrong, then zero out the register value.
+ memset(dst, 0, 16);
+}
+
+lldb_private::minidump::MinidumpContext_x86_64
+GetThreadContext_x86_64(RegisterContext *reg_ctx) {
+ lldb_private::minidump::MinidumpContext_x86_64 thread_context = {};
+ thread_context.p1_home = {};
+ thread_context.context_flags = static_cast<uint32_t>(
+ lldb_private::minidump::MinidumpContext_x86_64_Flags::x86_64_Flag |
+ lldb_private::minidump::MinidumpContext_x86_64_Flags::Control |
+ lldb_private::minidump::MinidumpContext_x86_64_Flags::Segments |
+ lldb_private::minidump::MinidumpContext_x86_64_Flags::Integer);
+ thread_context.rax = read_register_u64(reg_ctx, "rax");
+ thread_context.rbx = read_register_u64(reg_ctx, "rbx");
+ thread_context.rcx = read_register_u64(reg_ctx, "rcx");
+ thread_context.rdx = read_register_u64(reg_ctx, "rdx");
+ thread_context.rdi = read_register_u64(reg_ctx, "rdi");
+ thread_context.rsi = read_register_u64(reg_ctx, "rsi");
+ thread_context.rbp = read_register_u64(reg_ctx, "rbp");
+ thread_context.rsp = read_register_u64(reg_ctx, "rsp");
+ thread_context.r8 = read_register_u64(reg_ctx, "r8");
+ thread_context.r9 = read_register_u64(reg_ctx, "r9");
+ thread_context.r10 = read_register_u64(reg_ctx, "r10");
+ thread_context.r11 = read_register_u64(reg_ctx, "r11");
+ thread_context.r12 = read_register_u64(reg_ctx, "r12");
+ thread_context.r13 = read_register_u64(reg_ctx, "r13");
+ thread_context.r14 = read_register_u64(reg_ctx, "r14");
+ thread_context.r15 = read_register_u64(reg_ctx, "r15");
+ thread_context.rip = read_register_u64(reg_ctx, "rip");
+ thread_context.eflags = read_register_u32(reg_ctx, "rflags");
+ thread_context.cs = read_register_u16(reg_ctx, "cs");
+ thread_context.fs = read_register_u16(reg_ctx, "fs");
+ thread_context.gs = read_register_u16(reg_ctx, "gs");
+ thread_context.ss = read_register_u16(reg_ctx, "ss");
+ thread_context.ds = read_register_u16(reg_ctx, "ds");
+ return thread_context;
+}
+
+minidump::RegisterContextMinidump_ARM64::Context
+GetThreadContext_ARM64(RegisterContext *reg_ctx) {
+ minidump::RegisterContextMinidump_ARM64::Context thread_context = {};
+ thread_context.context_flags = static_cast<uint32_t>(
+ minidump::RegisterContextMinidump_ARM64::Flags::ARM64_Flag |
+ minidump::RegisterContextMinidump_ARM64::Flags::Integer |
+ minidump::RegisterContextMinidump_ARM64::Flags::FloatingPoint);
+ char reg_name[16];
+ for (uint32_t i = 0; i < 31; ++i) {
+ snprintf(reg_name, sizeof(reg_name), "x%u", i);
+ thread_context.x[i] = read_register_u64(reg_ctx, reg_name);
+ }
+ // Work around a bug in debugserver where "sp" on arm64 doesn't have the alt
+ // name set to "x31"
+ thread_context.x[31] = read_register_u64(reg_ctx, "sp");
+ thread_context.pc = read_register_u64(reg_ctx, "pc");
+ thread_context.cpsr = read_register_u32(reg_ctx, "cpsr");
+ thread_context.fpsr = read_register_u32(reg_ctx, "fpsr");
+ thread_context.fpcr = read_register_u32(reg_ctx, "fpcr");
+ for (uint32_t i = 0; i < 32; ++i) {
+ snprintf(reg_name, sizeof(reg_name), "v%u", i);
+ read_register_u128(reg_ctx, reg_name, &thread_context.v[i * 16]);
+ }
+ return thread_context;
+}
+
+class ArchThreadContexts {
+ llvm::Triple::ArchType m_arch;
+ union {
+ lldb_private::minidump::MinidumpContext_x86_64 x86_64;
+ lldb_private::minidump::RegisterContextMinidump_ARM64::Context arm64;
+ };
+
+public:
+ ArchThreadContexts(llvm::Triple::ArchType arch) : m_arch(arch) {}
+
+ bool prepareRegisterContext(RegisterContext *reg_ctx) {
+ switch (m_arch) {
+ case llvm::Triple::ArchType::x86_64:
+ x86_64 = GetThreadContext_x86_64(reg_ctx);
+ return true;
+ case llvm::Triple::ArchType::aarch64:
+ arm64 = GetThreadContext_ARM64(reg_ctx);
+ return true;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ const void *data() const { return &x86_64; }
+
+ size_t size() const {
+ switch (m_arch) {
+ case llvm::Triple::ArchType::x86_64:
+ return sizeof(x86_64);
+ case llvm::Triple::ArchType::aarch64:
+ return sizeof(arm64);
+ default:
+ break;
+ }
+ return 0;
+ }
+};
+
+Status MinidumpFileBuilder::FixThreadStacks() {
+ Status error;
+ // If we have anything in the heap flush it.
+ FlushBufferToDisk();
+ m_core_file->SeekFromStart(m_thread_list_start);
+ for (auto &pair : m_thread_by_range_end) {
+ // The thread objects will get a new memory descriptor added
+ // When we are emitting the memory list and then we write it here
+ const llvm::minidump::Thread &thread = pair.second;
+ size_t bytes_to_write = sizeof(llvm::minidump::Thread);
+ size_t bytes_written = bytes_to_write;
+ error = m_core_file->Write(&thread, bytes_written);
+ if (error.Fail() || bytes_to_write != bytes_written) {
+ error.SetErrorStringWithFormat(
+ "Wrote incorrect number of bytes to minidump file. (written %zd/%zd)",
+ bytes_written, bytes_to_write);
+ return error;
+ }
+ }
+
+ return error;
+}
+
+Status MinidumpFileBuilder::AddThreadList() {
+ constexpr size_t minidump_thread_size = sizeof(llvm::minidump::Thread);
+ lldb_private::ThreadList thread_list = m_process_sp->GetThreadList();
+
+ // size of the entire thread stream consists of:
+ // number of threads and threads array
+ size_t thread_stream_size = sizeof(llvm::support::ulittle32_t) +
+ thread_list.GetSize() * minidump_thread_size;
+ // save for the ability to set up RVA
+ size_t size_before = GetCurrentDataEndOffset();
+ Status error;
+ error = AddDirectory(StreamType::ThreadList, thread_stream_size);
+ if (error.Fail())
+ return error;
+
+ llvm::support::ulittle32_t thread_count =
+ static_cast<llvm::support::ulittle32_t>(thread_list.GetSize());
+ m_data.AppendData(&thread_count, sizeof(llvm::support::ulittle32_t));
+
+ // Take the offset after the thread count.
+ m_thread_list_start = GetCurrentDataEndOffset();
+ DataBufferHeap helper_data;
+
+ const uint32_t num_threads = thread_list.GetSize();
+ Log *log = GetLog(LLDBLog::Object);
+ for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
+ ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
+ RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
+
+ if (!reg_ctx_sp) {
+ error.SetErrorString("Unable to get the register context.");
+ return error;
+ }
+ RegisterContext *reg_ctx = reg_ctx_sp.get();
+ Target &target = m_process_sp->GetTarget();
+ const ArchSpec &arch = target.GetArchitecture();
+ ArchThreadContexts thread_context(arch.GetMachine());
+ if (!thread_context.prepareRegisterContext(reg_ctx)) {
+ error.SetErrorStringWithFormat(
+ "architecture %s not supported.",
+ arch.GetTriple().getArchName().str().c_str());
+ return error;
+ }
+
+ uint64_t sp = reg_ctx->GetSP();
+ MemoryRegionInfo sp_region;
+ m_process_sp->GetMemoryRegionInfo(sp, sp_region);
+
+ // Emit a blank descriptor
+ MemoryDescriptor stack;
+ LocationDescriptor empty_label;
+ empty_label.DataSize = 0;
+ empty_label.RVA = 0;
+ stack.Memory = empty_label;
+ stack.StartOfMemoryRange = 0;
+ LocationDescriptor thread_context_memory_locator;
+ thread_context_memory_locator.DataSize =
+ static_cast<llvm::support::ulittle32_t>(thread_context.size());
+ thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(
+ size_before + thread_stream_size + helper_data.GetByteSize());
+ // Cache thie thread context memory so we can reuse for exceptions.
+ m_tid_to_reg_ctx[thread_sp->GetID()] = thread_context_memory_locator;
+
+ LLDB_LOGF(log, "AddThreadList for thread %d: thread_context %zu bytes",
+ thread_idx, thread_context.size());
+ helper_data.AppendData(thread_context.data(), thread_context.size());
+
+ llvm::minidump::Thread t;
+ t.ThreadId = static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
+ t.SuspendCount = static_cast<llvm::support::ulittle32_t>(
+ (thread_sp->GetState() == StateType::eStateSuspended) ? 1 : 0);
+ t.PriorityClass = static_cast<llvm::support::ulittle32_t>(0);
+ t.Priority = static_cast<llvm::support::ulittle32_t>(0);
+ t.EnvironmentBlock = static_cast<llvm::support::ulittle64_t>(0);
+ t.Stack = stack, t.Context = thread_context_memory_locator;
+
+ // We save off the stack object so we can circle back and clean it up.
+ m_thread_by_range_end[sp_region.GetRange().GetRangeEnd()] = t;
+ m_data.AppendData(&t, sizeof(llvm::minidump::Thread));
+ }
+
+ LLDB_LOGF(log, "AddThreadList(): total helper_data %" PRIx64 " bytes",
+ helper_data.GetByteSize());
+ m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());
+ return Status();
+}
+
+Status MinidumpFileBuilder::AddExceptions() {
+ lldb_private::ThreadList thread_list = m_process_sp->GetThreadList();
+ Status error;
+ const uint32_t num_threads = thread_list.GetSize();
+ for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
+ ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
+ StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
+ bool add_exception = false;
+ if (stop_info_sp) {
+ switch (stop_info_sp->GetStopReason()) {
+ case eStopReasonSignal:
+ case eStopReasonException:
+ add_exception = true;
+ break;
+ default:
+ break;
+ }
+ }
+ if (add_exception) {
+ constexpr size_t minidump_exception_size =
+ sizeof(llvm::minidump::ExceptionStream);
+ error = AddDirectory(StreamType::Exception, minidump_exception_size);
+ if (error.Fail())
+ return error;
+
+ StopInfoSP stop_info_sp = thread_sp->GetStopInfo();
+ RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());
+ Exception exp_record = {};
+ exp_record.ExceptionCode =
+ static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue());
+ exp_record.ExceptionFlags = static_cast<llvm::support::ulittle32_t>(0);
+ exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0);
+ exp_record.ExceptionAddress = reg_ctx_sp->GetPC();
+ exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(0);
+ exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
+ // exp_record.ExceptionInformation;
+
+ ExceptionStream exp_stream;
+ exp_stream.ThreadId =
+ static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());
+ exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);
+ exp_stream.ExceptionRecord = exp_record;
+ auto Iter = m_tid_to_reg_ctx.find(thread_sp->GetID());
+ if (Iter != m_tid_to_reg_ctx.end()) {
+ exp_stream.ThreadContext = Iter->second;
+ } else {
+ exp_stream.ThreadContext.DataSize = 0;
+ exp_stream.ThreadContext.RVA = 0;
+ }
+ m_data.AppendData(&exp_stream, minidump_exception_size);
+ }
+ }
+
+ return error;
+}
+
+lldb_private::Status MinidumpFileBuilder::AddMiscInfo() {
+ Status error;
+ error = AddDirectory(StreamType::MiscInfo,
+ sizeof(lldb_private::minidump::MinidumpMiscInfo));
+ if (error.Fail())
+ return error;
+
+ lldb_private::minidump::MinidumpMiscInfo misc_info;
+ misc_info.size = static_cast<llvm::support::ulittle32_t>(
+ sizeof(lldb_private::minidump::MinidumpMiscInfo));
+ // Default set flags1 to 0, in case that we will not be able to
+ // get any information
+ misc_info.flags1 = static_cast<llvm::support::ulittle32_t>(0);
+
+ lldb_private::ProcessInstanceInfo process_info;
+ m_process_sp->GetProcessInfo(process_info);
+ if (process_info.ProcessIDIsValid()) {
+ // Set flags1 to reflect that PID is filled in
+ misc_info.flags1 =
+ static_cast<llvm::support::ulittle32_t>(static_cast<uint32_t>(
+ lldb_private::minidump::MinidumpMiscInfoFlags::ProcessID));
+ misc_info.process_id =
+ static_cast<llvm::support::ulittle32_t>(process_info.GetProcessID());
+ }
+
+ m_data.AppendData(&misc_info,
+ sizeof(lldb_private::minidump::MinidumpMiscInfo));
+ return error;
+}
+
+std::unique_ptr<llvm::MemoryBuffer>
+getFileStreamHelper(const std::string &path) {
+ auto maybe_stream = llvm::MemoryBuffer::getFileAsStream(path);
+ if (!maybe_stream)
+ return nullptr;
+ return std::move(maybe_stream.get());
+}
+
+Status MinidumpFileBuilder::AddLinuxFileStreams() {
+ Status error;
+ // No-op if we are not on linux.
+ if (m_process_sp->GetTarget().GetArchitecture().GetTriple().getOS() !=
+ llvm::Triple::Linux)
+ return error;
+
+ std::vector<std::pair<StreamType, std::string>> files_with_stream_types = {
+ {StreamType::LinuxCPUInfo, "/proc/cpuinfo"},
+ {StreamType::LinuxLSBRelease, "/etc/lsb-release"},
+ };
+
+ lldb_private::ProcessInstanceInfo process_info;
+ m_process_sp->GetProcessInfo(process_info);
+ if (process_info.ProcessIDIsValid()) {
+ lldb::pid_t pid = process_info.GetProcessID();
+ std::string pid_str = std::to_string(pid);
+ files_with_stream_types.push_back(
+ {StreamType::LinuxProcStatus, "/proc/" + pid_str + "/status"});
+ files_with_stream_types.push_back(
+ {StreamType::LinuxCMDLine, "/proc/" + pid_str + "/cmdline"});
+ files_with_stream_types.push_back(
+ {StreamType::LinuxEnviron, "/proc/" + pid_str + "/environ"});
+ files_with_stream_types.push_back(
+ {StreamType::LinuxAuxv, "/proc/" + pid_str + "/auxv"});
+ files_with_stream_types.push_back(
+ {StreamType::LinuxMaps, "/proc/" + pid_str + "/maps"});
+ files_with_stream_types.push_back(
+ {StreamType::LinuxProcStat, "/proc/" + pid_str + "/stat"});
+ files_with_stream_types.push_back(
+ {StreamType::LinuxProcFD, "/proc/" + pid_str + "/fd"});
+ }
+
+ for (const auto &entry : files_with_stream_types) {
+ StreamType stream = entry.first;
+ std::string path = entry.second;
+ auto memory_buffer = getFileStreamHelper(path);
+
+ if (memory_buffer) {
+ size_t size = memory_buffer->getBufferSize();
+ if (size == 0)
+ continue;
+ error = AddDirectory(stream, size);
+ if (error.Fail())
+ return error;
+ m_data.AppendData(memory_buffer->getBufferStart(), size);
+ }
+ }
+
+ return error;
+}
+
+Status MinidumpFileBuilder::AddMemoryList(SaveCoreStyle core_style) {
+ Status error;
+
+ // We first save the thread stacks to ensure they fit in the first UINT32_MAX
+ // bytes of the core file. Thread structures in minidump files can only use
+ // 32 bit memory descriptiors, so we emit them first to ensure the memory is
+ // in accessible with a 32 bit offset.
+ Process::CoreFileMemoryRanges ranges_32;
+ Process::CoreFileMemoryRanges ranges_64;
+ error = m_process_sp->CalculateCoreFileSaveRanges(
+ SaveCoreStyle::eSaveCoreStackOnly, ranges_32);
+ if (error.Fail())
+ return error;
+
+ // Calculate totalsize including the current offset.
+ uint64_t total_size = GetCurrentDataEndOffset();
+ total_size += ranges_32.size() * sizeof(llvm::minidump::MemoryDescriptor);
+ std::unordered_set<addr_t> stack_start_addresses;
+ for (const auto &core_range : ranges_32) {
+ stack_start_addresses.insert(core_range.range.start());
+ total_size += core_range.range.size();
+ }
+
+ if (total_size >= UINT32_MAX) {
+ error.SetErrorStringWithFormat("Unable to write minidump. Stack memory "
+ "exceeds 32b limit. (Num Stacks %zu)",
+ ranges_32.size());
+ return error;
+ }
+
+ Process::CoreFileMemoryRanges all_core_memory_ranges;
+ if (core_style != SaveCoreStyle::eSaveCoreStackOnly) {
+ error = m_process_sp->CalculateCoreFileSaveRanges(core_style,
+ all_core_memory_ranges);
+ if (error.Fail())
+ return error;
+ }
+
+ // After saving the stacks, we start packing as much as we can into 32b.
+ // We apply a generous padding here so that the Directory, MemoryList and
+ // Memory64List sections all begin in 32b addressable space.
+ // Then anything overflow extends into 64b addressable space.
+ // All core memeroy ranges will either container nothing on stacks only
+ // or all the memory ranges including stacks
+ if (!all_core_memory_ranges.empty())
+ total_size +=
+ 256 + (all_core_memory_ranges.size() - stack_start_addresses.size()) *
+ sizeof(llvm::minidump::MemoryDescriptor_64);
+
+ for (const auto &core_range : all_core_memory_ranges) {
+ const addr_t range_size = core_range.range.size();
+ if (stack_start_addresses.count(core_range.range.start()) > 0)
+ // Don't double save stacks.
+ continue;
+
+ if (total_size + range_size < UINT32_MAX) {
+ ranges_32.push_back(core_range);
+ total_size += range_size;
+ } else {
+ ranges_64.push_back(core_range);
+ }
+ }
+
+ error = AddMemoryList_32(ranges_32);
+ if (error.Fail())
+ return error;
+
+ // Add the remaining memory as a 64b range.
+ if (!ranges_64.empty()) {
+ error = AddMemoryList_64(ranges_64);
+ if (error.Fail())
+ return error;
+ }
+
+ return FixThreadStacks();
+}
+
+Status MinidumpFileBuilder::DumpHeader() const {
+ // write header
+ llvm::minidump::Header header;
+ header.Signature = static_cast<llvm::support::ulittle32_t>(
+ llvm::minidump::Header::MagicSignature);
+ header.Version = static_cast<llvm::support::ulittle32_t>(
+ llvm::minidump::Header::MagicVersion);
+ header.NumberOfStreams =
+ static_cast<llvm::support::ulittle32_t>(m_directories.size());
+ // We write the directories right after the header.
+ header.StreamDirectoryRVA =
+ static_cast<llvm::support::ulittle32_t>(HEADER_SIZE);
+ header.Checksum = static_cast<llvm::support::ulittle32_t>(
+ 0u), // not used in most of the writers
+ header.TimeDateStamp =
+ static_cast<llvm::support::ulittle32_t>(std::time(nullptr));
+ header.Flags =
+ static_cast<llvm::support::ulittle64_t>(0u); // minidump normal flag
+
+ Status error;
+ size_t bytes_written;
+
+ m_core_file->SeekFromStart(0);
+ bytes_written = HEADER_SIZE;
+ error = m_core_file->Write(&header, bytes_written);
+ if (error.Fail() || bytes_written != HEADER_SIZE) {
+ if (bytes_written != HEADER_SIZE)
+ error.SetErrorStringWithFormat(
+ "Unable to write the minidump header (written %zd/%zd)",
+ bytes_written, HEADER_SIZE);
+ return error;
+ }
+ return error;
+}
+
+offset_t MinidumpFileBuilder::GetCurrentDataEndOffset() const {
+ return m_data.GetByteSize() + m_saved_data_size;
+}
+
+Status MinidumpFileBuilder::DumpDirectories() const {
+ Status error;
+ size_t bytes_written;
+ m_core_file->SeekFromStart(HEADER_SIZE);
+ for (const Directory &dir : m_directories) {
+ bytes_written = DIRECTORY_SIZE;
+ error = m_core_file->Write(&dir, bytes_written);
+ if (error.Fail() || bytes_written != DIRECTORY_SIZE) {
+ if (bytes_written != DIRECTORY_SIZE)
+ error.SetErrorStringWithFormat(
+ "unable to write the directory (written %zd/%zd)", bytes_written,
+ DIRECTORY_SIZE);
+ return error;
+ }
+ }
+
+ return error;
+}
+
+static uint64_t
+GetLargestRangeSize(const Process::CoreFileMemoryRanges &ranges) {
+ uint64_t max_size = 0;
+ for (const auto &core_range : ranges)
+ max_size = std::max(max_size, core_range.range.size());
+ return max_size;
+}
+
+Status
+MinidumpFileBuilder::AddMemoryList_32(Process::CoreFileMemoryRanges &ranges) {
+ std::vector<MemoryDescriptor> descriptors;
+ Status error;
+ if (ranges.size() == 0)
+ return error;
+
+ Log *log = GetLog(LLDBLog::Object);
+ size_t region_index = 0;
+ auto data_up =
+ std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0);
+ for (const auto &core_range : ranges) {
+ // Take the offset before we write.
+ const offset_t offset_for_data = GetCurrentDataEndOffset();
+ const addr_t addr = core_range.range.start();
+ const addr_t size = core_range.range.size();
+ const addr_t end = core_range.range.end();
+
+ LLDB_LOGF(log,
+ "AddMemoryList %zu/%zu reading memory for region "
+ "(%" PRIx64 " bytes) [%" PRIx64 ", %" PRIx64 ")",
+ region_index, ranges.size(), size, addr, addr + size);
+ ++region_index;
+
+ const size_t bytes_read =
+ m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);
+ if (error.Fail() || bytes_read == 0) {
+ LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s",
+ bytes_read, error.AsCString());
+ // Just skip sections with errors or zero bytes in 32b mode
+ continue;
+ } else if (bytes_read != size) {
+ LLDB_LOGF(
+ log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes",
+ addr, size);
+ }
+
+ MemoryDescriptor descriptor;
+ descriptor.StartOfMemoryRange =
+ static_cast<llvm::support::ulittle64_t>(addr);
+ descriptor.Memory.DataSize =
+ static_cast<llvm::support::ulittle32_t>(bytes_read);
+ descriptor.Memory.RVA =
+ static_cast<llvm::support::ulittle32_t>(offset_for_data);
+ descriptors.push_back(descriptor);
+ if (m_thread_by_range_end.count(end) > 0)
+ m_thread_by_range_end[end].Stack = descriptor;
+
+ // Add the data to the buffer, flush as needed.
+ error = AddData(data_up->GetBytes(), bytes_read);
+ if (error.Fail())
+ return error;
+ }
+
+ // Add a directory that references this list
+ // With a size of the number of ranges as a 32 bit num
+ // And then the size of all the ranges
+ error = AddDirectory(StreamType::MemoryList,
+ sizeof(llvm::support::ulittle32_t) +
+ descriptors.size() *
+ sizeof(llvm::minidump::MemoryDescriptor));
+ if (error.Fail())
+ return error;
+
+ llvm::support::ulittle32_t memory_ranges_num =
+ static_cast<llvm::support::ulittle32_t>(descriptors.size());
+ m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle32_t));
+ // For 32b we can get away with writing off the descriptors after the data.
+ // This means no cleanup loop needed.
+ m_data.AppendData(descriptors.data(),
+ descriptors.size() * sizeof(MemoryDescriptor));
+
+ return error;
+}
+
+Status
+MinidumpFileBuilder::AddMemoryList_64(Process::CoreFileMemoryRanges &ranges) {
+ Status error;
+ if (ranges.empty())
+ return error;
+
+ error = AddDirectory(StreamType::Memory64List,
+ (sizeof(llvm::support::ulittle64_t) * 2) +
+ ranges.size() *
+ sizeof(llvm::minidump::MemoryDescriptor_64));
+ if (error.Fail())
+ return error;
+
+ llvm::support::ulittle64_t memory_ranges_num =
+ static_cast<llvm::support::ulittle64_t>(ranges.size());
+ m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle64_t));
+ // Capture the starting offset for all the descriptors so we can clean them up
+ // if needed.
+ offset_t starting_offset =
+ GetCurrentDataEndOffset() + sizeof(llvm::support::ulittle64_t);
+ // The base_rva needs to start after the directories, which is right after
+ // this 8 byte variable.
+ offset_t base_rva =
+ starting_offset +
+ (ranges.size() * sizeof(llvm::minidump::MemoryDescriptor_64));
+ llvm::support::ulittle64_t memory_ranges_base_rva =
+ static_cast<llvm::support::ulittle64_t>(base_rva);
+ m_data.AppendData(&memory_ranges_base_rva,
+ sizeof(llvm::support::ulittle64_t));
+
+ bool cleanup_required = false;
+ std::vector<MemoryDescriptor_64> descriptors;
+ // Enumerate the ranges and create the memory descriptors so we can append
+ // them first
+ for (const auto core_range : ranges) {
+ // Add the space required to store the memory descriptor
+ MemoryDescriptor_64 memory_desc;
+ memory_desc.StartOfMemoryRange =
+ static_cast<llvm::support::ulittle64_t>(core_range.range.start());
+ memory_desc.DataSize =
+ static_cast<llvm::support::ulittle64_t>(core_range.range.size());
+ descriptors.push_back(memory_desc);
+ // Now write this memory descriptor to the buffer.
+ m_data.AppendData(&memory_desc, sizeof(MemoryDescriptor_64));
+ }
+
+ Log *log = GetLog(LLDBLog::Object);
+ size_t region_index = 0;
+ auto data_up =
+ std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0);
+ for (const auto &core_range : ranges) {
+ const addr_t addr = core_range.range.start();
+ const addr_t size = core_range.range.size();
+
+ LLDB_LOGF(log,
+ "AddMemoryList_64 %zu/%zu reading memory for region "
+ "(%" PRIx64 "bytes) "
+ "[%" PRIx64 ", %" PRIx64 ")",
+ region_index, ranges.size(), size, addr, addr + size);
+ ++region_index;
+
+ const size_t bytes_read =
+ m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);
+ if (error.Fail()) {
+ LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s",
+ bytes_read, error.AsCString());
+ error.Clear();
+ cleanup_required = true;
+ descriptors[region_index].DataSize = 0;
+ }
+ if (bytes_read != size) {
+ LLDB_LOGF(
+ log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes",
+ addr, size);
+ cleanup_required = true;
+ descriptors[region_index].DataSize = bytes_read;
+ }
+
+ // Add the data to the buffer, flush as needed.
+ error = AddData(data_up->GetBytes(), bytes_read);
+ if (error.Fail())
+ return error;
+ }
+
+ // Early return if there is no cleanup needed.
+ if (!cleanup_required) {
+ return error;
+ } else {
+ // Flush to disk we can make the fixes in place.
+ FlushBufferToDisk();
+ // Fixup the descriptors that were not read correctly.
+ m_core_file->SeekFromStart(starting_offset);
+ size_t bytes_written = sizeof(MemoryDescriptor_64) * descriptors.size();
+ error = m_core_file->Write(descriptors.data(), bytes_written);
+ if (error.Fail() ||
+ bytes_written != sizeof(MemoryDescriptor_64) * descriptors.size()) {
+ error.SetErrorStringWithFormat(
+ "unable to write the memory descriptors (written %zd/%zd)",
+ bytes_written, sizeof(MemoryDescriptor_64) * descriptors.size());
+ }
+
+ return error;
+ }
+}
+
+Status MinidumpFileBuilder::AddData(const void *data, uint64_t size) {
+ // This should also get chunked, because worst case we copy over a big
+ // object / memory range, say 5gb. In that case, we'd have to allocate 10gb
+ // 5 gb for the buffer we're copying from, and then 5gb for the buffer we're
+ // copying to. Which will be short lived and immedaitely go to disk, the goal
+ // here is to limit the number of bytes we need to host in memory at any given
+ // time.
+ m_data.AppendData(data, size);
+ if (m_data.GetByteSize() > MAX_WRITE_CHUNK_SIZE)
+ return FlushBufferToDisk();
+
+ return Status();
+}
+
+Status MinidumpFileBuilder::FlushBufferToDisk() {
+ Status error;
+ // Set the stream to it's end.
+ m_core_file->SeekFromStart(m_saved_data_size);
+ addr_t starting_size = m_data.GetByteSize();
+ addr_t remaining_bytes = starting_size;
+ offset_t offset = 0;
+
+ while (remaining_bytes > 0) {
+ size_t bytes_written = remaining_bytes;
+ // We don't care how many bytes we wrote unless we got an error
+ // so just decrement the remaining bytes.
+ error = m_core_file->Write(m_data.GetBytes() + offset, bytes_written);
+ if (error.Fail()) {
+ error.SetErrorStringWithFormat(
+ "Wrote incorrect number of bytes to minidump file. (written %" PRIx64
+ "/%" PRIx64 ")",
+ starting_size - remaining_bytes, starting_size);
+ return error;
+ }
+
+ offset += bytes_written;
+ remaining_bytes -= bytes_written;
+ }
+
+ m_saved_data_size += starting_size;
+ m_data.Clear();
+ return error;
+}
+
+Status MinidumpFileBuilder::DumpFile() {
+ Status error;
+ // If anything is left unsaved, dump it.
+ error = FlushBufferToDisk();
+ if (error.Fail())
+ return error;
+
+ // Overwrite the header which we filled in earlier.
+ error = DumpHeader();
+ if (error.Fail())
+ return error;
+
+ // Overwrite the space saved for directories
+ error = DumpDirectories();
+ if (error.Fail())
+ return error;
+
+ return error;
+}