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 §ion : *mod->GetObjectFile()->GetSectionList()) {256 SizeOfImage += section->GetByteSize();257 }258 return SizeOfImage;259 }260 SectionSP sect_sp = mod->GetObjectFile()->GetBaseAddress().GetSection();261 262 if (!sect_sp) {263 return llvm::createStringError(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