labath updated this revision to Diff 184103.
labath added a comment.

It took me a while to get back to this, but I believe this version now does what
we want. It creates a compile unit for each function. The compile units are
created as soon as the symbol file is initialized (it's needed to get the number
of compile units). The global list of support files is also parsed at this time
(needed to get the compile unit name). The list of support files and line tables
is created lazily for those compile units that need them.

A new element here is the "bookmark" class, which allows us to efficiently go
back the to place where the line tables for a given function/compile unit are
defined and parse those.

I added a couple of new tests to exercise handling of odd or broken minidump
files.

Let me know what you think.


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 {
@@ -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<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(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<FileSpec> all_files);
+    std::unique_ptr<LineTable>
+    ReleaseLineTable(ObjectFile &obj_file, llvm::ArrayRef<FileSpec> all_files);
+
+  private:
+    lldb::CompUnitSP m_comp_unit;
+    Bookmark m_bookmark;
+    llvm::Optional<FileSpecList> m_support_files;
+    std::unique_ptr<LineTable> m_line_table_up;
+
+    void Parse(ObjectFile &obj_file, llvm::ArrayRef<FileSpec> all_files);
+  };
+
+  ObjectFile &GetBaseObjectFile();
+
+  using CompUnitMap = RangeDataVector<lldb::addr_t, lldb::addr_t, CompUnitData>;
+
+  std::vector<FileSpec> 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 &sect =
+      *obj.GetSectionList()->GetSectionAtIndex(m_next_section_idx - 1);
+  assert(sect.GetName() == m_section_type);
+
+  DataExtractor data;
+  obj.ReadSectionData(&sect, 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 &sect = *list.GetSectionAtIndex(m_next_section_idx++);
     if (sect.GetName() != m_section_type)
       continue;
     DataExtractor data;
     m_obj->ReadSectionData(&sect, 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));
+}
+
+// Construct the list of support files and line table entries for the given
+// compile unit.
+void SymbolFileBreakpad::CompUnitData::Parse(
+    ObjectFile &obj_file, llvm::ArrayRef<FileSpec> all_files) {
+  addr_t base =
+      obj_file.GetModule()->GetObjectFile()->GetBaseAddress().GetFileAddress();
+
+  SupportFileMap map;
+  m_line_table_up = llvm::make_unique<LineTable>(m_comp_unit.get());
+  std::unique_ptr<LineSequence> line_seq_up(
+      m_line_table_up->CreateLineSequenceContainer());
+  llvm::Optional<addr_t> 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<FileSpec> all_files) {
+  if (!m_support_files)
+    Parse(obj_file, all_files);
+  return std::move(*m_support_files);
+}
+
+std::unique_ptr<LineTable> SymbolFileBreakpad::CompUnitData::ReleaseLineTable(
+    ObjectFile &obj_file, llvm::ArrayRef<FileSpec> 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<Bookmark> id;
+  llvm::Optional<FuncRecord> 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<CompileUnit>(
+                           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();
+}
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: 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
@@ -47,6 +47,9 @@
   //------------------------------------------------------------------
   FileSpecList(const FileSpecList &rhs);
 
+  /// Initialize this object from a vector of FileSpecs
+  FileSpecList(std::vector<FileSpec> &&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.
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to