brintos

brintos / llvm-project-archived public Read only

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