labath updated this revision to Diff 185533. labath marked 2 inline comments as done. labath added a comment.
Tried to make parsing as lazy as possible. GetNumSections() will count the number of FUNC records, but will not create CompileUnit objects. FILE records will be parsed when we create the first compile unit. The support files and line tables will be parsed when the first of them is requested for a given CU. I've removed the CU shared pointer from the CompUnitData structure. Instead, I go through the symbol vendor to fetch the CU SP. Added the move constructor for FileSpecList. Please take another look. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D56595/new/ https://reviews.llvm.org/D56595 Files: include/lldb/Core/FileSpecList.h include/lldb/Core/RangeMap.h lit/SymbolFile/Breakpad/Inputs/line-table-discontinuous-file-ids.syms lit/SymbolFile/Breakpad/Inputs/line-table-edgecases.syms lit/SymbolFile/Breakpad/Inputs/line-table-missing-file.syms lit/SymbolFile/Breakpad/Inputs/line-table.syms lit/SymbolFile/Breakpad/line-table-discontinuous-file-ids.test lit/SymbolFile/Breakpad/line-table-edgecases.test lit/SymbolFile/Breakpad/line-table-missing-file.test lit/SymbolFile/Breakpad/line-table.test source/Core/FileSpecList.cpp source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
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 { @@ -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 @@ -98,6 +99,11 @@ 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 +143,66 @@ 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<LineIterator> 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(Bookmark bookmark) : bookmark(bookmark) {} + + CompUnitData() = default; + CompUnitData(const CompUnitData &rhs) : bookmark(rhs.bookmark) {} + CompUnitData &operator=(const CompUnitData &rhs) { + bookmark = rhs.bookmark; + support_files.reset(); + line_table_up.reset(); + return *this; + } + friend bool operator<(const CompUnitData &lhs, const CompUnitData &rhs) { + return std::tie(lhs.bookmark.section, lhs.bookmark.offset) < + std::tie(rhs.bookmark.section, rhs.bookmark.offset); + } + + Bookmark bookmark; + llvm::Optional<FileSpecList> support_files; + std::unique_ptr<LineTable> line_table_up; + + }; + + SymbolVendor &GetSymbolVendor(); + lldb::addr_t GetBaseFileAddress(); + void ParseFileRecords(); + void ParseCUData(); + void ParseLineTableAndSupportFiles(CompileUnit &cu, CompUnitData &data); + + using CompUnitMap = RangeDataVector<lldb::addr_t, lldb::addr_t, CompUnitData>; + + llvm::Optional<std::vector<FileSpec>> m_files; + llvm::Optional<CompUnitMap> m_cu_data; }; } // 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,141 @@ 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<const char *>(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<LineIterator> lines(ObjectFile &obj, - Record::Kind section_type) { - return llvm::make_range(LineIterator(obj, section_type), LineIterator(obj)); +llvm::iterator_range<SymbolFileBreakpad::LineIterator> +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<FileSpec> all_files); + +private: + llvm::DenseMap<size_t, size_t> m_map; +}; +} // namespace + +FileSpecList SupportFileMap::translate(const FileSpec &cu_spec, + llvm::ArrayRef<FileSpec> all_files) { + std::vector<FileSpec> 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)); } void SymbolFileBreakpad::Initialize() { @@ -103,17 +182,39 @@ if (m_obj_file->GetPluginName() != ObjectFileBreakpad::GetPluginNameStatic()) return 0; - return CompileUnits | Functions; + return CompileUnits | Functions | LineTables; } uint32_t SymbolFileBreakpad::GetNumCompileUnits() { - // TODO - return 0; + ParseCUData(); + return m_cu_data->GetSize(); } CompUnitSP SymbolFileBreakpad::ParseCompileUnitAtIndex(uint32_t index) { - // TODO - return nullptr; + ParseFileRecords(); + + CompUnitData &data = m_cu_data->GetEntryRef(index).data; + + FileSpec spec; + + // The FileSpec of the compile unit will be the file corresponding to the + // first LINE record. + LineIterator It(*m_obj_file, Record::Func, data.bookmark), End(*m_obj_file); + assert(Record::classify(*It) == Record::Func); + ++It; // Skip FUNC record. + if (It != End) { + auto record = LineRecord::parse(*It); + if (record && record->FileNum < m_files->size()) + spec = (*m_files)[record->FileNum]; + } + + auto cu_sp = std::make_shared<CompileUnit>(m_obj_file->GetModule(), + /*user_data*/ nullptr, spec, index, + eLanguageTypeUnknown, + /*is_optimized*/ eLazyBoolNo); + + GetSymbolVendor().SetCompileUnitAtIndex(index, cu_sp); + return cu_sp; } size_t SymbolFileBreakpad::ParseFunctions(CompileUnit &comp_unit) { @@ -122,16 +223,63 @@ } bool SymbolFileBreakpad::ParseLineTable(CompileUnit &comp_unit) { - // TODO - return 0; + CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data; + + if (!data.line_table_up) + ParseLineTableAndSupportFiles(comp_unit, data); + + comp_unit.SetLineTable(data.line_table_up.release()); + return true; +} + +bool SymbolFileBreakpad::ParseSupportFiles(CompileUnit &comp_unit, + FileSpecList &support_files) { + CompUnitData &data = m_cu_data->GetEntryRef(comp_unit.GetID()).data; + if (!data.support_files) + ParseLineTableAndSupportFiles(comp_unit, data); + + support_files = std::move(*data.support_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; + + ParseCUData(); + uint32_t idx = + m_cu_data->FindEntryIndexThatContains(so_addr.GetFileAddress()); + if (idx == UINT32_MAX) + return 0; + + sc.comp_unit = GetSymbolVendor().GetCompileUnitAtIndex(idx).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, size = GetNumCompileUnits(); i < size; ++i) { + CompileUnit &cu = *GetSymbolVendor().GetCompileUnitAtIndex(i); + 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 +321,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 = GetBaseFileAddress(); if (base == LLDB_INVALID_ADDRESS) { LLDB_LOG(log, "Unable to fetch the base address of object file. Skipping " "symtab population."); @@ -202,12 +350,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 +366,107 @@ symtab.AddSymbol(std::move(KV.second)); symtab.CalculateSymbolSizes(); } + +SymbolVendor &SymbolFileBreakpad::GetSymbolVendor() { + return *m_obj_file->GetModule()->GetSymbolVendor(); +} + +addr_t SymbolFileBreakpad::GetBaseFileAddress() { + return m_obj_file->GetModule() + ->GetObjectFile() + ->GetBaseAddress() + .GetFileAddress(); +} + +// Parse out all the FILE records from the breakpad file. These will be needed +// when constructing the support file lists for individual compile units. +void SymbolFileBreakpad::ParseFileRecords() { + if (m_files) + return; + m_files.emplace(); + + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); + 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); + } +} + +void SymbolFileBreakpad::ParseCUData() { + if (m_cu_data) + return; + + m_cu_data.emplace(); + Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS); + addr_t base = GetBaseFileAddress(); + if (base == LLDB_INVALID_ADDRESS) { + LLDB_LOG(log, "SymbolFile parsing failed: Unable to fetch the base address " + "of object file."); + } + + // We shall create one compile unit for each FUNC record. So, count the number + // of FUNC records, and store them in m_cu_data, together with their ranges. + for (LineIterator It(*m_obj_file, Record::Func), End(*m_obj_file); It != End; + ++It) { + if (auto record = FuncRecord::parse(*It)) { + m_cu_data->Append(CompUnitMap::Entry(base + record->Address, record->Size, + CompUnitData(It.GetBookmark()))); + } else + LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", *It); + } + m_cu_data->Sort(); +} + +// Construct the list of support files and line table entries for the given +// compile unit. +void SymbolFileBreakpad::ParseLineTableAndSupportFiles(CompileUnit &cu, CompUnitData &data) { + addr_t base = GetBaseFileAddress(); + assert(base != LLDB_INVALID_ADDRESS && + "How did we create compile units without a base address?"); + + SupportFileMap map; + data.line_table_up = llvm::make_unique<LineTable>(&cu); + std::unique_ptr<LineSequence> line_seq_up( + data.line_table_up->CreateLineSequenceContainer()); + llvm::Optional<addr_t> next_addr; + auto finish_sequence = [&]() { + data.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); + data.line_table_up->InsertSequence(line_seq_up.get()); + line_seq_up->Clear(); + }; + + LineIterator It(*m_obj_file, Record::Func, data.bookmark), End(*m_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(); + } + data.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(); + data.support_files = map.translate(cu, *m_files); +} Index: source/Core/FileSpecList.cpp =================================================================== --- source/Core/FileSpecList.cpp +++ source/Core/FileSpecList.cpp @@ -20,19 +20,8 @@ FileSpecList::FileSpecList() : m_files() {} -FileSpecList::FileSpecList(const FileSpecList &rhs) = default; - 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: 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: 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-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-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/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/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-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-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: 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: include/lldb/Core/FileSpecList.h =================================================================== --- include/lldb/Core/FileSpecList.h +++ include/lldb/Core/FileSpecList.h @@ -37,15 +37,14 @@ //------------------------------------------------------------------ FileSpecList(); - //------------------------------------------------------------------ /// Copy constructor. - /// - /// Initialize this object with a copy of the file list from \a rhs. - /// - /// @param[in] rhs - /// A const reference to another file list object. - //------------------------------------------------------------------ - FileSpecList(const FileSpecList &rhs); + FileSpecList(const FileSpecList &rhs) = default; + + /// Move constructor + FileSpecList(FileSpecList &&rhs) = default; + + /// Initialize this object from a vector of FileSpecs + FileSpecList(std::vector<FileSpec> &&rhs) : m_files(std::move(rhs)) {} //------------------------------------------------------------------ /// Destructor. @@ -63,7 +62,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.
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits