labath created this revision. labath added reviewers: clayborg, zturner, lemo, amccarth. Herald added subscribers: fedor.sergeev, mgorny.
This patch adds the scaffolding necessary for lldb to recognise symbol files generated by breakpad. These (textual) files contain just enough information to be able to produce a backtrace from a crash dump. This information includes: - UUID, architecture and name of the module - line tables - list of symbols - unwind information A minimal breakpad file could look like this: MODULE Linux x86_64 0000000024B5D199F0F766FFFFFF5DC30 a.out INFO CODE_ID 00000000B52499D1F0F766FFFFFF5DC3 FILE 0 /tmp/a.c FUNC 1010 10 0 _start 1010 4 4 0 1014 5 5 0 1019 5 6 0 101e 2 7 0 PUBLIC 1010 0 _start STACK CFI INIT 1010 10 .cfa: $rsp 8 + .ra: .cfa -8 + ^ STACK CFI 1011 $rbp: .cfa -16 + ^ .cfa: $rsp 16 + STACK CFI 1014 .cfa: $rbp 16 + Even though this data would normally be considered "symbol" information, in the current lldb infrastructure it is assumed every SymbolFile object is backed by an ObjectFile instance. So, in order to better interoperate with the rest of the code (particularly symbol vendors). In this patch I just parse the breakpad header, which is enough to populate the UUID and architecture fields of the ObjectFile interface. The rough plan for followup patches is to expose the individual parts of the breakpad file as ObjectFile "sections", which can then be used by other parts of the codebase (SymbolFileBreakpad ?) to vend the necessary information. https://reviews.llvm.org/D55214 Files: include/lldb/Symbol/ObjectFile.h lit/Modules/Breakpad/Inputs/identification-linux.syms lit/Modules/Breakpad/Inputs/identification-macosx.syms lit/Modules/Breakpad/Inputs/identification-windows.syms lit/Modules/Breakpad/breakpad-identification.test lit/Modules/Breakpad/lit.local.cfg source/Plugins/ObjectFile/Breakpad/CMakeLists.txt source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h source/Plugins/ObjectFile/CMakeLists.txt source/Symbol/ObjectFile.cpp tools/lldb-test/SystemInitializerTest.cpp tools/lldb-test/lldb-test.cpp
Index: tools/lldb-test/lldb-test.cpp =================================================================== --- tools/lldb-test/lldb-test.cpp +++ tools/lldb-test/lldb-test.cpp @@ -734,9 +734,16 @@ continue; } + auto *ObjectPtr = ModulePtr->GetObjectFile(); + + Printer.formatLine("Plugin name: {0}", ObjectPtr->GetPluginName()); Printer.formatLine("Architecture: {0}", ModulePtr->GetArchitecture().GetTriple().getTriple()); Printer.formatLine("UUID: {0}", ModulePtr->GetUUID().GetAsString()); + Printer.formatLine("Executable: {0}", ObjectPtr->IsExecutable()); + Printer.formatLine("Stripped: {0}", ObjectPtr->IsStripped()); + Printer.formatLine("Type: {0}", ObjectPtr->GetType()); + Printer.formatLine("Strata: {0}", ObjectPtr->GetStrata()); size_t Count = Sections->GetNumSections(0); Printer.formatLine("Showing {0} sections", Count); Index: tools/lldb-test/SystemInitializerTest.cpp =================================================================== --- tools/lldb-test/SystemInitializerTest.cpp +++ tools/lldb-test/SystemInitializerTest.cpp @@ -52,6 +52,7 @@ #include "Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.h" #include "Plugins/LanguageRuntime/RenderScript/RenderScriptRuntime/RenderScriptRuntime.h" #include "Plugins/MemoryHistory/asan/MemoryHistoryASan.h" +#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h" #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" #include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" #include "Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h" @@ -114,6 +115,7 @@ void SystemInitializerTest::Initialize() { SystemInitializerCommon::Initialize(); + breakpad::ObjectFileBreakpad::Initialize(); ObjectFileELF::Initialize(); ObjectFileMachO::Initialize(); ObjectFilePECOFF::Initialize(); @@ -327,6 +329,7 @@ PlatformDarwinKernel::Terminate(); #endif + breakpad::ObjectFileBreakpad::Terminate(); ObjectFileELF::Terminate(); ObjectFileMachO::Terminate(); ObjectFilePECOFF::Terminate(); Index: source/Symbol/ObjectFile.cpp =================================================================== --- source/Symbol/ObjectFile.cpp +++ source/Symbol/ObjectFile.cpp @@ -688,3 +688,63 @@ uint64_t Offset) { return FileSystem::Instance().CreateDataBuffer(file.GetPath(), Size, Offset); } + +void llvm::format_provider<ObjectFile::Type>::format( + const ObjectFile::Type &type, raw_ostream &OS, StringRef Style) { + switch (type) { + case ObjectFile::eTypeInvalid: + OS << "invalid"; + break; + case ObjectFile::eTypeCoreFile: + OS << "core file"; + break; + case ObjectFile::eTypeExecutable: + OS << "executable"; + break; + case ObjectFile::eTypeDebugInfo: + OS << "debug info"; + break; + case ObjectFile::eTypeDynamicLinker: + OS << "dynamic linker"; + break; + case ObjectFile::eTypeObjectFile: + OS << "object file"; + break; + case ObjectFile::eTypeSharedLibrary: + OS << "shared library"; + break; + case ObjectFile::eTypeStubLibrary: + OS << "stub library"; + break; + case ObjectFile::eTypeJIT: + OS << "jit"; + break; + case ObjectFile::eTypeUnknown: + OS << "unknown"; + break; + } +} + +void llvm::format_provider<ObjectFile::Strata>::format( + const ObjectFile::Strata &strata, raw_ostream &OS, StringRef Style) { + switch (strata) { + case ObjectFile::eStrataInvalid: + OS << "invalid"; + break; + case ObjectFile::eStrataUnknown: + OS << "unknown"; + break; + case ObjectFile::eStrataUser: + OS << "user"; + break; + case ObjectFile::eStrataKernel: + OS << "kernel"; + break; + case ObjectFile::eStrataRawImage: + OS << "raw image"; + break; + case ObjectFile::eStrataJIT: + OS << "jit"; + break; + } +} Index: source/Plugins/ObjectFile/CMakeLists.txt =================================================================== --- source/Plugins/ObjectFile/CMakeLists.txt +++ source/Plugins/ObjectFile/CMakeLists.txt @@ -1,4 +1,5 @@ +add_subdirectory(Breakpad) add_subdirectory(ELF) add_subdirectory(Mach-O) add_subdirectory(PECOFF) -add_subdirectory(JIT) \ No newline at end of file +add_subdirectory(JIT) Index: source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h =================================================================== --- /dev/null +++ source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h @@ -0,0 +1,116 @@ +//===-- ObjectFileBreakpad.h ---------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H +#define LLDB_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H + +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Utility/ArchSpec.h" +#include "llvm/ADT/Triple.h" + +namespace lldb_private { +namespace breakpad { + +class ObjectFileBreakpad : public ObjectFile { +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void Initialize(); + static void Terminate(); + + static ConstString GetPluginNameStatic(); + static const char *GetPluginDescriptionStatic() { + return "Breakpad object file reader."; + } + + static ObjectFile * + CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, const FileSpec *file, + lldb::offset_t file_offset, lldb::offset_t length); + + static size_t GetModuleSpecifications(const FileSpec &file, + lldb::DataBufferSP &data_sp, + lldb::offset_t data_offset, + lldb::offset_t file_offset, + lldb::offset_t length, + ModuleSpecList &specs); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + ConstString GetPluginName() override { return GetPluginNameStatic(); } + + uint32_t GetPluginVersion() override { return 1; } + + //------------------------------------------------------------------ + // ObjectFile Protocol. + //------------------------------------------------------------------ + + bool ParseHeader() override; + + lldb::ByteOrder GetByteOrder() const override { + return m_arch.GetByteOrder(); + } + + bool IsExecutable() const override { return false; } + + uint32_t GetAddressByteSize() const override { + return m_arch.GetAddressByteSize(); + } + + AddressClass GetAddressClass(lldb::addr_t file_addr) override { + return AddressClass::eInvalid; + } + + Symtab *GetSymtab() override; + + bool IsStripped() override { return false; } + + void CreateSections(SectionList &unified_section_list) override; + + void Dump(Stream *s) override {} + + bool GetArchitecture(ArchSpec &arch) override; + + bool GetUUID(UUID *uuid) override; + + FileSpecList GetDebugSymbolFilePaths() override { return FileSpecList(); } + + uint32_t GetDependentModules(FileSpecList &files) override { return 0; } + + Type CalculateType() override { return eTypeDebugInfo; } + + Strata CalculateStrata() override { return eStrataUser; } + + size_t ReadSectionData(Section *section, lldb::offset_t section_offset, + void *dst, size_t dst_len) override; + + size_t ReadSectionData(Section *section, + DataExtractor §ion_data) override; + +private: + struct Header { + ArchSpec arch; + UUID uuid; + static llvm::Optional<Header> parse(llvm::StringRef text); + }; + + ArchSpec m_arch; + UUID m_uuid; + + ObjectFileBreakpad(const lldb::ModuleSP &module_sp, + lldb::DataBufferSP &data_sp, lldb::offset_t data_offset, + const FileSpec *file, lldb::offset_t offset, + lldb::offset_t length, const Header &header); +}; + +} // namespace breakpad +} // namespace lldb_private +#endif // LLDB_PLUGINS_OBJECTFILE_BREAKPAD_OBJECTFILEBREAKPAD_H Index: source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp =================================================================== --- /dev/null +++ source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp @@ -0,0 +1,191 @@ +//===-- ObjectFileBreakpad.cpp -------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Utility/DataBuffer.h" +#include "llvm/ADT/StringExtras.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::breakpad; + +static llvm::Triple::OSType toOS(llvm::StringRef str) { + using llvm::Triple; + return llvm::StringSwitch<Triple::OSType>(str) + .Case("Linux", Triple::Linux) + .Case("mac", Triple::MacOSX) + .Case("windows", Triple::Win32) + .Default(Triple::UnknownOS); +} + +static llvm::Triple::ArchType toArch(llvm::StringRef str) { + using llvm::Triple; + return llvm::StringSwitch<Triple::ArchType>(str) + .Case("arm", Triple::arm) + .Case("arm64", Triple::aarch64) + .Case("mips", Triple::mips) + .Case("ppc", Triple::ppc) + .Case("ppc64", Triple::ppc64) + .Case("s390", Triple::systemz) + .Case("sparc", Triple::sparc) + .Case("sparcv9", Triple::sparcv9) + .Case("x86", Triple::x86) + .Case("x86_64", Triple::x86_64) + .Default(Triple::UnknownArch); +} + +llvm::Optional<ObjectFileBreakpad::Header> +ObjectFileBreakpad::Header::parse(llvm::StringRef text) { + // Ideally, the text should contain something like: + // MODULE Linux x86_64 2AD7CF49A539A773DBF9BE14856F61240 a.out + llvm::StringRef token; + std::tie(token, text) = getToken(text); + if (token != "MODULE") + return llvm::None; + + std::tie(token, text) = getToken(text); + llvm::Triple::OSType os = toOS(token); + if (os == llvm::Triple::UnknownOS) + return llvm::None; + + std::tie(token, text) = getToken(text); + llvm::Triple::ArchType arch = toArch(token); + if (arch == llvm::Triple::UnknownArch) + return llvm::None; + + std::tie(token, text) = getToken(text); + UUID uuid; + if (os == llvm::Triple::Win32) { + // In binary form, the module id should have 20 bytes: 16 bytes for UUID, + // and 4 bytes for the "age". However, in the textual format, the 4 bytes of + // age are printed via %x, which can lead to shorter strings. So, we pad the + // string with zeroes after the 16 bytes, to obtain a string of appropriate + // size. + if (token.size() < 33 || token.size() > 40) + return llvm::None; + std::string padded = token; + padded.insert(32, 40 - token.size(), '0'); + if (uuid.SetFromStringRef(padded, 20) != 40) + return llvm::None; + } else { + // The age will always be "0", so strip it to obtain the cannonical build-id + // representation on these platforms. + token.consume_back("0"); + if (uuid.SetFromStringRef(token, token.size() / 2) != token.size()) + return llvm::None; + } + llvm::Triple triple; + triple.setArch(arch); + triple.setOS(os); + return Header{ArchSpec(triple), uuid}; +} + +void ObjectFileBreakpad::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + GetPluginDescriptionStatic(), CreateInstance, + nullptr, GetModuleSpecifications); +} + +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) { + if (!data_sp) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + } + llvm::StringRef text(reinterpret_cast<const char *>(data_sp->GetBytes()), + data_sp->GetByteSize()); + + llvm::Optional<Header> header = Header::parse(text); + if (!header) + return nullptr; + + // Update the data to contain the entire file if it doesn't already + if (data_sp->GetByteSize() < length) { + data_sp = MapFileData(*file, length, file_offset); + if (!data_sp) + return nullptr; + data_offset = 0; + } + + return new ObjectFileBreakpad(module_sp, data_sp, data_offset, file, + file_offset, length, *header); +} + +size_t ObjectFileBreakpad::GetModuleSpecifications( + const FileSpec &file, DataBufferSP &data_sp, offset_t data_offset, + offset_t file_offset, offset_t length, ModuleSpecList &specs) { + llvm::StringRef text(reinterpret_cast<const char *>(data_sp->GetBytes()), + data_sp->GetByteSize()); + llvm::Optional<Header> header = Header::parse(text); + if (!header) + return 0; + ModuleSpec spec(file, header->arch); + spec.GetUUID() = header->uuid; + specs.Append(spec); + return 1; +} + +ObjectFileBreakpad::ObjectFileBreakpad(const ModuleSP &module_sp, + DataBufferSP &data_sp, + offset_t data_offset, + const FileSpec *file, offset_t offset, + offset_t length, const Header &header) + : ObjectFile(module_sp, file, offset, length, data_sp, data_offset), + m_arch(header.arch), m_uuid(header.uuid) {} + +bool ObjectFileBreakpad::ParseHeader() { + // We already parsed the header during initialization. + return true; +} + +Symtab *ObjectFileBreakpad::GetSymtab() { + // TODO + return nullptr; +} + +bool ObjectFileBreakpad::GetArchitecture(ArchSpec &arch) { + arch = m_arch; + return true; +} + +bool ObjectFileBreakpad::GetUUID(UUID *uuid) { + *uuid = m_uuid; + return true; +} + +void ObjectFileBreakpad::CreateSections(SectionList &unified_section_list) { + // TODO +} + +size_t ObjectFileBreakpad::ReadSectionData(Section *section, + lldb::offset_t section_offset, + void *dst, size_t dst_len) { + // TODO + return 0; +} + +size_t ObjectFileBreakpad::ReadSectionData(Section *section, + DataExtractor §ion_data) { + // TODO + return 0; +} Index: source/Plugins/ObjectFile/Breakpad/CMakeLists.txt =================================================================== --- /dev/null +++ source/Plugins/ObjectFile/Breakpad/CMakeLists.txt @@ -0,0 +1,11 @@ +add_lldb_library(lldbPluginObjectFileBreakpad PLUGIN + ObjectFileBreakpad.cpp + + LINK_LIBS + lldbCore + lldbHost + lldbSymbol + lldbUtility + LINK_COMPONENTS + Support + ) Index: lit/Modules/Breakpad/lit.local.cfg =================================================================== --- /dev/null +++ lit/Modules/Breakpad/lit.local.cfg @@ -0,0 +1 @@ +config.suffixes = ['.test'] Index: lit/Modules/Breakpad/breakpad-identification.test =================================================================== --- /dev/null +++ lit/Modules/Breakpad/breakpad-identification.test @@ -0,0 +1,27 @@ +RUN: lldb-test object-file %p/Inputs/identification-linux.syms | FileCheck %s --check-prefix=LINUX +RUN: lldb-test object-file %p/Inputs/identification-macosx.syms | FileCheck %s --check-prefix=MAC +RUN: lldb-test object-file %p/Inputs/identification-windows.syms | FileCheck %s --check-prefix=WINDOWS + +LINUX: Plugin name: breakpad +LINUX: Architecture: x86_64--linux +LINUX: UUID: E5894855-C35D-CCCC-CCCC-CCCCCCCCCCCC +LINUX: Executable: false +LINUX: Stripped: false +LINUX: Type: debug info +LINUX: Strata: user + +MAC: Plugin name: breakpad +MAC: Architecture: x86_64--macosx +MAC: UUID: D98C0E68-2089-AA1B-EACD-6A8C1F16707B +MAC: Executable: false +MAC: Stripped: false +MAC: Type: debug info +MAC: Strata: user + +WINDOWS: Plugin name: breakpad +WINDOWS: Architecture: i386--windows +WINDOWS: UUID: A0C91657-80B5-4909-81A1-925EA62165C0-00000001 +WINDOWS: Executable: false +WINDOWS: Stripped: false +WINDOWS: Type: debug info +WINDOWS: Strata: user Index: lit/Modules/Breakpad/Inputs/identification-windows.syms =================================================================== --- /dev/null +++ lit/Modules/Breakpad/Inputs/identification-windows.syms @@ -0,0 +1,4 @@ +MODULE windows x86 A0C9165780B5490981A1925EA62165C01 a.pdb +INFO CODE_ID 5C01672A4000 a.exe +FILE 1 c:\tmp\a.cpp +PUBLIC 1000 0 main Index: lit/Modules/Breakpad/Inputs/identification-macosx.syms =================================================================== --- /dev/null +++ lit/Modules/Breakpad/Inputs/identification-macosx.syms @@ -0,0 +1,6 @@ +MODULE mac x86_64 D98C0E682089AA1BEACD6A8C1F16707B0 mac.out +PUBLIC 0 0 _mh_execute_header +PUBLIC f30 0 start +STACK CFI INIT f30 6 .cfa: $rsp 8 + .ra: .cfa -8 + ^ +STACK CFI f31 $rbp: .cfa -16 + ^ .cfa: $rsp 16 + +STACK CFI f34 .cfa: $rbp 16 + Index: lit/Modules/Breakpad/Inputs/identification-linux.syms =================================================================== --- /dev/null +++ lit/Modules/Breakpad/Inputs/identification-linux.syms @@ -0,0 +1,6 @@ +MODULE Linux x86_64 E5894855C35DCCCCCCCCCCCCCCCCCCCC0 linux.out +INFO CODE_ID 554889E55DC3CCCCCCCCCCCCCCCCCCCC +PUBLIC 1000 0 _start +STACK CFI INIT 1000 6 .cfa: $rsp 8 + .ra: .cfa -8 + ^ +STACK CFI 1001 $rbp: .cfa -16 + ^ .cfa: $rsp 16 + +STACK CFI 1004 .cfa: $rbp 16 + Index: include/lldb/Symbol/ObjectFile.h =================================================================== --- include/lldb/Symbol/ObjectFile.h +++ include/lldb/Symbol/ObjectFile.h @@ -817,4 +817,16 @@ } // namespace lldb_private +namespace llvm { +template <> struct format_provider<lldb_private::ObjectFile::Type> { + static void format(const lldb_private::ObjectFile::Type &type, + raw_ostream &OS, StringRef Style); +}; + +template <> struct format_provider<lldb_private::ObjectFile::Strata> { + static void format(const lldb_private::ObjectFile::Strata &strata, + raw_ostream &OS, StringRef Style); +}; +} // namespace llvm + #endif // liblldb_ObjectFile_h_
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits