Index: include/lldb/Core/FileSpecList.h =================================================================== --- include/lldb/Core/FileSpecList.h +++ include/lldb/Core/FileSpecList.h @@ -47,6 +47,9 @@ //------------------------------------------------------------------ FileSpecList(const FileSpecList &rhs); + /// Initialize this object from a vector of FileSpecs + FileSpecList(std::vector &&rhs) : m_files(std::move(rhs)) {} + //------------------------------------------------------------------ /// Destructor. //------------------------------------------------------------------ @@ -63,7 +66,10 @@ /// @return /// A const reference to this object. //------------------------------------------------------------------ - const FileSpecList &operator=(const FileSpecList &rhs); + FileSpecList &operator=(const FileSpecList &rhs) = default; + + /// Move-assignment operator. + FileSpecList &operator=(FileSpecList &&rhs) = default; //------------------------------------------------------------------ /// Append a FileSpec object to the list. Index: include/lldb/Core/RangeMap.h =================================================================== --- include/lldb/Core/RangeMap.h +++ include/lldb/Core/RangeMap.h @@ -715,6 +715,7 @@ // Clients must ensure that "i" is a valid index prior to calling this // function + Entry &GetEntryRef(size_t i) { return m_entries[i]; } const Entry &GetEntryRef(size_t i) const { return m_entries[i]; } static bool BaseLessThan(const Entry &lhs, const Entry &rhs) { Index: lit/SymbolFile/Breakpad/Inputs/line-table-discontinuous-file-ids.syms =================================================================== --- /dev/null +++ lit/SymbolFile/Breakpad/Inputs/line-table-discontinuous-file-ids.syms @@ -0,0 +1,8 @@ +MODULE Linux x86_64 761550E08086333960A9074A9CE2895C0 a.out +INFO CODE_ID E05015768680393360A9074A9CE2895C +FILE 1 /tmp/a.c +FILE 3 /tmp/c.c +FUNC b0 10 0 func +b0 1 1 1 +b1 1 2 1 +b2 1 2 3 Index: lit/SymbolFile/Breakpad/Inputs/line-table-edgecases.syms =================================================================== --- /dev/null +++ lit/SymbolFile/Breakpad/Inputs/line-table-edgecases.syms @@ -0,0 +1,7 @@ +MODULE Linux x86_64 761550E08086333960A9074A9CE2895C0 a.out +INFO CODE_ID E05015768680393360A9074A9CE2895C +FILE 0 /tmp/a.c +a0 1 1 0 +FUNC b0 10 0 func +FUNC c0 10 0 func2 +c0 2 2 0 Index: lit/SymbolFile/Breakpad/Inputs/line-table-missing-file.syms =================================================================== --- /dev/null +++ lit/SymbolFile/Breakpad/Inputs/line-table-missing-file.syms @@ -0,0 +1,7 @@ +MODULE Linux x86_64 761550E08086333960A9074A9CE2895C0 a.out +INFO CODE_ID E05015768680393360A9074A9CE2895C +FILE 0 /tmp/a.c +FUNC b0 10 0 func +b0 1 1 0 +b1 1 2 0 +b2 1 2 1 Index: lit/SymbolFile/Breakpad/Inputs/line-table.syms =================================================================== --- /dev/null +++ lit/SymbolFile/Breakpad/Inputs/line-table.syms @@ -0,0 +1,17 @@ +MODULE Linux x86_64 761550E08086333960A9074A9CE2895C0 a.out +INFO CODE_ID E05015768680393360A9074A9CE2895C +FILE 0 /tmp/a.c +FILE 1 /tmp/c.c +FILE 2 /tmp/d.c +FUNC b0 10 0 func +b0 1 1 0 +b1 1 2 0 +b2 1 2 1 +b4 1 3 1 +FUNC c0 10 0 func2 +c0 2 1 1 +c2 2 2 0 +FUNC d0 10 0 func3 +d0 2 1 2 +FUNC e0 10 0 func4 +e0 2 2 2 Index: lit/SymbolFile/Breakpad/line-table-discontinuous-file-ids.test =================================================================== --- /dev/null +++ lit/SymbolFile/Breakpad/line-table-discontinuous-file-ids.test @@ -0,0 +1,13 @@ +# Test that we handle files which has gaps in the FILE record IDs. + +# RUN: yaml2obj %S/Inputs/basic-elf.yaml > %T/line-table-discontinuous-file-ids.out +# RUN: %lldb %T/line-table-discontinuous-file-ids.out \ +# RUN: -o "target symbols add -s line-table-discontinuous-file-ids.out %S/Inputs/line-table-discontinuous-file-ids.syms" \ +# RUN: -s %s -o exit | FileCheck %s + +image dump line-table a.c +# CHECK-LABEL: Line table for /tmp/a.c +# CHECK-NEXT: 0x00000000004000b0: /tmp/a.c:1 +# CHECK-NEXT: 0x00000000004000b1: /tmp/a.c:2 +# CHECK-NEXT: 0x00000000004000b2: /tmp/c.c:2 +# CHECK-NEXT: 0x00000000004000b3: Index: lit/SymbolFile/Breakpad/line-table-edgecases.test =================================================================== --- /dev/null +++ lit/SymbolFile/Breakpad/line-table-edgecases.test @@ -0,0 +1,21 @@ +# Test handling of breakpad files with some unusual or erroneous constructs. The +# input contains a LINE record which does not belong to any function as well as +# a FUNC record without any LINE records. + +# RUN: yaml2obj %S/Inputs/basic-elf.yaml > %T/line-table-edgecases.out +# RUN: %lldb %T/line-table-edgecases.out \ +# RUN: -o "target symbols add -s line-table-edgecases.out %S/Inputs/line-table-edgecases.syms" \ +# RUN: -s %s -o exit | FileCheck %s + +# Test that line table for func2 was parsed properly: +image dump line-table a.c +# CHECK-LABEL: Line table for /tmp/a.c +# CHECK-NEXT: 0x00000000004000c0: /tmp/a.c:2 +# CHECK-NEXT: 0x00000000004000c2: +# CHECK-EMPTY: + +# Looking up an address inside func should still work even if it does not result +# in a line entry. +image lookup -a 0x4000b2 -v +# CHECK-LABEL: image lookup -a 0x4000b2 -v +# CHECK: Summary: line-table-edgecases.out`func + 2 Index: lit/SymbolFile/Breakpad/line-table-missing-file.test =================================================================== --- /dev/null +++ lit/SymbolFile/Breakpad/line-table-missing-file.test @@ -0,0 +1,17 @@ +# Test that we do something reasonable if a LINE record references a +# non-existing FILE record. +# Right now, "something reasonable" means creating a line entry with an empty +# file. + +# RUN: yaml2obj %S/Inputs/basic-elf.yaml > %T/line-table-missing-file.out +# RUN: %lldb %T/line-table-missing-file.out \ +# RUN: -o "target symbols add -s line-table-missing-file.out %S/Inputs/line-table-missing-file.syms" \ +# RUN: -s %s -o exit | FileCheck %s + +image dump line-table a.c +# CHECK-LABEL: Line table for /tmp/a.c +# CHECK-NEXT: 0x00000000004000b0: /tmp/a.c:1 +# CHECK-NEXT: 0x00000000004000b1: /tmp/a.c:2 +# CHECK-NEXT: 0x00000000004000b2: :2 +# CHECK-NEXT: 0x00000000004000b3: +# CHECK-EMPTY: Index: lit/SymbolFile/Breakpad/line-table.test =================================================================== --- /dev/null +++ lit/SymbolFile/Breakpad/line-table.test @@ -0,0 +1,45 @@ +# RUN: yaml2obj %S/Inputs/basic-elf.yaml > %T/line-table.out +# RUN: %lldb %T/line-table.out -o "target symbols add -s line-table.out %S/Inputs/line-table.syms" \ +# RUN: -s %s -o exit | FileCheck %s + +# We create a compile unit for each function. The compile unit name is the first +# line table entry in that function. +# This symbol file contains a single function in the "compile unit" a.c. This +# function has two line table sequences. +image dump line-table a.c +# CHECK-LABEL: Line table for /tmp/a.c +# CHECK-NEXT: 0x00000000004000b0: /tmp/a.c:1 +# CHECK-NEXT: 0x00000000004000b1: /tmp/a.c:2 +# CHECK-NEXT: 0x00000000004000b2: /tmp/c.c:2 +# CHECK-NEXT: 0x00000000004000b3: +# CHECK-EMPTY: +# CHECK-NEXT: 0x00000000004000b4: /tmp/c.c:3 +# CHECK-NEXT: 0x00000000004000b5: +# CHECK-EMPTY: + +# Single compile unit for c.c with a single line sequence. +image dump line-table c.c +# CHECK-LABEL: Line table for /tmp/c.c +# CHECK-NEXT: 0x00000000004000c0: /tmp/c.c:1 +# CHECK-NEXT: 0x00000000004000c2: /tmp/a.c:2 +# CHECK-NEXT: 0x00000000004000c4: +# CHECK-EMPTY: + +# There are two compile units called "d.c". Hence, two line tables. +image dump line-table d.c +# CHECK-LABEL: Line table for /tmp/d.c +# CHECK-NEXT: 0x00000000004000d0: /tmp/d.c:1 +# CHECK-NEXT: 0x00000000004000d2: +# CHECK-EMPTY: +# CHECK-LABEL: Line table for /tmp/d.c +# CHECK-NEXT: 0x00000000004000e0: /tmp/d.c:2 +# CHECK-NEXT: 0x00000000004000e2: +# CHECK-EMPTY: + +image lookup -a 0x4000b2 -v +# CHECK-LABEL: image lookup -a 0x4000b2 -v +# CHECK: Summary: line-table.out`func + 2 + +breakpoint set -f c.c -l 2 +# CHECK-LABEL: breakpoint set -f c.c -l 2 +# CHECK: Breakpoint 1: where = line-table.out`func + 2, address = 0x00000000004000b2 Index: source/Core/FileSpecList.cpp =================================================================== --- source/Core/FileSpecList.cpp +++ source/Core/FileSpecList.cpp @@ -24,15 +24,6 @@ FileSpecList::~FileSpecList() = default; -//------------------------------------------------------------------ -// Assignment operator -//------------------------------------------------------------------ -const FileSpecList &FileSpecList::operator=(const FileSpecList &rhs) { - if (this != &rhs) - m_files = rhs.m_files; - return *this; -} - //------------------------------------------------------------------ // Append the "file_spec" to the end of the file spec list. //------------------------------------------------------------------ Index: source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h =================================================================== --- source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h +++ source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h @@ -9,6 +9,9 @@ #ifndef LLDB_PLUGINS_SYMBOLFILE_BREAKPAD_SYMBOLFILEBREAKPAD_H #define LLDB_PLUGINS_SYMBOLFILE_BREAKPAD_SYMBOLFILEBREAKPAD_H +#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Symbol/LineTable.h" #include "lldb/Symbol/SymbolFile.h" namespace lldb_private { @@ -42,7 +45,7 @@ uint32_t CalculateAbilities() override; - void InitializeObject() override {} + void InitializeObject() override; //------------------------------------------------------------------ // Compile Unit function calls @@ -63,9 +66,7 @@ bool ParseDebugMacros(CompileUnit &comp_unit) override { return false; } bool ParseSupportFiles(CompileUnit &comp_unit, - FileSpecList &support_files) override { - return false; - } + FileSpecList &support_files) override; size_t ParseTypes(CompileUnit &cu) override { return 0; } bool @@ -94,10 +95,16 @@ } bool CompleteType(CompilerType &compiler_type) override { return false; } + uint32_t ResolveSymbolContext(const Address &so_addr, lldb::SymbolContextItem resolve_scope, SymbolContext &sc) override; + uint32_t ResolveSymbolContext(const FileSpec &file_spec, uint32_t line, + bool check_inlines, + lldb::SymbolContextItem resolve_scope, + SymbolContextList &sc_list) override; + size_t GetTypes(SymbolContextScope *sc_scope, lldb::TypeClass type_mask, TypeList &type_list) override { return 0; @@ -137,6 +144,75 @@ uint32_t GetPluginVersion() override { return 1; } private: + // A class representing a position in the breakpad file. Useful for + // remembering the position so we can go back to it later and parse more data. + // Can be converted to/from a LineIterator, but it has a much smaller memory + // footprint. + struct Bookmark { + uint32_t section; + size_t offset; + }; + + // At iterator class for simplifying algorithms reading data from the breakpad + // file. It iterates over all records (lines) in the sections of a given type. + // It also supports saving a specific position (via the GetBookmark() method) + // and then resuming from it afterwards. + class LineIterator; + + // Return an iterator range for all records in the given object file of the + // given type. + llvm::iterator_range lines(Record::Kind section_type); + + // Breakpad files do not contain sufficient information to correctly + // reconstruct compile units. The approach chosen here is to treat each + // function as a compile unit. The compile unit name is the name if the first + // line entry belonging to this function. + // This class is our internal representation of a compile unit. It stores the + // CompileUnit object and a bookmark pointing to the FUNC record of the + // compile unit function. It also lazily construct the list of support files + // and line table entries for the compile unit, when these are needed. + class CompUnitData { + public: + CompUnitData(lldb::CompUnitSP comp_unit, Bookmark bookmark) + : m_comp_unit(std::move(comp_unit)), m_bookmark(bookmark) {} + + CompUnitData() = default; + CompUnitData(const CompUnitData &rhs) + : m_comp_unit(rhs.m_comp_unit), m_bookmark(rhs.m_bookmark) {} + CompUnitData &operator=(const CompUnitData &rhs) { + m_comp_unit = rhs.m_comp_unit; + m_bookmark = rhs.m_bookmark; + m_support_files.reset(); + m_line_table_up.reset(); + return *this; + } + friend bool operator<(const CompUnitData &lhs, const CompUnitData &rhs) { + return std::tie(lhs.m_bookmark.section, lhs.m_bookmark.offset) < + std::tie(rhs.m_bookmark.section, rhs.m_bookmark.offset); + } + + const lldb::CompUnitSP &GetCompUnit() const { return m_comp_unit; } + Bookmark GetBookmark() const { return m_bookmark; } + FileSpecList ReleaseSupportFiles(ObjectFile &obj_file, + llvm::ArrayRef all_files); + std::unique_ptr + ReleaseLineTable(ObjectFile &obj_file, llvm::ArrayRef all_files); + + private: + lldb::CompUnitSP m_comp_unit; + Bookmark m_bookmark; + llvm::Optional m_support_files; + std::unique_ptr m_line_table_up; + + void Parse(ObjectFile &obj_file, llvm::ArrayRef all_files); + }; + + ObjectFile &GetBaseObjectFile(); + + using CompUnitMap = RangeDataVector; + + std::vector m_files; + CompUnitMap m_comp_units; }; } // namespace breakpad Index: source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp =================================================================== --- source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp +++ source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp @@ -13,7 +13,9 @@ #include "lldb/Core/PluginManager.h" #include "lldb/Core/Section.h" #include "lldb/Host/FileSystem.h" +#include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeMap.h" #include "lldb/Utility/Log.h" #include "llvm/ADT/StringExtras.h" @@ -22,64 +24,202 @@ using namespace lldb_private; using namespace lldb_private::breakpad; -namespace { -class LineIterator { +class SymbolFileBreakpad::LineIterator { public: // begin iterator for sections of given type LineIterator(ObjectFile &obj, Record::Kind section_type) : m_obj(&obj), m_section_type(toString(section_type)), - m_next_section_idx(0) { + m_next_section_idx(0), m_next_line(llvm::StringRef::npos) { ++*this; } + // An iterator starting at the position given by the bookmark. + LineIterator(ObjectFile &obj, Record::Kind section_type, Bookmark bookmark); + // end iterator explicit LineIterator(ObjectFile &obj) : m_obj(&obj), - m_next_section_idx(m_obj->GetSectionList()->GetNumSections(0)) {} + m_next_section_idx(m_obj->GetSectionList()->GetNumSections(0)), + m_current_line(llvm::StringRef::npos), + m_next_line(llvm::StringRef::npos) {} friend bool operator!=(const LineIterator &lhs, const LineIterator &rhs) { assert(lhs.m_obj == rhs.m_obj); if (lhs.m_next_section_idx != rhs.m_next_section_idx) return true; - if (lhs.m_next_text.data() != rhs.m_next_text.data()) + if (lhs.m_current_line != rhs.m_current_line) return true; - assert(lhs.m_current_text == rhs.m_current_text); - assert(rhs.m_next_text == rhs.m_next_text); + assert(lhs.m_next_line == rhs.m_next_line); return false; } const LineIterator &operator++(); - llvm::StringRef operator*() const { return m_current_text; } + llvm::StringRef operator*() const { + return m_section_text.slice(m_current_line, m_next_line); + } + + Bookmark GetBookmark() const { + return Bookmark{m_next_section_idx, m_current_line}; + } private: ObjectFile *m_obj; ConstString m_section_type; uint32_t m_next_section_idx; - llvm::StringRef m_current_text; - llvm::StringRef m_next_text; + llvm::StringRef m_section_text; + size_t m_current_line; + size_t m_next_line; + + void FindNextLine() { + m_next_line = m_section_text.find('\n', m_current_line); + if (m_next_line != llvm::StringRef::npos) { + ++m_next_line; + if (m_next_line >= m_section_text.size()) + m_next_line = llvm::StringRef::npos; + } + } }; -} // namespace -const LineIterator &LineIterator::operator++() { +SymbolFileBreakpad::LineIterator::LineIterator(ObjectFile &obj, + Record::Kind section_type, + Bookmark bookmark) + : m_obj(&obj), m_section_type(toString(section_type)), + m_next_section_idx(bookmark.section), m_current_line(bookmark.offset) { + Section § = + *obj.GetSectionList()->GetSectionAtIndex(m_next_section_idx - 1); + assert(sect.GetName() == m_section_type); + + DataExtractor data; + obj.ReadSectionData(§, data); + m_section_text = toStringRef(data.GetData()); + + assert(m_current_line < m_section_text.size()); + FindNextLine(); +} + +const SymbolFileBreakpad::LineIterator & +SymbolFileBreakpad::LineIterator::operator++() { const SectionList &list = *m_obj->GetSectionList(); size_t num_sections = list.GetNumSections(0); - while (m_next_text.empty() && m_next_section_idx < num_sections) { + while (m_next_line != llvm::StringRef::npos || + m_next_section_idx < num_sections) { + if (m_next_line != llvm::StringRef::npos) { + m_current_line = m_next_line; + FindNextLine(); + return *this; + } + Section § = *list.GetSectionAtIndex(m_next_section_idx++); if (sect.GetName() != m_section_type) continue; DataExtractor data; m_obj->ReadSectionData(§, data); - m_next_text = - llvm::StringRef(reinterpret_cast(data.GetDataStart()), - data.GetByteSize()); + m_section_text = toStringRef(data.GetData()); + m_next_line = 0; } - std::tie(m_current_text, m_next_text) = m_next_text.split('\n'); + // We've reached the end. + m_current_line = m_next_line; return *this; } -static llvm::iterator_range lines(ObjectFile &obj, - Record::Kind section_type) { - return llvm::make_range(LineIterator(obj, section_type), LineIterator(obj)); +llvm::iterator_range +SymbolFileBreakpad::lines(Record::Kind section_type) { + return llvm::make_range(LineIterator(*m_obj_file, section_type), + LineIterator(*m_obj_file)); +} + +namespace { +// A helper class for constructing the list of support files for a given compile +// unit. +class SupportFileMap { +public: + // Given a breakpad file ID, return a file ID to be used in the support files + // for this compile unit. + size_t operator[](size_t file) { + return m_map.try_emplace(file, m_map.size() + 1).first->second; + } + + // Construct a FileSpecList containing only the support files relevant for + // this compile unit (in the correct order). + FileSpecList translate(const FileSpec &cu_spec, + llvm::ArrayRef all_files); + +private: + llvm::DenseMap m_map; +}; +} // namespace + +FileSpecList SupportFileMap::translate(const FileSpec &cu_spec, + llvm::ArrayRef all_files) { + std::vector result; + result.resize(m_map.size() + 1); + result[0] = cu_spec; + for (const auto &KV : m_map) { + if (KV.first < all_files.size()) + result[KV.second] = all_files[KV.first]; + } + return FileSpecList(std::move(result)); +} + +// Construct the list of support files and line table entries for the given +// compile unit. +void SymbolFileBreakpad::CompUnitData::Parse( + ObjectFile &obj_file, llvm::ArrayRef all_files) { + addr_t base = + obj_file.GetModule()->GetObjectFile()->GetBaseAddress().GetFileAddress(); + + SupportFileMap map; + m_line_table_up = llvm::make_unique(m_comp_unit.get()); + std::unique_ptr line_seq_up( + m_line_table_up->CreateLineSequenceContainer()); + llvm::Optional next_addr; + auto finish_sequence = [&]() { + m_line_table_up->AppendLineEntryToSequence( + line_seq_up.get(), *next_addr, /*line*/ 0, /*column*/ 0, + /*file_idx*/ 0, /*is_start_of_statement*/ false, + /*is_start_of_basic_block*/ false, /*is_prologue_end*/ false, + /*is_epilogue_begin*/ false, /*is_terminal_entry*/ true); + m_line_table_up->InsertSequence(line_seq_up.get()); + line_seq_up->Clear(); + }; + + LineIterator It(obj_file, Record::Func, m_bookmark), End(obj_file); + assert(Record::classify(*It) == Record::Func); + for (++It; It != End; ++It) { + auto record = LineRecord::parse(*It); + if (!record) + break; + + record->Address += base; + + if (next_addr && *next_addr != record->Address) { + // Discontiguous entries. Finish off the previous sequence and reset. + finish_sequence(); + } + m_line_table_up->AppendLineEntryToSequence( + line_seq_up.get(), record->Address, record->LineNum, /*column*/ 0, + map[record->FileNum], /*is_start_of_statement*/ true, + /*is_start_of_basic_block*/ false, /*is_prologue_end*/ false, + /*is_epilogue_begin*/ false, /*is_terminal_entry*/ false); + next_addr = record->Address + record->Size; + } + if (next_addr) + finish_sequence(); + m_support_files = map.translate(*m_comp_unit, all_files); +} + +FileSpecList SymbolFileBreakpad::CompUnitData::ReleaseSupportFiles( + ObjectFile &obj_file, llvm::ArrayRef all_files) { + if (!m_support_files) + Parse(obj_file, all_files); + return std::move(*m_support_files); +} + +std::unique_ptr SymbolFileBreakpad::CompUnitData::ReleaseLineTable( + ObjectFile &obj_file, llvm::ArrayRef all_files) { + if (!m_line_table_up) + Parse(obj_file, all_files); + return std::move(m_line_table_up); } void SymbolFileBreakpad::Initialize() { @@ -103,17 +243,78 @@ if (m_obj_file->GetPluginName() != ObjectFileBreakpad::GetPluginNameStatic()) return 0; - return CompileUnits | Functions; + return CompileUnits | Functions | LineTables; +} + +void SymbolFileBreakpad::InitializeObject() { + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); + addr_t base = GetBaseObjectFile().GetBaseAddress().GetFileAddress(); + if (base == LLDB_INVALID_ADDRESS) { + LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address " + "of object file."); + return; + } + + // Parse out all the FILE records from the breakpad file. These will be needed + // when constructing the support file lists for individual compile units. + for (llvm::StringRef line : lines(Record::File)) { + auto record = FileRecord::parse(line); + if (!record) { + LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line); + continue; + } + + if (record->Number >= m_files.size()) + m_files.resize(record->Number + 1); + m_files[record->Number] = FileSpec(record->Name); + } + + // Create the list of compile units. We create a compile unit for each FUNC + // record which has at least one line entry. The name of the compile unit is + // the name of the first line entry. We store the compile units in a map + // indexed by their ranges to enable easy lookup when resolving symbol + // contexts by address. + llvm::Optional id; + llvm::Optional func; + for (LineIterator It(*m_obj_file, Record::Func), End(*m_obj_file); It != End; + ++It) { + if (auto record = FuncRecord::parse(*It)) { + id = It.GetBookmark(); + func = record; + } else if (!id) + continue; + else if (auto record = LineRecord::parse(*It)) { + FileSpec spec; + if (record->FileNum <= m_files.size()) + spec = FileSpec(m_files[record->FileNum]); + m_comp_units.Append(CompUnitMap::Entry( + base + func->Address, func->Size, + CompUnitData(std::make_shared( + m_obj_file->GetModule(), /*user_data*/ nullptr, spec, + /*uid*/ 0, eLanguageTypeUnknown, + /*is_optimized*/ eLazyBoolNo), + *id))); + id = llvm::None; + func = llvm::None; + } else + LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It); + } + m_comp_units.Sort(); + + // Set back pointers in the CompileUnit objects, so we can look up the compile + // unit data when given a CompileUnit. + for (size_t i = 0; i < m_comp_units.GetSize(); ++i) + m_comp_units.GetEntryRef(i).data.GetCompUnit()->SetID(i); } uint32_t SymbolFileBreakpad::GetNumCompileUnits() { - // TODO - return 0; + return m_comp_units.GetSize(); } CompUnitSP SymbolFileBreakpad::ParseCompileUnitAtIndex(uint32_t index) { - // TODO - return nullptr; + CompUnitSP cu = m_comp_units.GetEntryRef(index).data.GetCompUnit(); + m_obj_file->GetModule()->GetSymbolVendor()->SetCompileUnitAtIndex(index, cu); + return cu; } size_t SymbolFileBreakpad::ParseFunctions(CompileUnit &comp_unit) { @@ -122,16 +323,53 @@ } bool SymbolFileBreakpad::ParseLineTable(CompileUnit &comp_unit) { - // TODO - return 0; + CompUnitData &data = m_comp_units.GetEntryRef(comp_unit.GetID()).data; + comp_unit.SetLineTable(data.ReleaseLineTable(*m_obj_file, m_files).release()); + return true; +} +bool SymbolFileBreakpad::ParseSupportFiles(CompileUnit &comp_unit, + FileSpecList &support_files) { + CompUnitData &data = m_comp_units.GetEntryRef(comp_unit.GetID()).data; + support_files = data.ReleaseSupportFiles(*m_obj_file, m_files); + return true; } uint32_t SymbolFileBreakpad::ResolveSymbolContext(const Address &so_addr, SymbolContextItem resolve_scope, SymbolContext &sc) { - // TODO - return 0; + if (!(resolve_scope & (eSymbolContextCompUnit | eSymbolContextLineEntry))) + return 0; + const CompUnitMap::Entry *entry = + m_comp_units.FindEntryThatContains(so_addr.GetFileAddress()); + if (!entry) + return 0; + + sc.comp_unit = entry->data.GetCompUnit().get(); + SymbolContextItem result = eSymbolContextCompUnit; + if (resolve_scope & eSymbolContextLineEntry) { + if (sc.comp_unit->GetLineTable()->FindLineEntryByAddress(so_addr, + sc.line_entry)) { + result |= eSymbolContextLineEntry; + } + } + + return result; +} + +uint32_t SymbolFileBreakpad::ResolveSymbolContext( + const FileSpec &file_spec, uint32_t line, bool check_inlines, + lldb::SymbolContextItem resolve_scope, SymbolContextList &sc_list) { + if (!(resolve_scope & eSymbolContextCompUnit)) + return 0; + + uint32_t old_size = sc_list.GetSize(); + for (size_t i = 0; i < m_comp_units.GetSize(); ++i) { + CompileUnit &cu = *m_comp_units.GetEntryRef(i).data.GetCompUnit(); + cu.ResolveSymbolContext(file_spec, line, check_inlines, + /*exact*/ false, resolve_scope, sc_list); + } + return sc_list.GetSize() - old_size; } uint32_t SymbolFileBreakpad::FindFunctions( @@ -173,7 +411,7 @@ void SymbolFileBreakpad::AddSymbols(Symtab &symtab) { Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); Module &module = *m_obj_file->GetModule(); - addr_t base = module.GetObjectFile()->GetBaseAddress().GetFileAddress(); + addr_t base = GetBaseObjectFile().GetBaseAddress().GetFileAddress(); if (base == LLDB_INVALID_ADDRESS) { LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping " "symtab population."); @@ -202,12 +440,12 @@ size.hasValue(), /*contains_linker_annotations*/ false, /*flags*/ 0); }; - for (llvm::StringRef line : lines(*m_obj_file, Record::Func)) { + for (llvm::StringRef line : lines(Record::Func)) { if (auto record = FuncRecord::parse(line)) add_symbol(record->Address, record->Size, record->Name); } - for (llvm::StringRef line : lines(*m_obj_file, Record::Public)) { + for (llvm::StringRef line : lines(Record::Public)) { if (auto record = PublicRecord::parse(line)) add_symbol(record->Address, llvm::None, record->Name); else @@ -218,3 +456,7 @@ symtab.AddSymbol(std::move(KV.second)); symtab.CalculateSymbolSizes(); } + +ObjectFile &SymbolFileBreakpad::GetBaseObjectFile() { + return *m_obj_file->GetModule()->GetObjectFile(); +}