Speed up accelerator table lookups
authorAdrian Prantl <aprantl@apple.com>
Thu, 10 Oct 2019 17:59:15 +0000 (17:59 +0000)
committerAdrian Prantl <aprantl@apple.com>
Thu, 10 Oct 2019 17:59:15 +0000 (17:59 +0000)
When debugging a large program like clang and doing "frame variable
*this", the ValueObject pretty printer is doing hundreds of scoped
FindTypes lookups. The ones that take longest are the ones where the
DWARFDeclContext ends in something like ::Iterator which produces many
false positives that need to be filtered out *after* extracting the
DIEs. This patch demonstrates a way to filter out false positives at
the accerator table lookup step.

With this patch
  lldb clang-10 -o "b EmitFunctionStart" -o r -o "f 2" -o "fr v *this" -b -- ...
goes (in user time) from 5.6s -> 4.8s
or (in wall clock) from 6.9s -> 6.0s.

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

llvm-svn: 374401

12 files changed:
lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/TestCPPAccelerator.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/a.cpp [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/b.cpp [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/c.cpp [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/d.cpp [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/e.cpp [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/f.cpp [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/g.cpp [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/main.cpp [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/source.h [new file with mode: 0644]
lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp

diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/Makefile b/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/Makefile
new file mode 100644 (file)
index 0000000..4c053a0
--- /dev/null
@@ -0,0 +1,7 @@
+# There is no guaranteed order in which the linker will order these
+# files, so we just have a lot of them to make it unlikely that we hit
+# the right one first by pure luck.
+
+CXX_SOURCES := main.cpp a.cpp b.cpp c.cpp d.cpp e.cpp f.cpp g.cpp
+
+include Makefile.rules
diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/TestCPPAccelerator.py b/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/TestCPPAccelerator.py
new file mode 100644 (file)
index 0000000..3705e95
--- /dev/null
@@ -0,0 +1,31 @@
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+class CPPAcceleratorTableTestCase(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+
+    @skipUnlessDarwin
+    @skipIf(debug_info=no_match(["dwarf"]))
+    def test(self):
+        """Test that type lookups fail early (performance)"""
+        self.build()
+        logfile = self.getBuildArtifact('dwarf.log')
+        self.expect('log enable dwarf lookups -f' + logfile)
+        target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
+            self, 'break here', lldb.SBFileSpec('main.cpp'))
+        # Pick one from the middle of the list to have a high chance
+        # of it not being in the first file looked at.
+        self.expect('frame variable inner_d')
+
+        log = open(logfile, 'r')
+        n = 0
+        for line in log:
+            if re.findall(r'[abcdefg]\.o: FindByNameAndTag\(\)', line):
+                self.assertTrue("d.o" in line)
+                n += 1
+
+        self.assertEqual(n, 1, log)
diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/a.cpp b/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/a.cpp
new file mode 100644 (file)
index 0000000..d9f758e
--- /dev/null
@@ -0,0 +1,2 @@
+#include "source.h"
+CLASS(A)
diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/b.cpp b/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/b.cpp
new file mode 100644 (file)
index 0000000..a0cdffa
--- /dev/null
@@ -0,0 +1,2 @@
+#include "source.h"
+CLASS(B)
diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/c.cpp b/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/c.cpp
new file mode 100644 (file)
index 0000000..1bd7172
--- /dev/null
@@ -0,0 +1,2 @@
+#include "source.h"
+CLASS(C)
diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/d.cpp b/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/d.cpp
new file mode 100644 (file)
index 0000000..e43c2ad
--- /dev/null
@@ -0,0 +1,2 @@
+#include "source.h"
+CLASS(D)
diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/e.cpp b/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/e.cpp
new file mode 100644 (file)
index 0000000..a3008f7
--- /dev/null
@@ -0,0 +1,2 @@
+#include "source.h"
+CLASS(E)
diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/f.cpp b/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/f.cpp
new file mode 100644 (file)
index 0000000..77df296
--- /dev/null
@@ -0,0 +1,2 @@
+#include "source.h"
+CLASS(F)
diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/g.cpp b/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/g.cpp
new file mode 100644 (file)
index 0000000..e144691
--- /dev/null
@@ -0,0 +1,2 @@
+#include "source.h"
+CLASS(G)
diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/main.cpp b/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/main.cpp
new file mode 100644 (file)
index 0000000..b7eb252
--- /dev/null
@@ -0,0 +1,28 @@
+#define CLASS(NAME)                             \
+  class NAME {                                  \
+  public:                                       \
+    struct Inner;                               \
+    Inner *i = nullptr;                         \
+  };                                            \
+NAME::Inner &getInner##NAME();
+
+CLASS(A)
+CLASS(B)
+CLASS(C)
+CLASS(D)
+CLASS(E)
+CLASS(F)
+CLASS(G)
+
+int main()
+{
+  A::Inner &inner_a = getInnerA();
+  B::Inner &inner_b = getInnerB();
+  C::Inner &inner_c = getInnerC();
+  D::Inner &inner_d = getInnerD();
+  E::Inner &inner_e = getInnerE();
+  F::Inner &inner_f = getInnerF();
+  G::Inner &inner_g = getInnerG();
+
+  return 0; // break here
+}
diff --git a/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/source.h b/lldb/packages/Python/lldbsuite/test/lang/cpp/accelerator-table/source.h
new file mode 100644 (file)
index 0000000..214e7da
--- /dev/null
@@ -0,0 +1,12 @@
+#define CLASS(NAME)                             \
+  class NAME {                                  \
+    public:                                     \
+    class Inner {                               \
+      int j = #NAME[0];                         \
+    };                                          \
+    Inner *i = nullptr;                         \
+  };                                            \
+                                                \
+  static NAME::Inner inner;                     \
+  static NAME obj;                              \
+  NAME::Inner &getInner##NAME() { return inner; }
index 97dbd6c..0a5073b 100644 (file)
@@ -110,6 +110,7 @@ void AppleDWARFIndex::GetTypes(const DWARFDeclContext &context,
   const bool has_qualified_name_hash =
       m_apple_types_up->GetHeader().header_data.ContainsAtom(
           DWARFMappedHash::eAtomTypeQualNameHash);
+
   const ConstString type_name(context[0].name);
   const dw_tag_t tag = context[0].tag;
   if (has_tag && has_qualified_name_hash) {
@@ -119,12 +120,32 @@ void AppleDWARFIndex::GetTypes(const DWARFDeclContext &context,
       m_module.LogMessage(log, "FindByNameAndTagAndQualifiedNameHash()");
     m_apple_types_up->FindByNameAndTagAndQualifiedNameHash(
         type_name.GetStringRef(), tag, qualified_name_hash, offsets);
-  } else if (has_tag) {
+    return;
+  }
+
+  if (has_tag) {
+    // When searching for a scoped type (for example,
+    // "std::vector<int>::const_iterator") searching for the innermost
+    // name alone ("const_iterator") could yield many false
+    // positives. By searching for the parent type ("vector<int>")
+    // first we can avoid extracting type DIEs from object files that
+    // would fail the filter anyway.
+    if (!has_qualified_name_hash && (context.GetSize() > 1) &&
+        (context[1].tag == DW_TAG_class_type ||
+         context[1].tag == DW_TAG_structure_type)) {
+      DIEArray class_matches;
+      m_apple_types_up->FindByName(context[1].name, class_matches);
+      if (class_matches.empty())
+        return;
+    }
+
     if (log)
       m_module.LogMessage(log, "FindByNameAndTag()");
     m_apple_types_up->FindByNameAndTag(type_name.GetStringRef(), tag, offsets);
-  } else
-    m_apple_types_up->FindByName(type_name.GetStringRef(), offsets);
+    return;
+  }
+
+  m_apple_types_up->FindByName(type_name.GetStringRef(), offsets);
 }
 
 void AppleDWARFIndex::GetNamespaces(ConstString name, DIEArray &offsets) {