diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2021-11-19 20:06:13 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2021-11-19 20:06:13 +0000 |
| commit | c0981da47d5696fe36474fcf86b4ce03ae3ff818 (patch) | |
| tree | f42add1021b9f2ac6a69ac7cf6c4499962739a45 /lldb/source/Plugins/ObjectFile | |
| parent | 344a3780b2e33f6ca763666c380202b18aab72a3 (diff) | |
Diffstat (limited to 'lldb/source/Plugins/ObjectFile')
16 files changed, 1226 insertions, 103 deletions
diff --git a/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp b/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp index bd8eeedce57d..24941be515de 100644 --- a/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp +++ b/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp @@ -23,6 +23,8 @@ enum class Token { CodeID, File, Func, + Inline, + InlineOrigin, Public, Stack, CFI, @@ -41,6 +43,8 @@ template <> Token stringTo<Token>(llvm::StringRef Str) { .Case("CODE_ID", Token::CodeID) .Case("FILE", Token::File) .Case("FUNC", Token::Func) + .Case("INLINE", Token::Inline) + .Case("INLINE_ORIGIN", Token::InlineOrigin) .Case("PUBLIC", Token::Public) .Case("STACK", Token::Stack) .Case("CFI", Token::CFI) @@ -145,7 +149,10 @@ llvm::Optional<Record::Kind> Record::classify(llvm::StringRef Line) { default: return llvm::None; } - + case Token::Inline: + return Record::Inline; + case Token::InlineOrigin: + return Record::InlineOrigin; case Token::Unknown: // Optimistically assume that any unrecognised token means this is a line // record, those don't have a special keyword and start directly with a @@ -216,9 +223,11 @@ llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS, return OS << "INFO CODE_ID " << R.ID.GetAsString(); } -llvm::Optional<FileRecord> FileRecord::parse(llvm::StringRef Line) { - // FILE number name - if (consume<Token>(Line) != Token::File) +template <typename T> +static llvm::Optional<T> parseNumberName(llvm::StringRef Line, + Token TokenType) { + // TOKEN number name + if (consume<Token>(Line) != TokenType) return llvm::None; llvm::StringRef Str; @@ -231,7 +240,12 @@ llvm::Optional<FileRecord> FileRecord::parse(llvm::StringRef Line) { if (Name.empty()) return llvm::None; - return FileRecord(Number, Name); + return T(Number, Name); +} + +llvm::Optional<FileRecord> FileRecord::parse(llvm::StringRef Line) { + // FILE number name + return parseNumberName<FileRecord>(Line, Token::File); } llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS, @@ -239,6 +253,17 @@ llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS, return OS << "FILE " << R.Number << " " << R.Name; } +llvm::Optional<InlineOriginRecord> +InlineOriginRecord::parse(llvm::StringRef Line) { + // INLINE_ORIGIN number name + return parseNumberName<InlineOriginRecord>(Line, Token::InlineOrigin); +} + +llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS, + const InlineOriginRecord &R) { + return OS << "INLINE_ORIGIN " << R.Number << " " << R.Name; +} + static bool parsePublicOrFunc(llvm::StringRef Line, bool &Multiple, lldb::addr_t &Address, lldb::addr_t *Size, lldb::addr_t &ParamSize, llvm::StringRef &Name) { @@ -299,6 +324,58 @@ llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS, R.ParamSize, R.Name); } +llvm::Optional<InlineRecord> InlineRecord::parse(llvm::StringRef Line) { + // INLINE inline_nest_level call_site_line call_site_file_num origin_num + // [address size]+ + if (consume<Token>(Line) != Token::Inline) + return llvm::None; + + llvm::SmallVector<llvm::StringRef> Tokens; + SplitString(Line, Tokens, " "); + if (Tokens.size() < 6 || Tokens.size() % 2 == 1) + return llvm::None; + + size_t InlineNestLevel; + uint32_t CallSiteLineNum; + size_t CallSiteFileNum; + size_t OriginNum; + if (!(to_integer(Tokens[0], InlineNestLevel) && + to_integer(Tokens[1], CallSiteLineNum) && + to_integer(Tokens[2], CallSiteFileNum) && + to_integer(Tokens[3], OriginNum))) + return llvm::None; + + InlineRecord Record = InlineRecord(InlineNestLevel, CallSiteLineNum, + CallSiteFileNum, OriginNum); + for (size_t i = 4; i < Tokens.size(); i += 2) { + lldb::addr_t Address; + if (!to_integer(Tokens[i], Address, 16)) + return llvm::None; + lldb::addr_t Size; + if (!to_integer(Tokens[i + 1].trim(), Size, 16)) + return llvm::None; + Record.Ranges.emplace_back(Address, Size); + } + return Record; +} + +bool breakpad::operator==(const InlineRecord &L, const InlineRecord &R) { + return L.InlineNestLevel == R.InlineNestLevel && + L.CallSiteLineNum == R.CallSiteLineNum && + L.CallSiteFileNum == R.CallSiteFileNum && L.OriginNum == R.OriginNum && + L.Ranges == R.Ranges; +} + +llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS, + const InlineRecord &R) { + OS << llvm::formatv("INLINE {0} {1} {2} {3}", R.InlineNestLevel, + R.CallSiteLineNum, R.CallSiteFileNum, R.OriginNum); + for (const auto &range : R.Ranges) { + OS << llvm::formatv(" {0:x-} {1:x-}", range.first, range.second); + } + return OS; +} + llvm::Optional<LineRecord> LineRecord::parse(llvm::StringRef Line) { lldb::addr_t Address; llvm::StringRef Str; @@ -490,6 +567,10 @@ llvm::StringRef breakpad::toString(Record::Kind K) { return "FILE"; case Record::Func: return "FUNC"; + case Record::Inline: + return "INLINE"; + case Record::InlineOrigin: + return "INLINE_ORIGIN"; case Record::Line: return "LINE"; case Record::Public: diff --git a/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h b/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h index 1620a1210b84..8a11323d521c 100644 --- a/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h +++ b/lldb/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h @@ -20,7 +20,18 @@ namespace breakpad { class Record { public: - enum Kind { Module, Info, File, Func, Line, Public, StackCFI, StackWin }; + enum Kind { + Module, + Info, + File, + Func, + Inline, + InlineOrigin, + Line, + Public, + StackCFI, + StackWin + }; /// Attempt to guess the kind of the record present in the argument without /// doing a full parse. The returned kind will always be correct for valid @@ -89,6 +100,23 @@ inline bool operator==(const FileRecord &L, const FileRecord &R) { } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const FileRecord &R); +class InlineOriginRecord : public Record { +public: + static llvm::Optional<InlineOriginRecord> parse(llvm::StringRef Line); + InlineOriginRecord(size_t Number, llvm::StringRef Name) + : Record(InlineOrigin), Number(Number), Name(Name) {} + + size_t Number; + llvm::StringRef Name; +}; + +inline bool operator==(const InlineOriginRecord &L, + const InlineOriginRecord &R) { + return L.Number == R.Number && L.Name == R.Name; +} +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const InlineOriginRecord &R); + class FuncRecord : public Record { public: static llvm::Optional<FuncRecord> parse(llvm::StringRef Line); @@ -107,6 +135,26 @@ public: bool operator==(const FuncRecord &L, const FuncRecord &R); llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const FuncRecord &R); +class InlineRecord : public Record { +public: + static llvm::Optional<InlineRecord> parse(llvm::StringRef Line); + InlineRecord(size_t InlineNestLevel, uint32_t CallSiteLineNum, + size_t CallSiteFileNum, size_t OriginNum) + : Record(Inline), InlineNestLevel(InlineNestLevel), + CallSiteLineNum(CallSiteLineNum), CallSiteFileNum(CallSiteFileNum), + OriginNum(OriginNum) {} + + size_t InlineNestLevel; + uint32_t CallSiteLineNum; + size_t CallSiteFileNum; + size_t OriginNum; + // A vector of address range covered by this inline + std::vector<std::pair<lldb::addr_t, lldb::addr_t>> Ranges; +}; + +bool operator==(const InlineRecord &L, const InlineRecord &R); +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const InlineRecord &R); + class LineRecord : public Record { public: static llvm::Optional<LineRecord> parse(llvm::StringRef Line); diff --git a/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp b/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp index 7a9163ddb880..bad730512ff4 100644 --- a/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp +++ b/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp @@ -56,11 +56,6 @@ void ObjectFileBreakpad::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } -ConstString ObjectFileBreakpad::GetPluginNameStatic() { - static ConstString g_name("breakpad"); - return g_name; -} - ObjectFile *ObjectFileBreakpad::CreateInstance( const ModuleSP &module_sp, DataBufferSP &data_sp, offset_t data_offset, const FileSpec *file, offset_t file_offset, offset_t length) { @@ -153,9 +148,9 @@ void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) { std::tie(line, text) = text.split('\n'); llvm::Optional<Record::Kind> next_section = Record::classify(line); - if (next_section == Record::Line) { - // Line records logically belong to the preceding Func record, so we put - // them in the same section. + if (next_section == Record::Line || next_section == Record::Inline) { + // Line/Inline records logically belong to the preceding Func record, so + // we put them in the same section. next_section = Record::Func; } if (next_section == current_section) diff --git a/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h b/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h index 8724feaa422d..c320c7ad3e2e 100644 --- a/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h +++ b/lldb/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h @@ -21,7 +21,7 @@ public: static void Initialize(); static void Terminate(); - static ConstString GetPluginNameStatic(); + static llvm::StringRef GetPluginNameStatic() { return "breakpad"; } static const char *GetPluginDescriptionStatic() { return "Breakpad object file reader."; } @@ -44,9 +44,7 @@ public: ModuleSpecList &specs); // PluginInterface protocol - ConstString GetPluginName() override { return GetPluginNameStatic(); } - - uint32_t GetPluginVersion() override { return 1; } + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } // LLVM RTTI support static char ID; diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index a5e86f0c2c1b..8e0f228a988f 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -55,36 +55,36 @@ using namespace llvm::ELF; LLDB_PLUGIN_DEFINE(ObjectFileELF) -namespace { - // ELF note owner definitions -const char *const LLDB_NT_OWNER_FREEBSD = "FreeBSD"; -const char *const LLDB_NT_OWNER_GNU = "GNU"; -const char *const LLDB_NT_OWNER_NETBSD = "NetBSD"; -const char *const LLDB_NT_OWNER_NETBSDCORE = "NetBSD-CORE"; -const char *const LLDB_NT_OWNER_OPENBSD = "OpenBSD"; -const char *const LLDB_NT_OWNER_ANDROID = "Android"; -const char *const LLDB_NT_OWNER_CORE = "CORE"; -const char *const LLDB_NT_OWNER_LINUX = "LINUX"; +static const char *const LLDB_NT_OWNER_FREEBSD = "FreeBSD"; +static const char *const LLDB_NT_OWNER_GNU = "GNU"; +static const char *const LLDB_NT_OWNER_NETBSD = "NetBSD"; +static const char *const LLDB_NT_OWNER_NETBSDCORE = "NetBSD-CORE"; +static const char *const LLDB_NT_OWNER_OPENBSD = "OpenBSD"; +static const char *const LLDB_NT_OWNER_ANDROID = "Android"; +static const char *const LLDB_NT_OWNER_CORE = "CORE"; +static const char *const LLDB_NT_OWNER_LINUX = "LINUX"; // ELF note type definitions -const elf_word LLDB_NT_FREEBSD_ABI_TAG = 0x01; -const elf_word LLDB_NT_FREEBSD_ABI_SIZE = 4; +static const elf_word LLDB_NT_FREEBSD_ABI_TAG = 0x01; +static const elf_word LLDB_NT_FREEBSD_ABI_SIZE = 4; -const elf_word LLDB_NT_GNU_ABI_TAG = 0x01; -const elf_word LLDB_NT_GNU_ABI_SIZE = 16; +static const elf_word LLDB_NT_GNU_ABI_TAG = 0x01; +static const elf_word LLDB_NT_GNU_ABI_SIZE = 16; -const elf_word LLDB_NT_GNU_BUILD_ID_TAG = 0x03; +static const elf_word LLDB_NT_GNU_BUILD_ID_TAG = 0x03; -const elf_word LLDB_NT_NETBSD_IDENT_TAG = 1; -const elf_word LLDB_NT_NETBSD_IDENT_DESCSZ = 4; -const elf_word LLDB_NT_NETBSD_IDENT_NAMESZ = 7; -const elf_word LLDB_NT_NETBSD_PROCINFO = 1; +static const elf_word LLDB_NT_NETBSD_IDENT_TAG = 1; +static const elf_word LLDB_NT_NETBSD_IDENT_DESCSZ = 4; +static const elf_word LLDB_NT_NETBSD_IDENT_NAMESZ = 7; +static const elf_word LLDB_NT_NETBSD_PROCINFO = 1; // GNU ABI note OS constants -const elf_word LLDB_NT_GNU_ABI_OS_LINUX = 0x00; -const elf_word LLDB_NT_GNU_ABI_OS_HURD = 0x01; -const elf_word LLDB_NT_GNU_ABI_OS_SOLARIS = 0x02; +static const elf_word LLDB_NT_GNU_ABI_OS_LINUX = 0x00; +static const elf_word LLDB_NT_GNU_ABI_OS_HURD = 0x01; +static const elf_word LLDB_NT_GNU_ABI_OS_SOLARIS = 0x02; + +namespace { //===----------------------------------------------------------------------===// /// \class ELFRelocation @@ -125,6 +125,7 @@ private: RelocUnion reloc; }; +} // end anonymous namespace ELFRelocation::ELFRelocation(unsigned type) { if (type == DT_REL || type == SHT_REL) @@ -208,8 +209,6 @@ unsigned ELFRelocation::RelocAddend64(const ELFRelocation &rel) { return rel.reloc.get<ELFRela *>()->r_addend; } -} // end anonymous namespace - static user_id_t SegmentID(size_t PHdrIndex) { return ~user_id_t(PHdrIndex); } @@ -335,15 +334,6 @@ void ObjectFileELF::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } -lldb_private::ConstString ObjectFileELF::GetPluginNameStatic() { - static ConstString g_name("elf"); - return g_name; -} - -const char *ObjectFileELF::GetPluginDescriptionStatic() { - return "ELF object file reader."; -} - ObjectFile *ObjectFileELF::CreateInstance(const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, lldb::offset_t data_offset, @@ -634,12 +624,6 @@ size_t ObjectFileELF::GetModuleSpecifications( return specs.GetSize() - initial_count; } -// PluginInterface protocol -lldb_private::ConstString ObjectFileELF::GetPluginName() { - return GetPluginNameStatic(); -} - -uint32_t ObjectFileELF::GetPluginVersion() { return m_plugin_version; } // ObjectFile protocol ObjectFileELF::ObjectFileELF(const lldb::ModuleSP &module_sp, @@ -2708,9 +2692,6 @@ Symtab *ObjectFileELF::GetSymtab() { if (!module_sp) return nullptr; - Progress progress(llvm::formatv("Parsing symbol table for {0}", - m_file.GetFilename().AsCString("<Unknown>"))); - // We always want to use the main object file so we (hopefully) only have one // cached copy of our symtab, dynamic sections, etc. ObjectFile *module_obj_file = module_sp->GetObjectFile(); @@ -2718,6 +2699,10 @@ Symtab *ObjectFileELF::GetSymtab() { return module_obj_file->GetSymtab(); if (m_symtab_up == nullptr) { + Progress progress( + llvm::formatv("Parsing symbol table for {0}", + m_file.GetFilename().AsCString("<Unknown>"))); + ElapsedTime elapsed(module_sp->GetSymtabParseTime()); SectionList *section_list = module_sp->GetSectionList(); if (!section_list) return nullptr; diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h index e678c2f5f011..5738e5cf60d5 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -61,9 +61,11 @@ public: static void Terminate(); - static lldb_private::ConstString GetPluginNameStatic(); + static llvm::StringRef GetPluginNameStatic() { return "elf"; } - static const char *GetPluginDescriptionStatic(); + static llvm::StringRef GetPluginDescriptionStatic() { + return "ELF object file reader."; + } static lldb_private::ObjectFile * CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, @@ -85,9 +87,7 @@ public: lldb::addr_t length); // PluginInterface protocol - lldb_private::ConstString GetPluginName() override; - - uint32_t GetPluginVersion() override; + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } // LLVM RTTI support static char ID; diff --git a/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp b/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp index f93ac9261afd..bec0099517c8 100644 --- a/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp +++ b/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.cpp @@ -53,15 +53,6 @@ void ObjectFileJIT::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } -lldb_private::ConstString ObjectFileJIT::GetPluginNameStatic() { - static ConstString g_name("jit"); - return g_name; -} - -const char *ObjectFileJIT::GetPluginDescriptionStatic() { - return "JIT code object file"; -} - ObjectFile *ObjectFileJIT::CreateInstance(const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, lldb::offset_t data_offset, @@ -120,6 +111,7 @@ Symtab *ObjectFileJIT::GetSymtab() { if (module_sp) { std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex()); if (m_symtab_up == nullptr) { + ElapsedTime elapsed(module_sp->GetSymtabParseTime()); m_symtab_up = std::make_unique<Symtab>(this); std::lock_guard<std::recursive_mutex> symtab_guard( m_symtab_up->GetMutex()); @@ -199,13 +191,6 @@ ArchSpec ObjectFileJIT::GetArchitecture() { return ArchSpec(); } -// PluginInterface protocol -lldb_private::ConstString ObjectFileJIT::GetPluginName() { - return GetPluginNameStatic(); -} - -uint32_t ObjectFileJIT::GetPluginVersion() { return 1; } - bool ObjectFileJIT::SetLoadAddress(Target &target, lldb::addr_t value, bool value_is_offset) { size_t num_loaded_sections = 0; diff --git a/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h b/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h index a3a1acea916a..03ac001988a0 100644 --- a/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h +++ b/lldb/source/Plugins/ObjectFile/JIT/ObjectFileJIT.h @@ -26,9 +26,11 @@ public: static void Terminate(); - static lldb_private::ConstString GetPluginNameStatic(); + static llvm::StringRef GetPluginNameStatic() { return "jit"; } - static const char *GetPluginDescriptionStatic(); + static llvm::StringRef GetPluginDescriptionStatic() { + return "JIT code object file"; + } static lldb_private::ObjectFile * CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, @@ -96,9 +98,7 @@ public: ObjectFile::Strata CalculateStrata() override; // PluginInterface protocol - lldb_private::ConstString GetPluginName() override; - - uint32_t GetPluginVersion() override; + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } protected: lldb::ObjectFileJITDelegateWP m_delegate_wp; diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp new file mode 100644 index 000000000000..a70e6a079f76 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.cpp @@ -0,0 +1,772 @@ +//===-- MinidumpFileBuilder.cpp -------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MinidumpFileBuilder.h" + +#include "Plugins/Process/minidump/RegisterContextMinidump_x86_64.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/MemoryRegionInfo.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/ThreadList.h" +#include "lldb/Utility/DataExtractor.h" +#include "lldb/Utility/RegisterValue.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/Minidump.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Error.h" + +#include "Plugins/Process/minidump/MinidumpTypes.h" + +#include <cinttypes> + +using namespace lldb; +using namespace lldb_private; +using namespace llvm::minidump; + +void MinidumpFileBuilder::AddDirectory(StreamType type, size_t stream_size) { + LocationDescriptor loc; + loc.DataSize = static_cast<llvm::support::ulittle32_t>(stream_size); + // Stream will begin at the current end of data section + loc.RVA = static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset()); + + Directory dir; + dir.Type = static_cast<llvm::support::little_t<StreamType>>(type); + dir.Location = loc; + + m_directories.push_back(dir); +} + +Status MinidumpFileBuilder::AddSystemInfo(const llvm::Triple &target_triple) { + Status error; + AddDirectory(StreamType::SystemInfo, sizeof(llvm::minidump::SystemInfo)); + + llvm::minidump::ProcessorArchitecture arch; + switch (target_triple.getArch()) { + case llvm::Triple::ArchType::x86_64: + arch = ProcessorArchitecture::AMD64; + break; + case llvm::Triple::ArchType::x86: + arch = ProcessorArchitecture::X86; + break; + case llvm::Triple::ArchType::arm: + arch = ProcessorArchitecture::ARM; + break; + case llvm::Triple::ArchType::aarch64: + arch = ProcessorArchitecture::ARM64; + break; + case llvm::Triple::ArchType::mips64: + case llvm::Triple::ArchType::mips64el: + case llvm::Triple::ArchType::mips: + case llvm::Triple::ArchType::mipsel: + arch = ProcessorArchitecture::MIPS; + break; + case llvm::Triple::ArchType::ppc64: + case llvm::Triple::ArchType::ppc: + case llvm::Triple::ArchType::ppc64le: + arch = ProcessorArchitecture::PPC; + break; + default: + error.SetErrorStringWithFormat("Architecture %s not supported.", + target_triple.getArchName().str().c_str()); + return error; + }; + + llvm::support::little_t<OSPlatform> platform_id; + switch (target_triple.getOS()) { + case llvm::Triple::OSType::Linux: + if (target_triple.getEnvironment() == + llvm::Triple::EnvironmentType::Android) + platform_id = OSPlatform::Android; + else + platform_id = OSPlatform::Linux; + break; + case llvm::Triple::OSType::Win32: + platform_id = OSPlatform::Win32NT; + break; + case llvm::Triple::OSType::MacOSX: + platform_id = OSPlatform::MacOSX; + break; + case llvm::Triple::OSType::IOS: + platform_id = OSPlatform::IOS; + break; + default: + error.SetErrorStringWithFormat("OS %s not supported.", + target_triple.getOSName().str().c_str()); + return error; + }; + + llvm::minidump::SystemInfo sys_info; + sys_info.ProcessorArch = + static_cast<llvm::support::little_t<ProcessorArchitecture>>(arch); + // Global offset to beginning of a csd_string in a data section + sys_info.CSDVersionRVA = static_cast<llvm::support::ulittle32_t>( + GetCurrentDataEndOffset() + sizeof(llvm::minidump::SystemInfo)); + sys_info.PlatformId = platform_id; + m_data.AppendData(&sys_info, sizeof(llvm::minidump::SystemInfo)); + + std::string csd_string = ""; + + error = WriteString(csd_string, &m_data); + if (error.Fail()) { + error.SetErrorString("Unable to convert the csd string to UTF16."); + return error; + } + + return error; +} + +Status WriteString(const std::string &to_write, + lldb_private::DataBufferHeap *buffer) { + Status error; + // let the StringRef eat also null termination char + llvm::StringRef to_write_ref(to_write.c_str(), to_write.size() + 1); + llvm::SmallVector<llvm::UTF16, 128> to_write_utf16; + + bool converted = convertUTF8ToUTF16String(to_write_ref, to_write_utf16); + if (!converted) { + error.SetErrorStringWithFormat( + "Unable to convert the string to UTF16. Failed to convert %s", + to_write.c_str()); + return error; + } + + // size of the UTF16 string should be written without the null termination + // character that is stored in 2 bytes + llvm::support::ulittle32_t to_write_size(to_write_utf16.size_in_bytes() - 2); + + buffer->AppendData(&to_write_size, sizeof(llvm::support::ulittle32_t)); + buffer->AppendData(to_write_utf16.data(), to_write_utf16.size_in_bytes()); + + return error; +} + +llvm::Expected<uint64_t> getModuleFileSize(Target &target, + const ModuleSP &mod) { + SectionSP sect_sp = mod->GetObjectFile()->GetBaseAddress().GetSection(); + uint64_t SizeOfImage = 0; + + if (!sect_sp) { + return llvm::createStringError(std::errc::operation_not_supported, + "Couldn't obtain the section information."); + } + lldb::addr_t sect_addr = sect_sp->GetLoadBaseAddress(&target); + // Use memory size since zero fill sections, like ".bss", will be smaller on + // disk. + lldb::addr_t sect_size = sect_sp->GetByteSize(); + // This will usually be zero, but make sure to calculate the BaseOfImage + // offset. + const lldb::addr_t base_sect_offset = + mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target) - + sect_addr; + SizeOfImage = sect_size - base_sect_offset; + lldb::addr_t next_sect_addr = sect_addr + sect_size; + Address sect_so_addr; + target.ResolveLoadAddress(next_sect_addr, sect_so_addr); + lldb::SectionSP next_sect_sp = sect_so_addr.GetSection(); + while (next_sect_sp && + next_sect_sp->GetLoadBaseAddress(&target) == next_sect_addr) { + sect_size = sect_sp->GetByteSize(); + SizeOfImage += sect_size; + next_sect_addr += sect_size; + target.ResolveLoadAddress(next_sect_addr, sect_so_addr); + next_sect_sp = sect_so_addr.GetSection(); + } + + return SizeOfImage; +} + +// ModuleList stream consists of a number of modules, followed by an array +// of llvm::minidump::Module's structures. Every structure informs about a +// single module. Additional data of variable length, such as module's names, +// are stored just after the ModuleList stream. The llvm::minidump::Module +// structures point to this helper data by global offset. +Status MinidumpFileBuilder::AddModuleList(Target &target) { + constexpr size_t minidump_module_size = sizeof(llvm::minidump::Module); + Status error; + + const ModuleList &modules = target.GetImages(); + llvm::support::ulittle32_t modules_count = + static_cast<llvm::support::ulittle32_t>(modules.GetSize()); + + // This helps us with getting the correct global offset in minidump + // file later, when we will be setting up offsets from the + // the llvm::minidump::Module's structures into helper data + size_t size_before = GetCurrentDataEndOffset(); + + // This is the size of the main part of the ModuleList stream. + // It consists of a module number and corresponding number of + // structs describing individual modules + size_t module_stream_size = + sizeof(llvm::support::ulittle32_t) + modules_count * minidump_module_size; + + // Adding directory describing this stream. + AddDirectory(StreamType::ModuleList, module_stream_size); + + m_data.AppendData(&modules_count, sizeof(llvm::support::ulittle32_t)); + + // Temporary storage for the helper data (of variable length) + // as these cannot be dumped to m_data before dumping entire + // array of module structures. + DataBufferHeap helper_data; + + for (size_t i = 0; i < modules_count; ++i) { + ModuleSP mod = modules.GetModuleAtIndex(i); + std::string module_name = mod->GetSpecificationDescription(); + auto maybe_mod_size = getModuleFileSize(target, mod); + if (!maybe_mod_size) { + error.SetErrorStringWithFormat("Unable to get the size of module %s.", + module_name.c_str()); + return error; + } + + uint64_t mod_size = std::move(*maybe_mod_size); + + llvm::support::ulittle32_t signature = + static_cast<llvm::support::ulittle32_t>( + static_cast<uint32_t>(minidump::CvSignature::ElfBuildId)); + auto uuid = mod->GetUUID().GetBytes(); + + VSFixedFileInfo info; + info.Signature = static_cast<llvm::support::ulittle32_t>(0u); + info.StructVersion = static_cast<llvm::support::ulittle32_t>(0u); + info.FileVersionHigh = static_cast<llvm::support::ulittle32_t>(0u); + info.FileVersionLow = static_cast<llvm::support::ulittle32_t>(0u); + info.ProductVersionHigh = static_cast<llvm::support::ulittle32_t>(0u); + info.ProductVersionLow = static_cast<llvm::support::ulittle32_t>(0u); + info.FileFlagsMask = static_cast<llvm::support::ulittle32_t>(0u); + info.FileFlags = static_cast<llvm::support::ulittle32_t>(0u); + info.FileOS = static_cast<llvm::support::ulittle32_t>(0u); + info.FileType = static_cast<llvm::support::ulittle32_t>(0u); + info.FileSubtype = static_cast<llvm::support::ulittle32_t>(0u); + info.FileDateHigh = static_cast<llvm::support::ulittle32_t>(0u); + info.FileDateLow = static_cast<llvm::support::ulittle32_t>(0u); + + LocationDescriptor ld; + ld.DataSize = static_cast<llvm::support::ulittle32_t>(0u); + ld.RVA = static_cast<llvm::support::ulittle32_t>(0u); + + // Setting up LocationDescriptor for uuid string. The global offset into + // minidump file is calculated. + LocationDescriptor ld_cv; + ld_cv.DataSize = static_cast<llvm::support::ulittle32_t>( + sizeof(llvm::support::ulittle32_t) + uuid.size()); + ld_cv.RVA = static_cast<llvm::support::ulittle32_t>( + size_before + module_stream_size + helper_data.GetByteSize()); + + helper_data.AppendData(&signature, sizeof(llvm::support::ulittle32_t)); + helper_data.AppendData(uuid.begin(), uuid.size()); + + llvm::minidump::Module m; + m.BaseOfImage = static_cast<llvm::support::ulittle64_t>( + mod->GetObjectFile()->GetBaseAddress().GetLoadAddress(&target)); + m.SizeOfImage = static_cast<llvm::support::ulittle32_t>(mod_size); + m.Checksum = static_cast<llvm::support::ulittle32_t>(0); + m.TimeDateStamp = static_cast<llvm::support::ulittle32_t>(std::time(0)); + m.ModuleNameRVA = static_cast<llvm::support::ulittle32_t>( + size_before + module_stream_size + helper_data.GetByteSize()); + m.VersionInfo = info; + m.CvRecord = ld_cv; + m.MiscRecord = ld; + + error = WriteString(module_name, &helper_data); + + if (error.Fail()) + return error; + + m_data.AppendData(&m, sizeof(llvm::minidump::Module)); + } + + m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize()); + return error; +} + +uint16_t read_register_u16_raw(RegisterContext *reg_ctx, + const std::string ®_name) { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); + if (!reg_info) + return 0; + lldb_private::RegisterValue reg_value; + bool success = reg_ctx->ReadRegister(reg_info, reg_value); + if (!success) + return 0; + return reg_value.GetAsUInt16(); +} + +uint32_t read_register_u32_raw(RegisterContext *reg_ctx, + const std::string ®_name) { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); + if (!reg_info) + return 0; + lldb_private::RegisterValue reg_value; + bool success = reg_ctx->ReadRegister(reg_info, reg_value); + if (!success) + return 0; + return reg_value.GetAsUInt32(); +} + +uint64_t read_register_u64_raw(RegisterContext *reg_ctx, + const std::string ®_name) { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); + if (!reg_info) + return 0; + lldb_private::RegisterValue reg_value; + bool success = reg_ctx->ReadRegister(reg_info, reg_value); + if (!success) + return 0; + return reg_value.GetAsUInt64(); +} + +llvm::support::ulittle16_t read_register_u16(RegisterContext *reg_ctx, + const std::string ®_name) { + return static_cast<llvm::support::ulittle16_t>( + read_register_u16_raw(reg_ctx, reg_name)); +} + +llvm::support::ulittle32_t read_register_u32(RegisterContext *reg_ctx, + const std::string ®_name) { + return static_cast<llvm::support::ulittle32_t>( + read_register_u32_raw(reg_ctx, reg_name)); +} + +llvm::support::ulittle64_t read_register_u64(RegisterContext *reg_ctx, + const std::string ®_name) { + return static_cast<llvm::support::ulittle64_t>( + read_register_u64_raw(reg_ctx, reg_name)); +} + +lldb_private::minidump::MinidumpContext_x86_64 +GetThreadContext_64(RegisterContext *reg_ctx) { + lldb_private::minidump::MinidumpContext_x86_64 thread_context; + thread_context.context_flags = static_cast<uint32_t>( + lldb_private::minidump::MinidumpContext_x86_64_Flags::x86_64_Flag | + lldb_private::minidump::MinidumpContext_x86_64_Flags::Control | + lldb_private::minidump::MinidumpContext_x86_64_Flags::Segments | + lldb_private::minidump::MinidumpContext_x86_64_Flags::Integer); + thread_context.rax = read_register_u64(reg_ctx, "rax"); + thread_context.rbx = read_register_u64(reg_ctx, "rbx"); + thread_context.rcx = read_register_u64(reg_ctx, "rcx"); + thread_context.rdx = read_register_u64(reg_ctx, "rdx"); + thread_context.rdi = read_register_u64(reg_ctx, "rdi"); + thread_context.rsi = read_register_u64(reg_ctx, "rsi"); + thread_context.rbp = read_register_u64(reg_ctx, "rbp"); + thread_context.rsp = read_register_u64(reg_ctx, "rsp"); + thread_context.r8 = read_register_u64(reg_ctx, "r8"); + thread_context.r9 = read_register_u64(reg_ctx, "r9"); + thread_context.r10 = read_register_u64(reg_ctx, "r10"); + thread_context.r11 = read_register_u64(reg_ctx, "r11"); + thread_context.r12 = read_register_u64(reg_ctx, "r12"); + thread_context.r13 = read_register_u64(reg_ctx, "r13"); + thread_context.r14 = read_register_u64(reg_ctx, "r14"); + thread_context.r15 = read_register_u64(reg_ctx, "r15"); + thread_context.rip = read_register_u64(reg_ctx, "rip"); + thread_context.eflags = read_register_u32(reg_ctx, "rflags"); + thread_context.cs = read_register_u16(reg_ctx, "cs"); + thread_context.fs = read_register_u16(reg_ctx, "fs"); + thread_context.gs = read_register_u16(reg_ctx, "gs"); + thread_context.ss = read_register_u16(reg_ctx, "ss"); + thread_context.ds = read_register_u16(reg_ctx, "ds"); + return thread_context; +} + +// Function returns start and size of the memory region that contains +// memory location pointed to by the current stack pointer. +llvm::Expected<std::pair<addr_t, addr_t>> +findStackHelper(const lldb::ProcessSP &process_sp, uint64_t rsp) { + MemoryRegionInfo range_info; + Status error = process_sp->GetMemoryRegionInfo(rsp, range_info); + // Skip failed memory region requests or any regions with no permissions. + if (error.Fail() || range_info.GetLLDBPermissions() == 0) + return llvm::createStringError( + std::errc::not_supported, + "unable to load stack segment of the process"); + + const addr_t addr = range_info.GetRange().GetRangeBase(); + const addr_t size = range_info.GetRange().GetByteSize(); + + if (size == 0) + return llvm::createStringError(std::errc::not_supported, + "stack segment of the process is empty"); + + return std::make_pair(addr, size); +} + +Status MinidumpFileBuilder::AddThreadList(const lldb::ProcessSP &process_sp) { + constexpr size_t minidump_thread_size = sizeof(llvm::minidump::Thread); + lldb_private::ThreadList thread_list = process_sp->GetThreadList(); + + // size of the entire thread stream consists of: + // number of threads and threads array + size_t thread_stream_size = sizeof(llvm::support::ulittle32_t) + + thread_list.GetSize() * minidump_thread_size; + // save for the ability to set up RVA + size_t size_before = GetCurrentDataEndOffset(); + + AddDirectory(StreamType::ThreadList, thread_stream_size); + + llvm::support::ulittle32_t thread_count = + static_cast<llvm::support::ulittle32_t>(thread_list.GetSize()); + m_data.AppendData(&thread_count, sizeof(llvm::support::ulittle32_t)); + + DataBufferHeap helper_data; + + const uint32_t num_threads = thread_list.GetSize(); + + for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) { + ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx)); + RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); + Status error; + + if (!reg_ctx_sp) { + error.SetErrorString("Unable to get the register context."); + return error; + } + RegisterContext *reg_ctx = reg_ctx_sp.get(); + auto thread_context = GetThreadContext_64(reg_ctx); + uint64_t rsp = read_register_u64_raw(reg_ctx, "rsp"); + auto expected_address_range = findStackHelper(process_sp, rsp); + + if (!expected_address_range) { + error.SetErrorString("Unable to get the stack address."); + return error; + } + + std::pair<uint64_t, uint64_t> range = std::move(*expected_address_range); + uint64_t addr = range.first; + uint64_t size = range.second; + + auto data_up = std::make_unique<DataBufferHeap>(size, 0); + const size_t stack_bytes_read = + process_sp->ReadMemory(addr, data_up->GetBytes(), size, error); + + if (error.Fail()) + return error; + + LocationDescriptor stack_memory; + stack_memory.DataSize = + static_cast<llvm::support::ulittle32_t>(stack_bytes_read); + stack_memory.RVA = static_cast<llvm::support::ulittle32_t>( + size_before + thread_stream_size + helper_data.GetByteSize()); + + MemoryDescriptor stack; + stack.StartOfMemoryRange = static_cast<llvm::support::ulittle64_t>(addr); + stack.Memory = stack_memory; + + helper_data.AppendData(data_up->GetBytes(), stack_bytes_read); + + LocationDescriptor thread_context_memory_locator; + thread_context_memory_locator.DataSize = + static_cast<llvm::support::ulittle32_t>(sizeof(thread_context)); + thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>( + size_before + thread_stream_size + helper_data.GetByteSize()); + + helper_data.AppendData( + &thread_context, + sizeof(lldb_private::minidump::MinidumpContext_x86_64)); + + llvm::minidump::Thread t; + t.ThreadId = static_cast<llvm::support::ulittle32_t>(thread_sp->GetID()); + t.SuspendCount = static_cast<llvm::support::ulittle32_t>( + (thread_sp->GetState() == StateType::eStateSuspended) ? 1 : 0); + t.PriorityClass = static_cast<llvm::support::ulittle32_t>(0); + t.Priority = static_cast<llvm::support::ulittle32_t>(0); + t.EnvironmentBlock = static_cast<llvm::support::ulittle64_t>(0); + t.Stack = stack, t.Context = thread_context_memory_locator; + + m_data.AppendData(&t, sizeof(llvm::minidump::Thread)); + } + + m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize()); + return Status(); +} + +Status MinidumpFileBuilder::AddException(const lldb::ProcessSP &process_sp) { + Status error; + lldb_private::ThreadList thread_list = process_sp->GetThreadList(); + + const uint32_t num_threads = thread_list.GetSize(); + uint32_t stop_reason_thread_idx = 0; + for (stop_reason_thread_idx = 0; stop_reason_thread_idx < num_threads; + ++stop_reason_thread_idx) { + ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx)); + StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); + + if (stop_info_sp && stop_info_sp->IsValid()) + break; + } + + if (stop_reason_thread_idx == num_threads) { + error.SetErrorString("No stop reason thread found."); + return error; + } + + constexpr size_t minidump_exception_size = + sizeof(llvm::minidump::ExceptionStream); + AddDirectory(StreamType::Exception, minidump_exception_size); + size_t size_before = GetCurrentDataEndOffset(); + + ThreadSP thread_sp(thread_list.GetThreadAtIndex(stop_reason_thread_idx)); + RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); + RegisterContext *reg_ctx = reg_ctx_sp.get(); + auto thread_context = GetThreadContext_64(reg_ctx); + StopInfoSP stop_info_sp = thread_sp->GetStopInfo(); + + DataBufferHeap helper_data; + + LocationDescriptor thread_context_memory_locator; + thread_context_memory_locator.DataSize = + static_cast<llvm::support::ulittle32_t>(sizeof(thread_context)); + thread_context_memory_locator.RVA = static_cast<llvm::support::ulittle32_t>( + size_before + minidump_exception_size + helper_data.GetByteSize()); + + helper_data.AppendData( + &thread_context, sizeof(lldb_private::minidump::MinidumpContext_x86_64)); + + Exception exp_record; + exp_record.ExceptionCode = + static_cast<llvm::support::ulittle32_t>(stop_info_sp->GetValue()); + exp_record.ExceptionFlags = static_cast<llvm::support::ulittle32_t>(0); + exp_record.ExceptionRecord = static_cast<llvm::support::ulittle64_t>(0); + exp_record.ExceptionAddress = read_register_u64(reg_ctx, "rip"); + exp_record.NumberParameters = static_cast<llvm::support::ulittle32_t>(0); + exp_record.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0); + // exp_record.ExceptionInformation; + + ExceptionStream exp_stream; + exp_stream.ThreadId = + static_cast<llvm::support::ulittle32_t>(thread_sp->GetID()); + exp_stream.UnusedAlignment = static_cast<llvm::support::ulittle32_t>(0); + exp_stream.ExceptionRecord = exp_record; + exp_stream.ThreadContext = thread_context_memory_locator; + + m_data.AppendData(&exp_stream, minidump_exception_size); + m_data.AppendData(helper_data.GetBytes(), helper_data.GetByteSize()); + return error; +} + +lldb_private::Status +MinidumpFileBuilder::AddMemoryList(const lldb::ProcessSP &process_sp) { + Status error; + + if (error.Fail()) { + error.SetErrorString("Process doesn't support getting memory region info."); + return error; + } + + // Get interesting addresses + std::vector<size_t> interesting_addresses; + auto thread_list = process_sp->GetThreadList(); + for (size_t i = 0; i < thread_list.GetSize(); ++i) { + ThreadSP thread_sp(thread_list.GetThreadAtIndex(i)); + RegisterContextSP reg_ctx_sp(thread_sp->GetRegisterContext()); + RegisterContext *reg_ctx = reg_ctx_sp.get(); + + interesting_addresses.push_back(read_register_u64(reg_ctx, "rsp")); + interesting_addresses.push_back(read_register_u64(reg_ctx, "rip")); + } + + DataBufferHeap helper_data; + std::vector<MemoryDescriptor> mem_descriptors; + + std::set<addr_t> visited_region_base_addresses; + for (size_t interesting_address : interesting_addresses) { + MemoryRegionInfo range_info; + error = process_sp->GetMemoryRegionInfo(interesting_address, range_info); + // Skip failed memory region requests or any regions with no permissions. + if (error.Fail() || range_info.GetLLDBPermissions() == 0) + continue; + const addr_t addr = range_info.GetRange().GetRangeBase(); + // Skip any regions we have already saved out. + if (visited_region_base_addresses.insert(addr).second == false) + continue; + const addr_t size = range_info.GetRange().GetByteSize(); + if (size == 0) + continue; + auto data_up = std::make_unique<DataBufferHeap>(size, 0); + const size_t bytes_read = + process_sp->ReadMemory(addr, data_up->GetBytes(), size, error); + if (bytes_read == 0) + continue; + // We have a good memory region with valid bytes to store. + LocationDescriptor memory_dump; + memory_dump.DataSize = static_cast<llvm::support::ulittle32_t>(bytes_read); + memory_dump.RVA = + static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset()); + MemoryDescriptor memory_desc; + memory_desc.StartOfMemoryRange = + static_cast<llvm::support::ulittle64_t>(addr); + memory_desc.Memory = memory_dump; + mem_descriptors.push_back(memory_desc); + m_data.AppendData(data_up->GetBytes(), bytes_read); + } + + AddDirectory(StreamType::MemoryList, + sizeof(llvm::support::ulittle32_t) + + mem_descriptors.size() * + sizeof(llvm::minidump::MemoryDescriptor)); + llvm::support::ulittle32_t memory_ranges_num(mem_descriptors.size()); + + m_data.AppendData(&memory_ranges_num, sizeof(llvm::support::ulittle32_t)); + for (auto memory_descriptor : mem_descriptors) { + m_data.AppendData(&memory_descriptor, + sizeof(llvm::minidump::MemoryDescriptor)); + } + + return error; +} + +void MinidumpFileBuilder::AddMiscInfo(const lldb::ProcessSP &process_sp) { + AddDirectory(StreamType::MiscInfo, + sizeof(lldb_private::minidump::MinidumpMiscInfo)); + + lldb_private::minidump::MinidumpMiscInfo misc_info; + misc_info.size = static_cast<llvm::support::ulittle32_t>( + sizeof(lldb_private::minidump::MinidumpMiscInfo)); + // Default set flags1 to 0, in case that we will not be able to + // get any information + misc_info.flags1 = static_cast<llvm::support::ulittle32_t>(0); + + lldb_private::ProcessInstanceInfo process_info; + process_sp->GetProcessInfo(process_info); + if (process_info.ProcessIDIsValid()) { + // Set flags1 to reflect that PID is filled in + misc_info.flags1 = + static_cast<llvm::support::ulittle32_t>(static_cast<uint32_t>( + lldb_private::minidump::MinidumpMiscInfoFlags::ProcessID)); + misc_info.process_id = + static_cast<llvm::support::ulittle32_t>(process_info.GetProcessID()); + } + + m_data.AppendData(&misc_info, + sizeof(lldb_private::minidump::MinidumpMiscInfo)); +} + +std::unique_ptr<llvm::MemoryBuffer> +getFileStreamHelper(const std::string &path) { + auto maybe_stream = llvm::MemoryBuffer::getFileAsStream(path); + if (!maybe_stream) + return nullptr; + return std::move(maybe_stream.get()); +} + +void MinidumpFileBuilder::AddLinuxFileStreams( + const lldb::ProcessSP &process_sp) { + std::vector<std::pair<StreamType, std::string>> files_with_stream_types = { + {StreamType::LinuxCPUInfo, "/proc/cpuinfo"}, + {StreamType::LinuxLSBRelease, "/etc/lsb-release"}, + }; + + lldb_private::ProcessInstanceInfo process_info; + process_sp->GetProcessInfo(process_info); + if (process_info.ProcessIDIsValid()) { + lldb::pid_t pid = process_info.GetProcessID(); + std::string pid_str = std::to_string(pid); + files_with_stream_types.push_back( + {StreamType::LinuxProcStatus, "/proc/" + pid_str + "/status"}); + files_with_stream_types.push_back( + {StreamType::LinuxCMDLine, "/proc/" + pid_str + "/cmdline"}); + files_with_stream_types.push_back( + {StreamType::LinuxEnviron, "/proc/" + pid_str + "/environ"}); + files_with_stream_types.push_back( + {StreamType::LinuxAuxv, "/proc/" + pid_str + "/auxv"}); + files_with_stream_types.push_back( + {StreamType::LinuxMaps, "/proc/" + pid_str + "/maps"}); + files_with_stream_types.push_back( + {StreamType::LinuxProcStat, "/proc/" + pid_str + "/stat"}); + files_with_stream_types.push_back( + {StreamType::LinuxProcFD, "/proc/" + pid_str + "/fd"}); + } + + for (const auto &entry : files_with_stream_types) { + StreamType stream = entry.first; + std::string path = entry.second; + auto memory_buffer = getFileStreamHelper(path); + + if (memory_buffer) { + size_t size = memory_buffer->getBufferSize(); + if (size == 0) + continue; + AddDirectory(stream, size); + m_data.AppendData(memory_buffer->getBufferStart(), size); + } + } +} + +Status MinidumpFileBuilder::Dump(lldb::FileUP &core_file) const { + constexpr size_t header_size = sizeof(llvm::minidump::Header); + constexpr size_t directory_size = sizeof(llvm::minidump::Directory); + + // write header + llvm::minidump::Header header; + header.Signature = static_cast<llvm::support::ulittle32_t>( + llvm::minidump::Header::MagicSignature); + header.Version = static_cast<llvm::support::ulittle32_t>( + llvm::minidump::Header::MagicVersion); + header.NumberOfStreams = + static_cast<llvm::support::ulittle32_t>(GetDirectoriesNum()); + header.StreamDirectoryRVA = + static_cast<llvm::support::ulittle32_t>(GetCurrentDataEndOffset()); + header.Checksum = static_cast<llvm::support::ulittle32_t>( + 0u), // not used in most of the writers + header.TimeDateStamp = + static_cast<llvm::support::ulittle32_t>(std::time(0)); + header.Flags = + static_cast<llvm::support::ulittle64_t>(0u); // minidump normal flag + + Status error; + size_t bytes_written; + + bytes_written = header_size; + error = core_file->Write(&header, bytes_written); + if (error.Fail() || bytes_written != header_size) { + if (bytes_written != header_size) + error.SetErrorStringWithFormat( + "unable to write the header (written %zd/%zd)", bytes_written, + header_size); + return error; + } + + // write data + bytes_written = m_data.GetByteSize(); + error = core_file->Write(m_data.GetBytes(), bytes_written); + if (error.Fail() || bytes_written != m_data.GetByteSize()) { + if (bytes_written != m_data.GetByteSize()) + error.SetErrorStringWithFormat( + "unable to write the data (written %zd/%" PRIu64 ")", bytes_written, + m_data.GetByteSize()); + return error; + } + + // write directories + for (const Directory &dir : m_directories) { + bytes_written = directory_size; + error = core_file->Write(&dir, bytes_written); + if (error.Fail() || bytes_written != directory_size) { + if (bytes_written != directory_size) + error.SetErrorStringWithFormat( + "unable to write the directory (written %zd/%zd)", bytes_written, + directory_size); + return error; + } + } + + return error; +} + +size_t MinidumpFileBuilder::GetDirectoriesNum() const { + return m_directories.size(); +} + +size_t MinidumpFileBuilder::GetCurrentDataEndOffset() const { + return sizeof(llvm::minidump::Header) + m_data.GetByteSize(); +} diff --git a/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h new file mode 100644 index 000000000000..f4017fb66384 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/Minidump/MinidumpFileBuilder.h @@ -0,0 +1,92 @@ +//===-- MinidumpFileBuilder.h ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Structure holding data neccessary for minidump file creation. +/// +/// The class MinidumpFileWriter is used to hold the data that will eventually +/// be dumped to the file. +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H +#define LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H + +#include <cstddef> + +#include "lldb/Target/Target.h" +#include "lldb/Utility/DataBufferHeap.h" +#include "lldb/Utility/Status.h" + +#include "llvm/Object/Minidump.h" + +// Write std::string to minidump in the UTF16 format(with null termination char) +// with the size(without null termination char) preceding the UTF16 string. +// Empty strings are also printed with zero length and just null termination +// char. +lldb_private::Status WriteString(const std::string &to_write, + lldb_private::DataBufferHeap *buffer); + +/// \class MinidumpFileBuilder +/// Minidump writer for Linux +/// +/// This class provides a Minidump writer that is able to +/// snapshot the current process state. For the whole time, it stores all +/// the data on heap. +class MinidumpFileBuilder { +public: + MinidumpFileBuilder() = default; + + MinidumpFileBuilder(const MinidumpFileBuilder &) = delete; + MinidumpFileBuilder &operator=(const MinidumpFileBuilder &) = delete; + + MinidumpFileBuilder(MinidumpFileBuilder &&other) = default; + MinidumpFileBuilder &operator=(MinidumpFileBuilder &&other) = default; + + ~MinidumpFileBuilder() = default; + + // Add SystemInfo stream, used for storing the most basic information + // about the system, platform etc... + lldb_private::Status AddSystemInfo(const llvm::Triple &target_triple); + // Add ModuleList stream, containing information about all loaded modules + // at the time of saving minidump. + lldb_private::Status AddModuleList(lldb_private::Target &target); + // Add ThreadList stream, containing information about all threads running + // at the moment of core saving. Contains information about thread + // contexts. + lldb_private::Status AddThreadList(const lldb::ProcessSP &process_sp); + // Add Exception stream, this contains information about the exception + // that stopped the process. In case no thread made exception it return + // failed status. + lldb_private::Status AddException(const lldb::ProcessSP &process_sp); + // Add MemoryList stream, containing dumps of important memory segments + lldb_private::Status AddMemoryList(const lldb::ProcessSP &process_sp); + // Add MiscInfo stream, mainly providing ProcessId + void AddMiscInfo(const lldb::ProcessSP &process_sp); + // Add informative files about a Linux process + void AddLinuxFileStreams(const lldb::ProcessSP &process_sp); + // Dump the prepared data into file. In case of the failure data are + // intact. + lldb_private::Status Dump(lldb::FileUP &core_file) const; + // Returns the current number of directories(streams) that have been so far + // created. This number of directories will be dumped when calling Dump() + size_t GetDirectoriesNum() const; + +private: + // Add directory of StreamType pointing to the current end of the prepared + // file with the specified size. + void AddDirectory(llvm::minidump::StreamType type, size_t stream_size); + size_t GetCurrentDataEndOffset() const; + + // Stores directories to later put them at the end of minidump file + std::vector<llvm::minidump::Directory> m_directories; + // Main data buffer consisting of data without the minidump header and + // directories + lldb_private::DataBufferHeap m_data; +}; + +#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_MINIDUMPFILEBUILDER_H diff --git a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp new file mode 100644 index 000000000000..715ccd311dee --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.cpp @@ -0,0 +1,114 @@ +//===-- ObjectFileMinidump.cpp --------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ObjectFileMinidump.h" + +#include "MinidumpFileBuilder.h" + +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/Process.h" + +#include "llvm/Support/FileSystem.h" + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(ObjectFileMinidump) + +void ObjectFileMinidump::Initialize() { + PluginManager::RegisterPlugin( + GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance, + CreateMemoryInstance, GetModuleSpecifications, SaveCore); +} + +void ObjectFileMinidump::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +ObjectFile *ObjectFileMinidump::CreateInstance( + const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length) { + return nullptr; +} + +ObjectFile *ObjectFileMinidump::CreateMemoryInstance( + const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, + const ProcessSP &process_sp, lldb::addr_t header_addr) { + return nullptr; +} + +size_t ObjectFileMinidump::GetModuleSpecifications( + const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, lldb::offset_t file_offset, + lldb::offset_t length, lldb_private::ModuleSpecList &specs) { + specs.Clear(); + return 0; +} + +bool ObjectFileMinidump::SaveCore(const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb::SaveCoreStyle &core_style, + lldb_private::Status &error) { + if (core_style != SaveCoreStyle::eSaveCoreStackOnly) { + error.SetErrorString("Only stack minidumps supported yet."); + return false; + } + + if (!process_sp) + return false; + + MinidumpFileBuilder builder; + + Target &target = process_sp->GetTarget(); + + error = builder.AddSystemInfo(target.GetArchitecture().GetTriple()); + if (error.Fail()) + return false; + + error = builder.AddModuleList(target); + if (error.Fail()) + return false; + + builder.AddMiscInfo(process_sp); + + if (target.GetArchitecture().GetMachine() == llvm::Triple::ArchType::x86_64) { + error = builder.AddThreadList(process_sp); + if (error.Fail()) + return false; + + error = builder.AddException(process_sp); + if (error.Fail()) + return false; + + error = builder.AddMemoryList(process_sp); + if (error.Fail()) + return false; + } + + if (target.GetArchitecture().GetTriple().getOS() == + llvm::Triple::OSType::Linux) { + builder.AddLinuxFileStreams(process_sp); + } + + llvm::Expected<lldb::FileUP> maybe_core_file = FileSystem::Instance().Open( + outfile, File::eOpenOptionWriteOnly | File::eOpenOptionCanCreate); + if (!maybe_core_file) { + error = maybe_core_file.takeError(); + return false; + } + lldb::FileUP core_file = std::move(maybe_core_file.get()); + + error = builder.Dump(core_file); + if (error.Fail()) + return false; + + return true; +} diff --git a/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.h b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.h new file mode 100644 index 000000000000..3e4d55dc6c8c --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/Minidump/ObjectFileMinidump.h @@ -0,0 +1,66 @@ +//===-- ObjectFileMinidump.h ---------------------------------- -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Placeholder plugin for the save core functionality. +/// +/// ObjectFileMinidump is created only to be able to save minidump core files +/// from existing processes with the ObjectFileMinidump::SaveCore function. +/// Minidump files are not ObjectFile objects, but they are core files and +/// currently LLDB's ObjectFile plug-ins handle emitting core files. If the +/// core file saving ever moves into a new plug-in type within LLDB, this code +/// should move as well, but for now this is the best place architecturally. +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_OBJECTFILEMINIDUMP_H +#define LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_OBJECTFILEMINIDUMP_H + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" + +class ObjectFileMinidump : public lldb_private::PluginInterface { +public: + // Static Functions + static void Initialize(); + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "minidump"; } + static const char *GetPluginDescriptionStatic() { + return "Minidump object file."; + } + + // PluginInterface protocol + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + static lldb_private::ObjectFile * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const lldb_private::FileSpec *file, + lldb::offset_t offset, lldb::offset_t length); + + static lldb_private::ObjectFile *CreateMemoryInstance( + const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + const lldb::ProcessSP &process_sp, lldb::addr_t header_addr); + + static size_t GetModuleSpecifications(const lldb_private::FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb_private::ModuleSpecList &specs); + + // Saves dump in Minidump file format + static bool SaveCore(const lldb::ProcessSP &process_sp, + const lldb_private::FileSpec &outfile, + lldb::SaveCoreStyle &core_style, + lldb_private::Status &error); + +private: + ObjectFileMinidump() = default; +}; + +#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_MINIDUMP_OBJECTFILEMINIDUMP_H diff --git a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp index cb7bbeeca054..b63cd8e70899 100644 --- a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp +++ b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.cpp @@ -46,11 +46,6 @@ void ObjectFilePDB::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } -ConstString ObjectFilePDB::GetPluginNameStatic() { - static ConstString g_name("pdb"); - return g_name; -} - ArchSpec ObjectFilePDB::GetArchitecture() { auto dbi_stream = m_file_up->getPDBDbiStream(); if (!dbi_stream) { diff --git a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.h b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.h index 19dd46b31406..36e71e21332f 100644 --- a/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.h +++ b/lldb/source/Plugins/ObjectFile/PDB/ObjectFilePDB.h @@ -22,7 +22,7 @@ public: static void Initialize(); static void Terminate(); - static ConstString GetPluginNameStatic(); + static llvm::StringRef GetPluginNameStatic() { return "pdb"; } static const char *GetPluginDescriptionStatic() { return "PDB object file reader."; } @@ -48,9 +48,7 @@ public: ModuleSpecList &specs); // PluginInterface protocol - ConstString GetPluginName() override { return GetPluginNameStatic(); } - - uint32_t GetPluginVersion() override { return 1; } + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } // LLVM RTTI support static char ID; diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp index 5272da9ab33a..0e6329885528 100644 --- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.cpp @@ -86,11 +86,6 @@ void ObjectFileWasm::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } -ConstString ObjectFileWasm::GetPluginNameStatic() { - static ConstString g_name("wasm"); - return g_name; -} - ObjectFile * ObjectFileWasm::CreateInstance(const ModuleSP &module_sp, DataBufferSP &data_sp, offset_t data_offset, const FileSpec *file, diff --git a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h index b6e906a7b15f..44939b6d4ea0 100644 --- a/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h +++ b/lldb/source/Plugins/ObjectFile/wasm/ObjectFileWasm.h @@ -24,7 +24,7 @@ public: static void Initialize(); static void Terminate(); - static ConstString GetPluginNameStatic(); + static llvm::StringRef GetPluginNameStatic() { return "wasm"; } static const char *GetPluginDescriptionStatic() { return "WebAssembly object file reader."; } @@ -48,8 +48,7 @@ public: /// PluginInterface protocol. /// \{ - ConstString GetPluginName() override { return GetPluginNameStatic(); } - uint32_t GetPluginVersion() override { return 1; } + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } /// \} /// LLVM RTTI support |
