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