[Lldb-commits] [lldb] r353404 - SymbolFileBreakpad: Add line table support

Pavel Labath via lldb-commits lldb-commits at lists.llvm.org
Thu Feb 7 05:42:32 PST 2019


Author: labath
Date: Thu Feb  7 05:42:32 2019
New Revision: 353404

URL: http://llvm.org/viewvc/llvm-project?rev=353404&view=rev
Log:
SymbolFileBreakpad: Add line table support

Summary:
This patch teaches SymbolFileBreakpad to parse the line information in
breakpad files and present it to lldb.

The trickiest question here was what kind of "compile units" to present
to lldb, as there really isn't enough information in breakpad files to
correctly reconstruct those.

A couple of options were considered
- have the entire file be one compile unit
- have one compile unit for each FILE record
- have one compile unit for each FUNC record

The main drawback of the first approach is that all of the files would
be considered "headers" by lldb, and so they wouldn't be searched if
target.inline-breakpoint-strategy=never. The single compile unit would
also be huge, and there isn't a good way to name it.

The second approach will create mostly correct compile units for cpp
files, but it will still be wrong for headers. However, the biggest
drawback here seemed to be the fact that this can cause a compile unit
to change mid-function (for example when a function from another file is
inlined or another file is #included into a function). While I don't
know of any specific thing that would break in this case, it does sound
like a thing that we should avoid.

In the end, we chose the third option, as it didn't seem to have any
major disadvantages, though it was not ideal either. One disadvantage
here is that this generates a large number of compile units, and there
is still a question on how to name it. We chose to simply name it after
the first line record in that function. This should be correct 99.99% of
the time, though it can produce somewhat strange results if the very
first line record comes from an #included file.

Reviewers: clayborg, zturner, lemo, markmentovai

Subscribers: lldb-commits

Differential Revision: https://reviews.llvm.org/D56595

Added:
    lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-discontinuous-file-ids.syms
    lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-edgecases.syms
    lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-missing-file.syms
    lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table.syms
    lldb/trunk/lit/SymbolFile/Breakpad/line-table-discontinuous-file-ids.test
    lldb/trunk/lit/SymbolFile/Breakpad/line-table-edgecases.test
    lldb/trunk/lit/SymbolFile/Breakpad/line-table-missing-file.test
    lldb/trunk/lit/SymbolFile/Breakpad/line-table.test
Modified:
    lldb/trunk/include/lldb/Core/FileSpecList.h
    lldb/trunk/include/lldb/Core/RangeMap.h
    lldb/trunk/source/Core/FileSpecList.cpp
    lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
    lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h

Modified: lldb/trunk/include/lldb/Core/FileSpecList.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Core/FileSpecList.h?rev=353404&r1=353403&r2=353404&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Core/FileSpecList.h (original)
+++ lldb/trunk/include/lldb/Core/FileSpecList.h Thu Feb  7 05:42:32 2019
@@ -37,15 +37,14 @@ public:
   //------------------------------------------------------------------
   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 @@ public:
   /// @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.

Modified: lldb/trunk/include/lldb/Core/RangeMap.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Core/RangeMap.h?rev=353404&r1=353403&r2=353404&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Core/RangeMap.h (original)
+++ lldb/trunk/include/lldb/Core/RangeMap.h Thu Feb  7 05:42:32 2019
@@ -715,6 +715,7 @@ public:
 
   // 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) {

Added: lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-discontinuous-file-ids.syms
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-discontinuous-file-ids.syms?rev=353404&view=auto
==============================================================================
--- lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-discontinuous-file-ids.syms (added)
+++ lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-discontinuous-file-ids.syms Thu Feb  7 05:42:32 2019
@@ -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

Added: lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-edgecases.syms
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-edgecases.syms?rev=353404&view=auto
==============================================================================
--- lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-edgecases.syms (added)
+++ lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-edgecases.syms Thu Feb  7 05:42:32 2019
@@ -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

Added: lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-missing-file.syms
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-missing-file.syms?rev=353404&view=auto
==============================================================================
--- lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-missing-file.syms (added)
+++ lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table-missing-file.syms Thu Feb  7 05:42:32 2019
@@ -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

Added: lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table.syms
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table.syms?rev=353404&view=auto
==============================================================================
--- lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table.syms (added)
+++ lldb/trunk/lit/SymbolFile/Breakpad/Inputs/line-table.syms Thu Feb  7 05:42:32 2019
@@ -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

Added: lldb/trunk/lit/SymbolFile/Breakpad/line-table-discontinuous-file-ids.test
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/SymbolFile/Breakpad/line-table-discontinuous-file-ids.test?rev=353404&view=auto
==============================================================================
--- lldb/trunk/lit/SymbolFile/Breakpad/line-table-discontinuous-file-ids.test (added)
+++ lldb/trunk/lit/SymbolFile/Breakpad/line-table-discontinuous-file-ids.test Thu Feb  7 05:42:32 2019
@@ -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: 

Added: lldb/trunk/lit/SymbolFile/Breakpad/line-table-edgecases.test
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/SymbolFile/Breakpad/line-table-edgecases.test?rev=353404&view=auto
==============================================================================
--- lldb/trunk/lit/SymbolFile/Breakpad/line-table-edgecases.test (added)
+++ lldb/trunk/lit/SymbolFile/Breakpad/line-table-edgecases.test Thu Feb  7 05:42:32 2019
@@ -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

Added: lldb/trunk/lit/SymbolFile/Breakpad/line-table-missing-file.test
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/SymbolFile/Breakpad/line-table-missing-file.test?rev=353404&view=auto
==============================================================================
--- lldb/trunk/lit/SymbolFile/Breakpad/line-table-missing-file.test (added)
+++ lldb/trunk/lit/SymbolFile/Breakpad/line-table-missing-file.test Thu Feb  7 05:42:32 2019
@@ -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: 

Added: lldb/trunk/lit/SymbolFile/Breakpad/line-table.test
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/lit/SymbolFile/Breakpad/line-table.test?rev=353404&view=auto
==============================================================================
--- lldb/trunk/lit/SymbolFile/Breakpad/line-table.test (added)
+++ lldb/trunk/lit/SymbolFile/Breakpad/line-table.test Thu Feb  7 05:42:32 2019
@@ -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

Modified: lldb/trunk/source/Core/FileSpecList.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Core/FileSpecList.cpp?rev=353404&r1=353403&r2=353404&view=diff
==============================================================================
--- lldb/trunk/source/Core/FileSpecList.cpp (original)
+++ lldb/trunk/source/Core/FileSpecList.cpp Thu Feb  7 05:42:32 2019
@@ -20,20 +20,9 @@ using namespace std;
 
 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.
 //------------------------------------------------------------------
 void FileSpecList::Append(const FileSpec &file_spec) {

Modified: lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp?rev=353404&r1=353403&r2=353404&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp (original)
+++ lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp Thu Feb  7 05:42:32 2019
@@ -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;
 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));
 }
 
 void SymbolFileBreakpad::Initialize() {
@@ -103,17 +182,42 @@ uint32_t SymbolFileBreakpad::CalculateAb
   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;
+  if (index >= m_cu_data->GetSize())
+    return nullptr;
+
+  CompUnitData &data = m_cu_data->GetEntryRef(index).data;
+
+  ParseFileRecords();
+
+  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 +226,63 @@ size_t SymbolFileBreakpad::ParseFunction
 }
 
 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 +324,7 @@ SymbolFileBreakpad::FindTypes(const std:
 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 +353,12 @@ void SymbolFileBreakpad::AddSymbols(Symt
         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 +369,108 @@ void SymbolFileBreakpad::AddSymbols(Symt
     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);
+}

Modified: lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h?rev=353404&r1=353403&r2=353404&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h (original)
+++ lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h Thu Feb  7 05:42:32 2019
@@ -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 @@ public:
   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 @@ public:
                                 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 @@ public:
   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




More information about the lldb-commits mailing list