Cache uwnind frame headers as they are found.

Summary:
This improves unwind performance quite substantially, and follows
a somewhat similar approach used in libgcc_s as described in the
thread here:

https://gcc.gnu.org/ml/gcc/2005-02/msg00625.html

On certain extremely exception heavy internal tests, the time
drops from about 80 minutes to about five minutes.

Subscribers: libcxx-commits

Tags: #libc

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

Cr-Mirrored-From: https://chromium.googlesource.com/external/github.com/llvm/llvm-project
Cr-Mirrored-Commit: c53c2058ffb8ff877702bb2dded31c85c1dfe66d
diff --git a/test/frameheadercache_test.pass.cpp b/test/frameheadercache_test.pass.cpp
new file mode 100644
index 0000000..ac75f7d
--- /dev/null
+++ b/test/frameheadercache_test.pass.cpp
@@ -0,0 +1,82 @@
+// The other libunwind tests don't test internal interfaces, so the include path
+// is a little wonky.
+#include "../src/config.h"
+
+// Only run this test under supported configurations.
+// This #if chain is ugly, but see the comments in AddressSpace.hpp for
+// the reasoning.
+
+#ifdef __APPLE__
+int main() { return 0; }
+#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL)
+int main() { return 0; }
+#elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL)
+int main() { return 0; }
+#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_WIN32)
+int main() { return 0; }
+#elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32)
+int main() { return 0; }
+#elif defined(_LIBUNWIND_ARM_EHABI) && defined(__BIONIC__)
+int main() { return 0; }
+#elif defined(_LIBUNWIND_ARM_EHABI) || defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
+
+#include <link.h>
+#include <stdio.h>
+
+// This file defines several of the data structures needed here,
+// and includes FrameHeaderCache.hpp as well.
+#include "../src/AddressSpace.hpp"
+
+#define kBaseAddr 0xFFF000
+#define kDwarfSectionLength 0xFF
+
+using namespace libunwind;
+
+int main() {
+  FrameHeaderCache FHC;
+  struct dl_phdr_info PInfo;
+  memset(&PInfo, 0, sizeof(PInfo));
+  // The cache itself should only care about these two fields--they
+  // tell the cache to invalidate or not; everything else is handled
+  // by AddressSpace.hpp.
+  PInfo.dlpi_adds = 6;
+  PInfo.dlpi_subs = 7;
+
+  UnwindInfoSections UIS;
+  UIS.dso_base = kBaseAddr;
+  UIS.dwarf_section_length = kDwarfSectionLength;
+  dl_iterate_cb_data CBData;
+  // Unused by the cache.
+  CBData.addressSpace = nullptr;
+  CBData.sects = &UIS;
+  CBData.targetAddr = kBaseAddr + 1;
+
+  // Nothing present, shouldn't find.
+  if (FHC.find(&PInfo, 0, &CBData))
+    abort();
+  FHC.add(&UIS);
+  // Just added. Should find.
+  if (!FHC.find(&PInfo, 0, &CBData))
+    abort();
+  // Cache is invalid. Shouldn't find.
+  PInfo.dlpi_adds++;
+  if (FHC.find(&PInfo, 0, &CBData))
+    abort();
+
+  FHC.add(&UIS);
+  CBData.targetAddr = kBaseAddr - 1;
+  // Shouldn't find something outside of the addresses.
+  if (FHC.find(&PInfo, 0, &CBData))
+    abort();
+  // Add enough things to the cache that the entry is evicted.
+  for (int i = 0; i < 9; i++) {
+    UIS.dso_base = kBaseAddr + (kDwarfSectionLength * i);
+    FHC.add(&UIS);
+  }
+  CBData.targetAddr = kBaseAddr;
+  // Should have been evicted.
+  if (FHC.find(&PInfo, 0, &CBData))
+    abort();
+  return 0;
+}
+#endif