Path: blob/main/contrib/llvm-project/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp
39644 views
//===-- MinidumpFileBuilder.cpp -------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "MinidumpFileBuilder.h"910#include "Plugins/Process/minidump/RegisterContextMinidump_ARM64.h"11#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h"1213#include "lldb/Core/Module.h"14#include "lldb/Core/ModuleList.h"15#include "lldb/Core/Section.h"16#include "lldb/Target/ABI.h"17#include "lldb/Target/MemoryRegionInfo.h"18#include "lldb/Target/Process.h"19#include "lldb/Target/RegisterContext.h"20#include "lldb/Target/StopInfo.h"21#include "lldb/Target/ThreadList.h"22#include "lldb/Utility/DataBufferHeap.h"23#include "lldb/Utility/DataExtractor.h"24#include "lldb/Utility/LLDBLog.h"25#include "lldb/Utility/Log.h"26#include "lldb/Utility/RangeMap.h"27#include "lldb/Utility/RegisterValue.h"2829#include "llvm/ADT/StringRef.h"30#include "llvm/BinaryFormat/Minidump.h"31#include "llvm/Support/ConvertUTF.h"32#include "llvm/Support/Endian.h"33#include "llvm/Support/Error.h"34#include "llvm/TargetParser/Triple.h"3536#include "Plugins/Process/minidump/MinidumpTypes.h"37#include "lldb/lldb-enumerations.h"38#include "lldb/lldb-forward.h"39#include "lldb/lldb-types.h"4041#include <algorithm>42#include <cinttypes>43#include <climits>44#include <cstddef>45#include <cstdint>46#include <functional>47#include <iostream>48#include <set>49#include <utility>50#include <vector>5152using namespace lldb;53using namespace lldb_private;54using namespace llvm::minidump;5556Status MinidumpFileBuilder::AddHeaderAndCalculateDirectories() {57// First set the offset on the file, and on the bytes saved58m_saved_data_size = HEADER_SIZE;59// We know we will have at least Misc, SystemInfo, Modules, and ThreadList60// (corresponding memory list for stacks) And an additional memory list for61// non-stacks.62lldb_private::Target &target = m_process_sp->GetTarget();63m_expected_directories = 6;64// Check if OS is linux and reserve directory space for all linux specific65// breakpad extension directories.66if (target.GetArchitecture().GetTriple().getOS() ==67llvm::Triple::OSType::Linux)68m_expected_directories += 9;6970// Go through all of the threads and check for exceptions.71lldb_private::ThreadList thread_list = m_process_sp->GetThreadList();72const uint32_t num_threads = thread_list.GetSize();73for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {74ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));75StopInfoSP stop_info_sp = thread_sp->GetStopInfo();76if (stop_info_sp) {77const StopReason &stop_reason = stop_info_sp->GetStopReason();78if (stop_reason == StopReason::eStopReasonException ||79stop_reason == StopReason::eStopReasonSignal)80m_expected_directories++;81}82}8384m_saved_data_size +=85m_expected_directories * sizeof(llvm::minidump::Directory);86Status error;87offset_t new_offset = m_core_file->SeekFromStart(m_saved_data_size);88if (new_offset != m_saved_data_size)89error.SetErrorStringWithFormat("Failed to fill in header and directory "90"sections. Written / Expected (%" PRIx6491" / %" PRIx64 ")",92new_offset, m_saved_data_size);9394return error;95}9697Status MinidumpFileBuilder::AddDirectory(StreamType type,98uint64_t stream_size) {99// We explicitly cast type, an 32b enum, to uint32_t to avoid warnings.100Status error;101if (GetCurrentDataEndOffset() > UINT32_MAX) {102error.SetErrorStringWithFormat("Unable to add directory for stream type "103"%x, offset is greater then 32 bit limit.",104(uint32_t)type);105return error;106}107108if (m_directories.size() + 1 > m_expected_directories) {109error.SetErrorStringWithFormat(110"Unable to add directory for stream type %x, exceeded expected number "111"of directories %zu.",112(uint32_t)type, m_expected_directories);113return error;114}115116LocationDescriptor loc;117loc.DataSize = static_cast<llvm::support::ulittle32_t>(stream_size);118// Stream will begin at the current end of data section119loc.RVA = static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset());120121Directory dir;122dir.Type = static_cast<llvm::support::little_t<StreamType>>(type);123dir.Location = loc;124125m_directories.push_back(dir);126return error;127}128129Status MinidumpFileBuilder::AddSystemInfo() {130Status error;131const llvm::Triple &target_triple =132m_process_sp->GetTarget().GetArchitecture().GetTriple();133error =134AddDirectory(StreamType::SystemInfo, sizeof(llvm::minidump::SystemInfo));135if (error.Fail())136return error;137138llvm::minidump::ProcessorArchitecture arch;139switch (target_triple.getArch()) {140case llvm::Triple::ArchType::x86_64:141arch = ProcessorArchitecture::AMD64;142break;143case llvm::Triple::ArchType::x86:144arch = ProcessorArchitecture::X86;145break;146case llvm::Triple::ArchType::arm:147arch = ProcessorArchitecture::ARM;148break;149case llvm::Triple::ArchType::aarch64:150arch = ProcessorArchitecture::ARM64;151break;152case llvm::Triple::ArchType::mips64:153case llvm::Triple::ArchType::mips64el:154case llvm::Triple::ArchType::mips:155case llvm::Triple::ArchType::mipsel:156arch = ProcessorArchitecture::MIPS;157break;158case llvm::Triple::ArchType::ppc64:159case llvm::Triple::ArchType::ppc:160case llvm::Triple::ArchType::ppc64le:161arch = ProcessorArchitecture::PPC;162break;163default:164error.SetErrorStringWithFormat("Architecture %s not supported.",165target_triple.getArchName().str().c_str());166return error;167};168169llvm::support::little_t<OSPlatform> platform_id;170switch (target_triple.getOS()) {171case llvm::Triple::OSType::Linux:172if (target_triple.getEnvironment() ==173llvm::Triple::EnvironmentType::Android)174platform_id = OSPlatform::Android;175else176platform_id = OSPlatform::Linux;177break;178case llvm::Triple::OSType::Win32:179platform_id = OSPlatform::Win32NT;180break;181case llvm::Triple::OSType::MacOSX:182platform_id = OSPlatform::MacOSX;183break;184case llvm::Triple::OSType::IOS:185platform_id = OSPlatform::IOS;186break;187default:188error.SetErrorStringWithFormat("OS %s not supported.",189target_triple.getOSName().str().c_str());190return error;191};192193llvm::minidump::SystemInfo sys_info;194sys_info.ProcessorArch =195static_cast<llvm::support::little_t<ProcessorArchitecture>>(arch);196// Global offset to beginning of a csd_string in a data section197sys_info.CSDVersionRVA = static_cast<llvm::support::ulittle32_t>(198GetCurrentDataEndOffset() + sizeof(llvm::minidump::SystemInfo));199sys_info.PlatformId = platform_id;200m_data.AppendData(&sys_info, sizeof(llvm::minidump::SystemInfo));201202std::string csd_string;203204error = WriteString(csd_string, &m_data);205if (error.Fail()) {206error.SetErrorString("Unable to convert the csd string to UTF16.");207return error;208}209210return error;211}212213Status WriteString(const std::string &to_write,214lldb_private::DataBufferHeap *buffer) {215Status error;216// let the StringRef eat also null termination char217llvm::StringRef to_write_ref(to_write.c_str(), to_write.size() + 1);218llvm::SmallVector<llvm::UTF16, 128> to_write_utf16;219220bool converted = convertUTF8ToUTF16String(to_write_ref, to_write_utf16);221if (!converted) {222error.SetErrorStringWithFormat(223"Unable to convert the string to UTF16. Failed to convert %s",224to_write.c_str());225return error;226}227228// size of the UTF16 string should be written without the null termination229// character that is stored in 2 bytes230llvm::support::ulittle32_t to_write_size(to_write_utf16.size_in_bytes() - 2);231232buffer->AppendData(&to_write_size, sizeof(llvm::support::ulittle32_t));233buffer->AppendData(to_write_utf16.data(), to_write_utf16.size_in_bytes());234235return error;236}237238llvm::Expected<uint64_t> getModuleFileSize(Target &target,239const ModuleSP &mod) {240// JIT module has the same vm and file size.241uint64_t SizeOfImage = 0;242if (mod->GetObjectFile()->CalculateType() == ObjectFile::Type::eTypeJIT) {243for (const auto §ion : *mod->GetObjectFile()->GetSectionList()) {244SizeOfImage += section->GetByteSize();245}246return SizeOfImage;247}248SectionSP sect_sp = mod->GetObjectFile()->GetBaseAddress().GetSection();249250if (!sect_sp) {251return llvm::createStringError(std::errc::operation_not_supported,252"Couldn't obtain the section information.");253}254lldb::addr_t sect_addr = sect_sp->GetLoadBaseAddress(&target);255// Use memory size since zero fill sections, like ".bss", will be smaller on256// disk.257lldb::addr_t sect_size = sect_sp->GetByteSize();258// This will usually be zero, but make sure to calculate the BaseOfImage259// offset.260const lldb::addr_t base_sect_offset =261mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target) -262sect_addr;263SizeOfImage = sect_size - base_sect_offset;264lldb::addr_t next_sect_addr = sect_addr + sect_size;265Address sect_so_addr;266target.ResolveLoadAddress(next_sect_addr, sect_so_addr);267lldb::SectionSP next_sect_sp = sect_so_addr.GetSection();268while (next_sect_sp &&269next_sect_sp->GetLoadBaseAddress(&target) == next_sect_addr) {270sect_size = sect_sp->GetByteSize();271SizeOfImage += sect_size;272next_sect_addr += sect_size;273target.ResolveLoadAddress(next_sect_addr, sect_so_addr);274next_sect_sp = sect_so_addr.GetSection();275}276277return SizeOfImage;278}279280// ModuleList stream consists of a number of modules, followed by an array281// of llvm::minidump::Module's structures. Every structure informs about a282// single module. Additional data of variable length, such as module's names,283// are stored just after the ModuleList stream. The llvm::minidump::Module284// structures point to this helper data by global offset.285Status MinidumpFileBuilder::AddModuleList() {286constexpr size_t minidump_module_size = sizeof(llvm::minidump::Module);287Status error;288289lldb_private::Target &target = m_process_sp->GetTarget();290const ModuleList &modules = target.GetImages();291llvm::support::ulittle32_t modules_count =292static_cast<llvm::support::ulittle32_t>(modules.GetSize());293294// This helps us with getting the correct global offset in minidump295// file later, when we will be setting up offsets from the296// the llvm::minidump::Module's structures into helper data297size_t size_before = GetCurrentDataEndOffset();298299// This is the size of the main part of the ModuleList stream.300// It consists of a module number and corresponding number of301// structs describing individual modules302size_t module_stream_size =303sizeof(llvm::support::ulittle32_t) + modules_count * minidump_module_size;304305// Adding directory describing this stream.306error = AddDirectory(StreamType::ModuleList, module_stream_size);307if (error.Fail())308return error;309310m_data.AppendData(&modules_count, sizeof(llvm::support::ulittle32_t));311312// Temporary storage for the helper data (of variable length)313// as these cannot be dumped to m_data before dumping entire314// array of module structures.315DataBufferHeap helper_data;316317for (size_t i = 0; i < modules_count; ++i) {318ModuleSP mod = modules.GetModuleAtIndex(i);319std::string module_name = mod->GetSpecificationDescription();320auto maybe_mod_size = getModuleFileSize(target, mod);321if (!maybe_mod_size) {322llvm::Error mod_size_err = maybe_mod_size.takeError();323llvm::handleAllErrors(std::move(mod_size_err),324[&](const llvm::ErrorInfoBase &E) {325error.SetErrorStringWithFormat(326"Unable to get the size of module %s: %s.",327module_name.c_str(), E.message().c_str());328});329return error;330}331332uint64_t mod_size = std::move(*maybe_mod_size);333334llvm::support::ulittle32_t signature =335static_cast<llvm::support::ulittle32_t>(336static_cast<uint32_t>(minidump::CvSignature::ElfBuildId));337auto uuid = mod->GetUUID().GetBytes();338339VSFixedFileInfo info;340info.Signature = static_cast<llvm::support::ulittle32_t>(0u);341info.StructVersion = static_cast<llvm::support::ulittle32_t>(0u);342info.FileVersionHigh = static_cast<llvm::support::ulittle32_t>(0u);343info.FileVersionLow = static_cast<llvm::support::ulittle32_t>(0u);344info.ProductVersionHigh = static_cast<llvm::support::ulittle32_t>(0u);345info.ProductVersionLow = static_cast<llvm::support::ulittle32_t>(0u);346info.FileFlagsMask = static_cast<llvm::support::ulittle32_t>(0u);347info.FileFlags = static_cast<llvm::support::ulittle32_t>(0u);348info.FileOS = static_cast<llvm::support::ulittle32_t>(0u);349info.FileType = static_cast<llvm::support::ulittle32_t>(0u);350info.FileSubtype = static_cast<llvm::support::ulittle32_t>(0u);351info.FileDateHigh = static_cast<llvm::support::ulittle32_t>(0u);352info.FileDateLow = static_cast<llvm::support::ulittle32_t>(0u);353354LocationDescriptor ld;355ld.DataSize = static_cast<llvm::support::ulittle32_t>(0u);356ld.RVA = static_cast<llvm::support::ulittle32_t>(0u);357358// Setting up LocationDescriptor for uuid string. The global offset into359// minidump file is calculated.360LocationDescriptor ld_cv;361ld_cv.DataSize = static_cast<llvm::support::ulittle32_t>(362sizeof(llvm::support::ulittle32_t) + uuid.size());363ld_cv.RVA = static_cast<llvm::support::ulittle32_t>(364size_before + module_stream_size + helper_data.GetByteSize());365366helper_data.AppendData(&signature, sizeof(llvm::support::ulittle32_t));367helper_data.AppendData(uuid.begin(), uuid.size());368369llvm::minidump::Module m;370m.BaseOfImage = static_cast<llvm::support::ulittle64_t>(371mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target));372m.SizeOfImage = static_cast<llvm::support::ulittle32_t>(mod_size);373m.Checksum = static_cast<llvm::support::ulittle32_t>(0);374m.TimeDateStamp =375static_cast<llvm::support::ulittle32_t>(std::time(nullptr));376m.ModuleNameRVA = static_cast<llvm::support::ulittle32_t>(377size_before + module_stream_size + helper_data.GetByteSize());378m.VersionInfo = info;379m.CvRecord = ld_cv;380m.MiscRecord = ld;381382error = WriteString(module_name, &helper_data);383384if (error.Fail())385return error;386387m_data.AppendData(&m, sizeof(llvm::minidump::Module));388}389390m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());391return error;392}393394uint16_t read_register_u16_raw(RegisterContext *reg_ctx,395llvm::StringRef reg_name) {396const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);397if (!reg_info)398return 0;399lldb_private::RegisterValue reg_value;400bool success = reg_ctx->ReadRegister(reg_info, reg_value);401if (!success)402return 0;403return reg_value.GetAsUInt16();404}405406uint32_t read_register_u32_raw(RegisterContext *reg_ctx,407llvm::StringRef reg_name) {408const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);409if (!reg_info)410return 0;411lldb_private::RegisterValue reg_value;412bool success = reg_ctx->ReadRegister(reg_info, reg_value);413if (!success)414return 0;415return reg_value.GetAsUInt32();416}417418uint64_t read_register_u64_raw(RegisterContext *reg_ctx,419llvm::StringRef reg_name) {420const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);421if (!reg_info)422return 0;423lldb_private::RegisterValue reg_value;424bool success = reg_ctx->ReadRegister(reg_info, reg_value);425if (!success)426return 0;427return reg_value.GetAsUInt64();428}429430llvm::support::ulittle16_t read_register_u16(RegisterContext *reg_ctx,431llvm::StringRef reg_name) {432return static_cast<llvm::support::ulittle16_t>(433read_register_u16_raw(reg_ctx, reg_name));434}435436llvm::support::ulittle32_t read_register_u32(RegisterContext *reg_ctx,437llvm::StringRef reg_name) {438return static_cast<llvm::support::ulittle32_t>(439read_register_u32_raw(reg_ctx, reg_name));440}441442llvm::support::ulittle64_t read_register_u64(RegisterContext *reg_ctx,443llvm::StringRef reg_name) {444return static_cast<llvm::support::ulittle64_t>(445read_register_u64_raw(reg_ctx, reg_name));446}447448void read_register_u128(RegisterContext *reg_ctx, llvm::StringRef reg_name,449uint8_t *dst) {450const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name);451if (reg_info) {452lldb_private::RegisterValue reg_value;453if (reg_ctx->ReadRegister(reg_info, reg_value)) {454Status error;455uint32_t bytes_copied = reg_value.GetAsMemoryData(456*reg_info, dst, 16, lldb::ByteOrder::eByteOrderLittle, error);457if (bytes_copied == 16)458return;459}460}461// If anything goes wrong, then zero out the register value.462memset(dst, 0, 16);463}464465lldb_private::minidump::MinidumpContext_x86_64466GetThreadContext_x86_64(RegisterContext *reg_ctx) {467lldb_private::minidump::MinidumpContext_x86_64 thread_context = {};468thread_context.p1_home = {};469thread_context.context_flags = static_cast<uint32_t>(470lldb_private::minidump::MinidumpContext_x86_64_Flags::x86_64_Flag |471lldb_private::minidump::MinidumpContext_x86_64_Flags::Control |472lldb_private::minidump::MinidumpContext_x86_64_Flags::Segments |473lldb_private::minidump::MinidumpContext_x86_64_Flags::Integer);474thread_context.rax = read_register_u64(reg_ctx, "rax");475thread_context.rbx = read_register_u64(reg_ctx, "rbx");476thread_context.rcx = read_register_u64(reg_ctx, "rcx");477thread_context.rdx = read_register_u64(reg_ctx, "rdx");478thread_context.rdi = read_register_u64(reg_ctx, "rdi");479thread_context.rsi = read_register_u64(reg_ctx, "rsi");480thread_context.rbp = read_register_u64(reg_ctx, "rbp");481thread_context.rsp = read_register_u64(reg_ctx, "rsp");482thread_context.r8 = read_register_u64(reg_ctx, "r8");483thread_context.r9 = read_register_u64(reg_ctx, "r9");484thread_context.r10 = read_register_u64(reg_ctx, "r10");485thread_context.r11 = read_register_u64(reg_ctx, "r11");486thread_context.r12 = read_register_u64(reg_ctx, "r12");487thread_context.r13 = read_register_u64(reg_ctx, "r13");488thread_context.r14 = read_register_u64(reg_ctx, "r14");489thread_context.r15 = read_register_u64(reg_ctx, "r15");490thread_context.rip = read_register_u64(reg_ctx, "rip");491thread_context.eflags = read_register_u32(reg_ctx, "rflags");492thread_context.cs = read_register_u16(reg_ctx, "cs");493thread_context.fs = read_register_u16(reg_ctx, "fs");494thread_context.gs = read_register_u16(reg_ctx, "gs");495thread_context.ss = read_register_u16(reg_ctx, "ss");496thread_context.ds = read_register_u16(reg_ctx, "ds");497return thread_context;498}499500minidump::RegisterContextMinidump_ARM64::Context501GetThreadContext_ARM64(RegisterContext *reg_ctx) {502minidump::RegisterContextMinidump_ARM64::Context thread_context = {};503thread_context.context_flags = static_cast<uint32_t>(504minidump::RegisterContextMinidump_ARM64::Flags::ARM64_Flag |505minidump::RegisterContextMinidump_ARM64::Flags::Integer |506minidump::RegisterContextMinidump_ARM64::Flags::FloatingPoint);507char reg_name[16];508for (uint32_t i = 0; i < 31; ++i) {509snprintf(reg_name, sizeof(reg_name), "x%u", i);510thread_context.x[i] = read_register_u64(reg_ctx, reg_name);511}512// Work around a bug in debugserver where "sp" on arm64 doesn't have the alt513// name set to "x31"514thread_context.x[31] = read_register_u64(reg_ctx, "sp");515thread_context.pc = read_register_u64(reg_ctx, "pc");516thread_context.cpsr = read_register_u32(reg_ctx, "cpsr");517thread_context.fpsr = read_register_u32(reg_ctx, "fpsr");518thread_context.fpcr = read_register_u32(reg_ctx, "fpcr");519for (uint32_t i = 0; i < 32; ++i) {520snprintf(reg_name, sizeof(reg_name), "v%u", i);521read_register_u128(reg_ctx, reg_name, &thread_context.v[i * 16]);522}523return thread_context;524}525526class ArchThreadContexts {527llvm::Triple::ArchType m_arch;528union {529lldb_private::minidump::MinidumpContext_x86_64 x86_64;530lldb_private::minidump::RegisterContextMinidump_ARM64::Context arm64;531};532533public:534ArchThreadContexts(llvm::Triple::ArchType arch) : m_arch(arch) {}535536bool prepareRegisterContext(RegisterContext *reg_ctx) {537switch (m_arch) {538case llvm::Triple::ArchType::x86_64:539x86_64 = GetThreadContext_x86_64(reg_ctx);540return true;541case llvm::Triple::ArchType::aarch64:542arm64 = GetThreadContext_ARM64(reg_ctx);543return true;544default:545break;546}547return false;548}549550const void *data() const { return &x86_64; }551552size_t size() const {553switch (m_arch) {554case llvm::Triple::ArchType::x86_64:555return sizeof(x86_64);556case llvm::Triple::ArchType::aarch64:557return sizeof(arm64);558default:559break;560}561return 0;562}563};564565Status MinidumpFileBuilder::FixThreadStacks() {566Status error;567// If we have anything in the heap flush it.568FlushBufferToDisk();569m_core_file->SeekFromStart(m_thread_list_start);570for (auto &pair : m_thread_by_range_end) {571// The thread objects will get a new memory descriptor added572// When we are emitting the memory list and then we write it here573const llvm::minidump::Thread &thread = pair.second;574size_t bytes_to_write = sizeof(llvm::minidump::Thread);575size_t bytes_written = bytes_to_write;576error = m_core_file->Write(&thread, bytes_written);577if (error.Fail() || bytes_to_write != bytes_written) {578error.SetErrorStringWithFormat(579"Wrote incorrect number of bytes to minidump file. (written %zd/%zd)",580bytes_written, bytes_to_write);581return error;582}583}584585return error;586}587588Status MinidumpFileBuilder::AddThreadList() {589constexpr size_t minidump_thread_size = sizeof(llvm::minidump::Thread);590lldb_private::ThreadList thread_list = m_process_sp->GetThreadList();591592// size of the entire thread stream consists of:593// number of threads and threads array594size_t thread_stream_size = sizeof(llvm::support::ulittle32_t) +595thread_list.GetSize() * minidump_thread_size;596// save for the ability to set up RVA597size_t size_before = GetCurrentDataEndOffset();598Status error;599error = AddDirectory(StreamType::ThreadList, thread_stream_size);600if (error.Fail())601return error;602603llvm::support::ulittle32_t thread_count =604static_cast<llvm::support::ulittle32_t>(thread_list.GetSize());605m_data.AppendData(&thread_count, sizeof(llvm::support::ulittle32_t));606607// Take the offset after the thread count.608m_thread_list_start = GetCurrentDataEndOffset();609DataBufferHeap helper_data;610611const uint32_t num_threads = thread_list.GetSize();612Log *log = GetLog(LLDBLog::Object);613for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {614ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));615RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());616617if (!reg_ctx_sp) {618error.SetErrorString("Unable to get the register context.");619return error;620}621RegisterContext *reg_ctx = reg_ctx_sp.get();622Target &target = m_process_sp->GetTarget();623const ArchSpec &arch = target.GetArchitecture();624ArchThreadContexts thread_context(arch.GetMachine());625if (!thread_context.prepareRegisterContext(reg_ctx)) {626error.SetErrorStringWithFormat(627"architecture %s not supported.",628arch.GetTriple().getArchName().str().c_str());629return error;630}631632uint64_t sp = reg_ctx->GetSP();633MemoryRegionInfo sp_region;634m_process_sp->GetMemoryRegionInfo(sp, sp_region);635636// Emit a blank descriptor637MemoryDescriptor stack;638LocationDescriptor empty_label;639empty_label.DataSize = 0;640empty_label.RVA = 0;641stack.Memory = empty_label;642stack.StartOfMemoryRange = 0;643LocationDescriptor thread_context_memory_locator;644thread_context_memory_locator.DataSize =645static_cast<llvm::support::ulittle32_t>(thread_context.size());646thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>(647size_before + thread_stream_size + helper_data.GetByteSize());648// Cache thie thread context memory so we can reuse for exceptions.649m_tid_to_reg_ctx[thread_sp->GetID()] = thread_context_memory_locator;650651LLDB_LOGF(log, "AddThreadList for thread %d: thread_context %zu bytes",652thread_idx, thread_context.size());653helper_data.AppendData(thread_context.data(), thread_context.size());654655llvm::minidump::Thread t;656t.ThreadId = static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());657t.SuspendCount = static_cast<llvm::support::ulittle32_t>(658(thread_sp->GetState() == StateType::eStateSuspended) ? 1 : 0);659t.PriorityClass = static_cast<llvm::support::ulittle32_t>(0);660t.Priority = static_cast<llvm::support::ulittle32_t>(0);661t.EnvironmentBlock = static_cast<llvm::support::ulittle64_t>(0);662t.Stack = stack, t.Context = thread_context_memory_locator;663664// We save off the stack object so we can circle back and clean it up.665m_thread_by_range_end[sp_region.GetRange().GetRangeEnd()] = t;666m_data.AppendData(&t, sizeof(llvm::minidump::Thread));667}668669LLDB_LOGF(log, "AddThreadList(): total helper_data %" PRIx64 " bytes",670helper_data.GetByteSize());671m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize());672return Status();673}674675Status MinidumpFileBuilder::AddExceptions() {676lldb_private::ThreadList thread_list = m_process_sp->GetThreadList();677Status error;678const uint32_t num_threads = thread_list.GetSize();679for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {680ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));681StopInfoSP stop_info_sp = thread_sp->GetStopInfo();682bool add_exception = false;683if (stop_info_sp) {684switch (stop_info_sp->GetStopReason()) {685case eStopReasonSignal:686case eStopReasonException:687add_exception = true;688break;689default:690break;691}692}693if (add_exception) {694constexpr size_t minidump_exception_size =695sizeof(llvm::minidump::ExceptionStream);696error = AddDirectory(StreamType::Exception, minidump_exception_size);697if (error.Fail())698return error;699700StopInfoSP stop_info_sp = thread_sp->GetStopInfo();701RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext());702Exception exp_record = {};703exp_record.ExceptionCode =704static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue());705exp_record.ExceptionFlags = static_cast<llvm::support::ulittle32_t>(0);706exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0);707exp_record.ExceptionAddress = reg_ctx_sp->GetPC();708exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(0);709exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);710// exp_record.ExceptionInformation;711712ExceptionStream exp_stream;713exp_stream.ThreadId =714static_cast<llvm::support::ulittle32_t>(thread_sp->GetID());715exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0);716exp_stream.ExceptionRecord = exp_record;717auto Iter = m_tid_to_reg_ctx.find(thread_sp->GetID());718if (Iter != m_tid_to_reg_ctx.end()) {719exp_stream.ThreadContext = Iter->second;720} else {721exp_stream.ThreadContext.DataSize = 0;722exp_stream.ThreadContext.RVA = 0;723}724m_data.AppendData(&exp_stream, minidump_exception_size);725}726}727728return error;729}730731lldb_private::Status MinidumpFileBuilder::AddMiscInfo() {732Status error;733error = AddDirectory(StreamType::MiscInfo,734sizeof(lldb_private::minidump::MinidumpMiscInfo));735if (error.Fail())736return error;737738lldb_private::minidump::MinidumpMiscInfo misc_info;739misc_info.size = static_cast<llvm::support::ulittle32_t>(740sizeof(lldb_private::minidump::MinidumpMiscInfo));741// Default set flags1 to 0, in case that we will not be able to742// get any information743misc_info.flags1 = static_cast<llvm::support::ulittle32_t>(0);744745lldb_private::ProcessInstanceInfo process_info;746m_process_sp->GetProcessInfo(process_info);747if (process_info.ProcessIDIsValid()) {748// Set flags1 to reflect that PID is filled in749misc_info.flags1 =750static_cast<llvm::support::ulittle32_t>(static_cast<uint32_t>(751lldb_private::minidump::MinidumpMiscInfoFlags::ProcessID));752misc_info.process_id =753static_cast<llvm::support::ulittle32_t>(process_info.GetProcessID());754}755756m_data.AppendData(&misc_info,757sizeof(lldb_private::minidump::MinidumpMiscInfo));758return error;759}760761std::unique_ptr<llvm::MemoryBuffer>762getFileStreamHelper(const std::string &path) {763auto maybe_stream = llvm::MemoryBuffer::getFileAsStream(path);764if (!maybe_stream)765return nullptr;766return std::move(maybe_stream.get());767}768769Status MinidumpFileBuilder::AddLinuxFileStreams() {770Status error;771// No-op if we are not on linux.772if (m_process_sp->GetTarget().GetArchitecture().GetTriple().getOS() !=773llvm::Triple::Linux)774return error;775776std::vector<std::pair<StreamType, std::string>> files_with_stream_types = {777{StreamType::LinuxCPUInfo, "/proc/cpuinfo"},778{StreamType::LinuxLSBRelease, "/etc/lsb-release"},779};780781lldb_private::ProcessInstanceInfo process_info;782m_process_sp->GetProcessInfo(process_info);783if (process_info.ProcessIDIsValid()) {784lldb::pid_t pid = process_info.GetProcessID();785std::string pid_str = std::to_string(pid);786files_with_stream_types.push_back(787{StreamType::LinuxProcStatus, "/proc/" + pid_str + "/status"});788files_with_stream_types.push_back(789{StreamType::LinuxCMDLine, "/proc/" + pid_str + "/cmdline"});790files_with_stream_types.push_back(791{StreamType::LinuxEnviron, "/proc/" + pid_str + "/environ"});792files_with_stream_types.push_back(793{StreamType::LinuxAuxv, "/proc/" + pid_str + "/auxv"});794files_with_stream_types.push_back(795{StreamType::LinuxMaps, "/proc/" + pid_str + "/maps"});796files_with_stream_types.push_back(797{StreamType::LinuxProcStat, "/proc/" + pid_str + "/stat"});798files_with_stream_types.push_back(799{StreamType::LinuxProcFD, "/proc/" + pid_str + "/fd"});800}801802for (const auto &entry : files_with_stream_types) {803StreamType stream = entry.first;804std::string path = entry.second;805auto memory_buffer = getFileStreamHelper(path);806807if (memory_buffer) {808size_t size = memory_buffer->getBufferSize();809if (size == 0)810continue;811error = AddDirectory(stream, size);812if (error.Fail())813return error;814m_data.AppendData(memory_buffer->getBufferStart(), size);815}816}817818return error;819}820821Status MinidumpFileBuilder::AddMemoryList(SaveCoreStyle core_style) {822Status error;823824// We first save the thread stacks to ensure they fit in the first UINT32_MAX825// bytes of the core file. Thread structures in minidump files can only use826// 32 bit memory descriptiors, so we emit them first to ensure the memory is827// in accessible with a 32 bit offset.828Process::CoreFileMemoryRanges ranges_32;829Process::CoreFileMemoryRanges ranges_64;830error = m_process_sp->CalculateCoreFileSaveRanges(831SaveCoreStyle::eSaveCoreStackOnly, ranges_32);832if (error.Fail())833return error;834835// Calculate totalsize including the current offset.836uint64_t total_size = GetCurrentDataEndOffset();837total_size += ranges_32.size() * sizeof(llvm::minidump::MemoryDescriptor);838std::unordered_set<addr_t> stack_start_addresses;839for (const auto &core_range : ranges_32) {840stack_start_addresses.insert(core_range.range.start());841total_size += core_range.range.size();842}843844if (total_size >= UINT32_MAX) {845error.SetErrorStringWithFormat("Unable to write minidump. Stack memory "846"exceeds 32b limit. (Num Stacks %zu)",847ranges_32.size());848return error;849}850851Process::CoreFileMemoryRanges all_core_memory_ranges;852if (core_style != SaveCoreStyle::eSaveCoreStackOnly) {853error = m_process_sp->CalculateCoreFileSaveRanges(core_style,854all_core_memory_ranges);855if (error.Fail())856return error;857}858859// After saving the stacks, we start packing as much as we can into 32b.860// We apply a generous padding here so that the Directory, MemoryList and861// Memory64List sections all begin in 32b addressable space.862// Then anything overflow extends into 64b addressable space.863// All core memeroy ranges will either container nothing on stacks only864// or all the memory ranges including stacks865if (!all_core_memory_ranges.empty())866total_size +=867256 + (all_core_memory_ranges.size() - stack_start_addresses.size()) *868sizeof(llvm::minidump::MemoryDescriptor_64);869870for (const auto &core_range : all_core_memory_ranges) {871const addr_t range_size = core_range.range.size();872if (stack_start_addresses.count(core_range.range.start()) > 0)873// Don't double save stacks.874continue;875876if (total_size + range_size < UINT32_MAX) {877ranges_32.push_back(core_range);878total_size += range_size;879} else {880ranges_64.push_back(core_range);881}882}883884error = AddMemoryList_32(ranges_32);885if (error.Fail())886return error;887888// Add the remaining memory as a 64b range.889if (!ranges_64.empty()) {890error = AddMemoryList_64(ranges_64);891if (error.Fail())892return error;893}894895return FixThreadStacks();896}897898Status MinidumpFileBuilder::DumpHeader() const {899// write header900llvm::minidump::Header header;901header.Signature = static_cast<llvm::support::ulittle32_t>(902llvm::minidump::Header::MagicSignature);903header.Version = static_cast<llvm::support::ulittle32_t>(904llvm::minidump::Header::MagicVersion);905header.NumberOfStreams =906static_cast<llvm::support::ulittle32_t>(m_directories.size());907// We write the directories right after the header.908header.StreamDirectoryRVA =909static_cast<llvm::support::ulittle32_t>(HEADER_SIZE);910header.Checksum = static_cast<llvm::support::ulittle32_t>(9110u), // not used in most of the writers912header.TimeDateStamp =913static_cast<llvm::support::ulittle32_t>(std::time(nullptr));914header.Flags =915static_cast<llvm::support::ulittle64_t>(0u); // minidump normal flag916917Status error;918size_t bytes_written;919920m_core_file->SeekFromStart(0);921bytes_written = HEADER_SIZE;922error = m_core_file->Write(&header, bytes_written);923if (error.Fail() || bytes_written != HEADER_SIZE) {924if (bytes_written != HEADER_SIZE)925error.SetErrorStringWithFormat(926"Unable to write the minidump header (written %zd/%zd)",927bytes_written, HEADER_SIZE);928return error;929}930return error;931}932933offset_t MinidumpFileBuilder::GetCurrentDataEndOffset() const {934return m_data.GetByteSize() + m_saved_data_size;935}936937Status MinidumpFileBuilder::DumpDirectories() const {938Status error;939size_t bytes_written;940m_core_file->SeekFromStart(HEADER_SIZE);941for (const Directory &dir : m_directories) {942bytes_written = DIRECTORY_SIZE;943error = m_core_file->Write(&dir, bytes_written);944if (error.Fail() || bytes_written != DIRECTORY_SIZE) {945if (bytes_written != DIRECTORY_SIZE)946error.SetErrorStringWithFormat(947"unable to write the directory (written %zd/%zd)", bytes_written,948DIRECTORY_SIZE);949return error;950}951}952953return error;954}955956static uint64_t957GetLargestRangeSize(const Process::CoreFileMemoryRanges &ranges) {958uint64_t max_size = 0;959for (const auto &core_range : ranges)960max_size = std::max(max_size, core_range.range.size());961return max_size;962}963964Status965MinidumpFileBuilder::AddMemoryList_32(Process::CoreFileMemoryRanges &ranges) {966std::vector<MemoryDescriptor> descriptors;967Status error;968if (ranges.size() == 0)969return error;970971Log *log = GetLog(LLDBLog::Object);972size_t region_index = 0;973auto data_up =974std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0);975for (const auto &core_range : ranges) {976// Take the offset before we write.977const offset_t offset_for_data = GetCurrentDataEndOffset();978const addr_t addr = core_range.range.start();979const addr_t size = core_range.range.size();980const addr_t end = core_range.range.end();981982LLDB_LOGF(log,983"AddMemoryList %zu/%zu reading memory for region "984"(%" PRIx64 " bytes) [%" PRIx64 ", %" PRIx64 ")",985region_index, ranges.size(), size, addr, addr + size);986++region_index;987988const size_t bytes_read =989m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);990if (error.Fail() || bytes_read == 0) {991LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s",992bytes_read, error.AsCString());993// Just skip sections with errors or zero bytes in 32b mode994continue;995} else if (bytes_read != size) {996LLDB_LOGF(997log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes",998addr, size);999}10001001MemoryDescriptor descriptor;1002descriptor.StartOfMemoryRange =1003static_cast<llvm::support::ulittle64_t>(addr);1004descriptor.Memory.DataSize =1005static_cast<llvm::support::ulittle32_t>(bytes_read);1006descriptor.Memory.RVA =1007static_cast<llvm::support::ulittle32_t>(offset_for_data);1008descriptors.push_back(descriptor);1009if (m_thread_by_range_end.count(end) > 0)1010m_thread_by_range_end[end].Stack = descriptor;10111012// Add the data to the buffer, flush as needed.1013error = AddData(data_up->GetBytes(), bytes_read);1014if (error.Fail())1015return error;1016}10171018// Add a directory that references this list1019// With a size of the number of ranges as a 32 bit num1020// And then the size of all the ranges1021error = AddDirectory(StreamType::MemoryList,1022sizeof(llvm::support::ulittle32_t) +1023descriptors.size() *1024sizeof(llvm::minidump::MemoryDescriptor));1025if (error.Fail())1026return error;10271028llvm::support::ulittle32_t memory_ranges_num =1029static_cast<llvm::support::ulittle32_t>(descriptors.size());1030m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle32_t));1031// For 32b we can get away with writing off the descriptors after the data.1032// This means no cleanup loop needed.1033m_data.AppendData(descriptors.data(),1034descriptors.size() * sizeof(MemoryDescriptor));10351036return error;1037}10381039Status1040MinidumpFileBuilder::AddMemoryList_64(Process::CoreFileMemoryRanges &ranges) {1041Status error;1042if (ranges.empty())1043return error;10441045error = AddDirectory(StreamType::Memory64List,1046(sizeof(llvm::support::ulittle64_t) * 2) +1047ranges.size() *1048sizeof(llvm::minidump::MemoryDescriptor_64));1049if (error.Fail())1050return error;10511052llvm::support::ulittle64_t memory_ranges_num =1053static_cast<llvm::support::ulittle64_t>(ranges.size());1054m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle64_t));1055// Capture the starting offset for all the descriptors so we can clean them up1056// if needed.1057offset_t starting_offset =1058GetCurrentDataEndOffset() + sizeof(llvm::support::ulittle64_t);1059// The base_rva needs to start after the directories, which is right after1060// this 8 byte variable.1061offset_t base_rva =1062starting_offset +1063(ranges.size() * sizeof(llvm::minidump::MemoryDescriptor_64));1064llvm::support::ulittle64_t memory_ranges_base_rva =1065static_cast<llvm::support::ulittle64_t>(base_rva);1066m_data.AppendData(&memory_ranges_base_rva,1067sizeof(llvm::support::ulittle64_t));10681069bool cleanup_required = false;1070std::vector<MemoryDescriptor_64> descriptors;1071// Enumerate the ranges and create the memory descriptors so we can append1072// them first1073for (const auto core_range : ranges) {1074// Add the space required to store the memory descriptor1075MemoryDescriptor_64 memory_desc;1076memory_desc.StartOfMemoryRange =1077static_cast<llvm::support::ulittle64_t>(core_range.range.start());1078memory_desc.DataSize =1079static_cast<llvm::support::ulittle64_t>(core_range.range.size());1080descriptors.push_back(memory_desc);1081// Now write this memory descriptor to the buffer.1082m_data.AppendData(&memory_desc, sizeof(MemoryDescriptor_64));1083}10841085Log *log = GetLog(LLDBLog::Object);1086size_t region_index = 0;1087auto data_up =1088std::make_unique<DataBufferHeap>(GetLargestRangeSize(ranges), 0);1089for (const auto &core_range : ranges) {1090const addr_t addr = core_range.range.start();1091const addr_t size = core_range.range.size();10921093LLDB_LOGF(log,1094"AddMemoryList_64 %zu/%zu reading memory for region "1095"(%" PRIx64 "bytes) "1096"[%" PRIx64 ", %" PRIx64 ")",1097region_index, ranges.size(), size, addr, addr + size);1098++region_index;10991100const size_t bytes_read =1101m_process_sp->ReadMemory(addr, data_up->GetBytes(), size, error);1102if (error.Fail()) {1103LLDB_LOGF(log, "Failed to read memory region. Bytes read: %zu, error: %s",1104bytes_read, error.AsCString());1105error.Clear();1106cleanup_required = true;1107descriptors[region_index].DataSize = 0;1108}1109if (bytes_read != size) {1110LLDB_LOGF(1111log, "Memory region at: %" PRIx64 " failed to read %" PRIx64 " bytes",1112addr, size);1113cleanup_required = true;1114descriptors[region_index].DataSize = bytes_read;1115}11161117// Add the data to the buffer, flush as needed.1118error = AddData(data_up->GetBytes(), bytes_read);1119if (error.Fail())1120return error;1121}11221123// Early return if there is no cleanup needed.1124if (!cleanup_required) {1125return error;1126} else {1127// Flush to disk we can make the fixes in place.1128FlushBufferToDisk();1129// Fixup the descriptors that were not read correctly.1130m_core_file->SeekFromStart(starting_offset);1131size_t bytes_written = sizeof(MemoryDescriptor_64) * descriptors.size();1132error = m_core_file->Write(descriptors.data(), bytes_written);1133if (error.Fail() ||1134bytes_written != sizeof(MemoryDescriptor_64) * descriptors.size()) {1135error.SetErrorStringWithFormat(1136"unable to write the memory descriptors (written %zd/%zd)",1137bytes_written, sizeof(MemoryDescriptor_64) * descriptors.size());1138}11391140return error;1141}1142}11431144Status MinidumpFileBuilder::AddData(const void *data, uint64_t size) {1145// This should also get chunked, because worst case we copy over a big1146// object / memory range, say 5gb. In that case, we'd have to allocate 10gb1147// 5 gb for the buffer we're copying from, and then 5gb for the buffer we're1148// copying to. Which will be short lived and immedaitely go to disk, the goal1149// here is to limit the number of bytes we need to host in memory at any given1150// time.1151m_data.AppendData(data, size);1152if (m_data.GetByteSize() > MAX_WRITE_CHUNK_SIZE)1153return FlushBufferToDisk();11541155return Status();1156}11571158Status MinidumpFileBuilder::FlushBufferToDisk() {1159Status error;1160// Set the stream to it's end.1161m_core_file->SeekFromStart(m_saved_data_size);1162addr_t starting_size = m_data.GetByteSize();1163addr_t remaining_bytes = starting_size;1164offset_t offset = 0;11651166while (remaining_bytes > 0) {1167size_t bytes_written = remaining_bytes;1168// We don't care how many bytes we wrote unless we got an error1169// so just decrement the remaining bytes.1170error = m_core_file->Write(m_data.GetBytes() + offset, bytes_written);1171if (error.Fail()) {1172error.SetErrorStringWithFormat(1173"Wrote incorrect number of bytes to minidump file. (written %" PRIx641174"/%" PRIx64 ")",1175starting_size - remaining_bytes, starting_size);1176return error;1177}11781179offset += bytes_written;1180remaining_bytes -= bytes_written;1181}11821183m_saved_data_size += starting_size;1184m_data.Clear();1185return error;1186}11871188Status MinidumpFileBuilder::DumpFile() {1189Status error;1190// If anything is left unsaved, dump it.1191error = FlushBufferToDisk();1192if (error.Fail())1193return error;11941195// Overwrite the header which we filled in earlier.1196error = DumpHeader();1197if (error.Fail())1198return error;11991200// Overwrite the space saved for directories1201error = DumpDirectories();1202if (error.Fail())1203return error;12041205return error;1206}120712081209