[Lldb-commits] [lldb] [lldb] Support new libc++ __compressed_pair layout (PR #96538)

Michael Buch via lldb-commits lldb-commits at lists.llvm.org
Thu Jul 18 07:17:09 PDT 2024


https://github.com/Michael137 updated https://github.com/llvm/llvm-project/pull/96538

>From 0358143d3bf98d66c1e7112b9e07109eb7b852a1 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Mon, 29 Jan 2024 16:23:16 +0000
Subject: [PATCH 1/3] [lldb] Support new libc++ __compressed_pair layout

---
 lldb/examples/synthetic/libcxx.py             |  26 ++-
 .../Plugins/Language/CPlusPlus/LibCxx.cpp     |  85 ++++++---
 .../Plugins/Language/CPlusPlus/LibCxx.h       |   3 +-
 .../Plugins/Language/CPlusPlus/LibCxxList.cpp |  72 +++++---
 .../Plugins/Language/CPlusPlus/LibCxxMap.cpp  |  41 +++--
 .../Language/CPlusPlus/LibCxxUnorderedMap.cpp | 166 +++++++++++-------
 .../Language/CPlusPlus/LibCxxVector.cpp       |  38 ++--
 .../list/TestDataFormatterGenericList.py      |   2 +-
 8 files changed, 282 insertions(+), 151 deletions(-)

diff --git a/lldb/examples/synthetic/libcxx.py b/lldb/examples/synthetic/libcxx.py
index 474aaa428fa23..060ff90100849 100644
--- a/lldb/examples/synthetic/libcxx.py
+++ b/lldb/examples/synthetic/libcxx.py
@@ -721,6 +721,12 @@ def _get_value_of_compressed_pair(self, pair):
     def update(self):
         logger = lldb.formatters.Logger.Logger()
         try:
+            has_compressed_pair_layout = True
+            alloc_valobj = self.valobj.GetChildMemberWithName("__alloc_")
+            size_valobj = self.valobj.GetChildMemberWithName("__size_")
+            if alloc_valobj.IsValid() and size_valobj.IsValid():
+                has_compressed_pair_layout = False
+
             # A deque is effectively a two-dim array, with fixed width.
             # 'map' contains pointers to the rows of this array. The
             # full memory area allocated by the deque is delimited
@@ -734,9 +740,13 @@ def update(self):
             # variable tells which element in this NxM array is the 0th
             # one, and the 'size' element gives the number of elements
             # in the deque.
-            count = self._get_value_of_compressed_pair(
-                self.valobj.GetChildMemberWithName("__size_")
-            )
+            if has_compressed_pair_layout:
+                count = self._get_value_of_compressed_pair(
+                    self.valobj.GetChildMemberWithName("__size_")
+                )
+            else:
+                count = size_valobj.GetValueAsUnsigned(0)
+
             # give up now if we cant access memory reliably
             if self.block_size < 0:
                 logger.write("block_size < 0")
@@ -748,9 +758,13 @@ def update(self):
             self.map_begin = map_.GetChildMemberWithName("__begin_")
             map_begin = self.map_begin.GetValueAsUnsigned(0)
             map_end = map_.GetChildMemberWithName("__end_").GetValueAsUnsigned(0)
-            map_endcap = self._get_value_of_compressed_pair(
-                map_.GetChildMemberWithName("__end_cap_")
-            )
+
+            if has_compressed_pair_layout:
+                map_endcap = self._get_value_of_compressed_pair(
+                    map_.GetChildMemberWithName("__end_cap_")
+                )
+            else:
+                map_endcap = map_.GetChildMemberWithName("__end_cap_").GetValueAsUnsigned(0)
 
             # check consistency
             if not map_first <= map_begin <= map_end <= map_endcap:
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
index feaa51a96843a..7d3b2410a7296 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp
@@ -27,6 +27,7 @@
 #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h"
 #include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
 #include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
 #include <optional>
 #include <tuple>
 
@@ -34,6 +35,32 @@ using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::formatters;
 
+static void consumeInlineNamespace(llvm::StringRef &name) {
+  // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::
+  auto scratch = name;
+  if (scratch.consume_front("__") && std::isalnum(scratch[0])) {
+    scratch = scratch.drop_while([](char c) { return std::isalnum(c); });
+    if (scratch.consume_front("::")) {
+      // Successfully consumed a namespace.
+      name = scratch;
+    }
+  }
+}
+
+bool lldb_private::formatters::isOldCompressedPairLayout(
+    ValueObject &pair_obj) {
+  return isStdTemplate(pair_obj.GetTypeName(), "__compressed_pair");
+}
+
+bool lldb_private::formatters::isStdTemplate(ConstString type_name,
+                                             llvm::StringRef type) {
+  llvm::StringRef name = type_name.GetStringRef();
+  // The type name may be prefixed with `std::__<inline-namespace>::`.
+  if (name.consume_front("std::"))
+    consumeInlineNamespace(name);
+  return name.consume_front(type) && name.starts_with("<");
+}
+
 lldb::ValueObjectSP lldb_private::formatters::GetChildMemberWithName(
     ValueObject &obj, llvm::ArrayRef<ConstString> alternative_names) {
   for (ConstString name : alternative_names) {
@@ -53,7 +80,7 @@ lldb_private::formatters::GetFirstValueOfLibCXXCompressedPair(
   if (first_child)
     value = first_child->GetChildMemberWithName("__value_");
   if (!value) {
-    // pre-r300140 member name
+    // pre-c88580c member name
     value = pair.GetChildMemberWithName("__first_");
   }
   return value;
@@ -70,7 +97,7 @@ lldb_private::formatters::GetSecondValueOfLibCXXCompressedPair(
     }
   }
   if (!value) {
-    // pre-r300140 member name
+    // pre-c88580c member name
     value = pair.GetChildMemberWithName("__second_");
   }
   return value;
@@ -176,7 +203,9 @@ bool lldb_private::formatters::LibcxxUniquePointerSummaryProvider(
   if (!ptr_sp)
     return false;
 
-  ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
+  if (isOldCompressedPairLayout(*ptr_sp))
+    ptr_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
+
   if (!ptr_sp)
     return false;
 
@@ -363,13 +392,22 @@ lldb_private::formatters::LibcxxUniquePtrSyntheticFrontEnd::Update() {
 
   // Retrieve the actual pointer and the deleter, and clone them to give them
   // user-friendly names.
-  ValueObjectSP value_pointer_sp = GetFirstValueOfLibCXXCompressedPair(*ptr_sp);
-  if (value_pointer_sp)
-    m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
+  if (isOldCompressedPairLayout(*ptr_sp)) {
+    if (ValueObjectSP value_pointer_sp =
+            GetFirstValueOfLibCXXCompressedPair(*ptr_sp))
+      m_value_ptr_sp = value_pointer_sp->Clone(ConstString("pointer"));
+
+    if (ValueObjectSP deleter_sp =
+            GetSecondValueOfLibCXXCompressedPair(*ptr_sp))
+      m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
+  } else {
+    m_value_ptr_sp = ptr_sp->Clone(ConstString("pointer"));
 
-  ValueObjectSP deleter_sp = GetSecondValueOfLibCXXCompressedPair(*ptr_sp);
-  if (deleter_sp)
-    m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
+    if (ValueObjectSP deleter_sp =
+            valobj_sp->GetChildMemberWithName("__deleter_"))
+      if (deleter_sp->GetNumChildrenIgnoringErrors() > 0)
+        m_deleter_sp = deleter_sp->Clone(ConstString("deleter"));
+  }
 
   return lldb::ChildCacheState::eRefetch;
 }
@@ -407,24 +445,27 @@ namespace {
 enum class StringLayout { CSD, DSC };
 }
 
+static ValueObjectSP ExtractLibCxxStringData(ValueObject &valobj) {
+  if (auto rep_sp = valobj.GetChildMemberWithName("__rep_"))
+    return rep_sp;
+
+  ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_");
+  if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
+    return nullptr;
+
+  if (!isOldCompressedPairLayout(*valobj_r_sp))
+    return nullptr;
+
+  return GetFirstValueOfLibCXXCompressedPair(*valobj_r_sp);
+}
+
 /// Determine the size in bytes of \p valobj (a libc++ std::string object) and
 /// extract its data payload. Return the size + payload pair.
 // TODO: Support big-endian architectures.
 static std::optional<std::pair<uint64_t, ValueObjectSP>>
 ExtractLibcxxStringInfo(ValueObject &valobj) {
-  ValueObjectSP valobj_r_sp = valobj.GetChildMemberWithName("__r_");
-  if (!valobj_r_sp || !valobj_r_sp->GetError().Success())
-    return {};
-
-  // __r_ is a compressed_pair of the actual data and the allocator. The data we
-  // want is in the first base class.
-  ValueObjectSP valobj_r_base_sp = valobj_r_sp->GetChildAtIndex(0);
-  if (!valobj_r_base_sp)
-    return {};
-
-  ValueObjectSP valobj_rep_sp =
-      valobj_r_base_sp->GetChildMemberWithName("__value_");
-  if (!valobj_rep_sp)
+  ValueObjectSP valobj_rep_sp = ExtractLibCxxStringData(valobj);
+  if (!valobj_rep_sp || !valobj_rep_sp->GetError().Success())
     return {};
 
   ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName("__l");
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
index 5307b5251ca84..dad033611b38d 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h
@@ -25,7 +25,8 @@ GetChildMemberWithName(ValueObject &obj,
 
 lldb::ValueObjectSP GetFirstValueOfLibCXXCompressedPair(ValueObject &pair);
 lldb::ValueObjectSP GetSecondValueOfLibCXXCompressedPair(ValueObject &pair);
-
+bool isOldCompressedPairLayout(ValueObject &pair_obj);
+bool isStdTemplate(ConstString type_name, llvm::StringRef type);
 
 bool LibcxxStringSummaryProviderASCII(
     ValueObject &valobj, Stream &stream,
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp
index d7cfeb30557c3..4479f592fc2d2 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp
@@ -17,6 +17,7 @@
 #include "lldb/Utility/Endian.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/Stream.h"
+#include "lldb/lldb-enumerations.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -294,12 +295,17 @@ lldb::ChildCacheState ForwardListFrontEnd::Update() {
 
   ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_"));
   if (!impl_sp)
-    return lldb::ChildCacheState::eRefetch;
-  impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp);
+    return ChildCacheState::eRefetch;
+
+  if (isOldCompressedPairLayout(*impl_sp))
+    impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp);
+
   if (!impl_sp)
-    return lldb::ChildCacheState::eRefetch;
+    return ChildCacheState::eRefetch;
+
   m_head = impl_sp->GetChildMemberWithName("__next_").get();
-  return lldb::ChildCacheState::eRefetch;
+
+  return ChildCacheState::eRefetch;
 }
 
 ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp)
@@ -313,34 +319,42 @@ llvm::Expected<uint32_t> ListFrontEnd::CalculateNumChildren() {
     return m_count;
   if (!m_head || !m_tail || m_node_address == 0)
     return 0;
-  ValueObjectSP size_alloc(m_backend.GetChildMemberWithName("__size_alloc_"));
-  if (size_alloc) {
-    ValueObjectSP value = GetFirstValueOfLibCXXCompressedPair(*size_alloc);
-    if (value) {
-      m_count = value->GetValueAsUnsigned(UINT32_MAX);
-    }
+
+  ValueObjectSP size_node_sp(m_backend.GetChildMemberWithName("__size_"));
+  if (!size_node_sp) {
+    size_node_sp = m_backend.GetChildMemberWithName(
+        "__size_alloc_"); // pre-compressed_pair rework
+
+    if (!isOldCompressedPairLayout(*size_node_sp))
+      return llvm::createStringError("Unexpected std::list layout: expected "
+                                     "old __compressed_pair layout.");
+
+    size_node_sp = GetFirstValueOfLibCXXCompressedPair(*size_node_sp);
   }
-  if (m_count != UINT32_MAX) {
+
+  if (size_node_sp)
+    m_count = size_node_sp->GetValueAsUnsigned(UINT32_MAX);
+
+  if (m_count != UINT32_MAX)
     return m_count;
-  } else {
-    uint64_t next_val = m_head->GetValueAsUnsigned(0);
-    uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
-    if (next_val == 0 || prev_val == 0)
-      return 0;
-    if (next_val == m_node_address)
-      return 0;
-    if (next_val == prev_val)
-      return 1;
-    uint64_t size = 2;
-    ListEntry current(m_head);
-    while (current.next() && current.next().value() != m_node_address) {
-      size++;
-      current = current.next();
-      if (size > m_list_capping_size)
-        break;
-    }
-    return m_count = (size - 1);
+
+  uint64_t next_val = m_head->GetValueAsUnsigned(0);
+  uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
+  if (next_val == 0 || prev_val == 0)
+    return 0;
+  if (next_val == m_node_address)
+    return 0;
+  if (next_val == prev_val)
+    return 1;
+  uint64_t size = 2;
+  ListEntry current(m_head);
+  while (current.next() && current.next().value() != m_node_address) {
+    size++;
+    current = current.next();
+    if (size > m_list_capping_size)
+      break;
   }
+  return m_count = (size - 1);
 }
 
 lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(uint32_t idx) {
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
index 5106a63d531f8..b89afd6a388ab 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
@@ -202,6 +202,8 @@ class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd {
   size_t GetIndexOfChildWithName(ConstString name) override;
 
 private:
+  llvm::Expected<uint32_t> CalculateNumChildrenForOldCompressedPairLayout();
+
   /// Returns the ValueObject for the __tree_node type that
   /// holds the key/value pair of the node at index \ref idx.
   ///
@@ -254,6 +256,29 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
     Update();
 }
 
+llvm::Expected<uint32_t>
+lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
+    CalculateNumChildrenForOldCompressedPairLayout() {
+  ValueObjectSP node_sp(m_tree->GetChildMemberWithName("__pair3_"));
+  if (!node_sp)
+    return 0;
+
+  // TODO: or should this just be: assert
+  // (!isOldCompressedPairLayout(*node_sp));
+  if (!isOldCompressedPairLayout(*node_sp))
+    return llvm::createStringError("Unexpected std::map layout: expected "
+                                   "old __compressed_pair layout.");
+
+  node_sp = GetFirstValueOfLibCXXCompressedPair(*node_sp);
+
+  if (!node_sp)
+    return 0;
+
+  m_count = node_sp->GetValueAsUnsigned(0);
+
+  return m_count;
+}
+
 llvm::Expected<uint32_t> lldb_private::formatters::
     LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren() {
   if (m_count != UINT32_MAX)
@@ -262,17 +287,12 @@ llvm::Expected<uint32_t> lldb_private::formatters::
   if (m_tree == nullptr)
     return 0;
 
-  ValueObjectSP size_node(m_tree->GetChildMemberWithName("__pair3_"));
-  if (!size_node)
-    return 0;
-
-  size_node = GetFirstValueOfLibCXXCompressedPair(*size_node);
-
-  if (!size_node)
-    return 0;
+  if (auto node_sp = m_tree->GetChildMemberWithName("__size_")) {
+    m_count = node_sp->GetValueAsUnsigned(0);
+    return m_count;
+  }
 
-  m_count = size_node->GetValueAsUnsigned(0);
-  return m_count;
+  return CalculateNumChildrenForOldCompressedPairLayout();
 }
 
 ValueObjectSP
@@ -371,6 +391,7 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() {
   m_tree = m_backend.GetChildMemberWithName("__tree_").get();
   if (!m_tree)
     return lldb::ChildCacheState::eRefetch;
+
   m_root_node = m_tree->GetChildMemberWithName("__begin_node_").get();
   m_node_ptr_type =
       m_tree->GetCompilerType().GetDirectNestedTypeWithName("__node_pointer");
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
index 93e7f4f4fd86c..2f65c726aa51a 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
@@ -19,6 +19,7 @@
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/Stream.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -44,6 +45,10 @@ class LibcxxStdUnorderedMapSyntheticFrontEnd
   size_t GetIndexOfChildWithName(ConstString name) override;
 
 private:
+  CompilerType GetNodeType();
+  CompilerType GetElementType(CompilerType node_type);
+  llvm::Expected<size_t> CalculateNumChildrenImpl(ValueObject &table);
+
   CompilerType m_element_type;
   CompilerType m_node_type;
   ValueObject *m_tree = nullptr;
@@ -91,29 +96,53 @@ llvm::Expected<uint32_t> lldb_private::formatters::
   return m_num_elements;
 }
 
-static void consumeInlineNamespace(llvm::StringRef &name) {
-  // Delete past an inline namespace, if any: __[a-zA-Z0-9_]+::
-  auto scratch = name;
-  if (scratch.consume_front("__") && std::isalnum(scratch[0])) {
-    scratch = scratch.drop_while([](char c) { return std::isalnum(c); });
-    if (scratch.consume_front("::")) {
-      // Successfully consumed a namespace.
-      name = scratch;
-    }
-  }
+static bool isUnorderedMap(ConstString type_name) {
+  return isStdTemplate(type_name, "unordered_map") ||
+         isStdTemplate(type_name, "unordered_multimap");
 }
 
-static bool isStdTemplate(ConstString type_name, llvm::StringRef type) {
-  llvm::StringRef name = type_name.GetStringRef();
-  // The type name may be prefixed with `std::__<inline-namespace>::`.
-  if (name.consume_front("std::"))
-    consumeInlineNamespace(name);
-  return name.consume_front(type) && name.starts_with("<");
+CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+    GetElementType(CompilerType node_type) {
+  CompilerType element_type = node_type.GetTypeTemplateArgument(0);
+
+  // This synthetic provider is used for both unordered_(multi)map and
+  // unordered_(multi)set. For unordered_map, the element type has an
+  // additional type layer, an internal struct (`__hash_value_type`)
+  // that wraps a std::pair. Peel away the internal wrapper type - whose
+  // structure is of no value to users, to expose the std::pair. This
+  // matches the structure returned by the std::map synthetic provider.
+  if (isUnorderedMap(m_backend.GetTypeName())) {
+    std::string name;
+    CompilerType field_type =
+        element_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr);
+    CompilerType actual_type = field_type.GetTypedefedType();
+    if (isStdTemplate(actual_type.GetTypeName(), "pair"))
+      element_type = actual_type;
+  }
+
+  return element_type;
 }
 
-static bool isUnorderedMap(ConstString type_name) {
-  return isStdTemplate(type_name, "unordered_map") ||
-         isStdTemplate(type_name, "unordered_multimap");
+CompilerType lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+    GetNodeType() {
+  auto node_sp = m_backend.GetChildAtNamePath({"__table_", "__first_node_"});
+
+  if (!node_sp) {
+    auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"});
+    if (!p1_sp)
+      return {};
+
+    if (!isOldCompressedPairLayout(*p1_sp))
+      return {};
+
+    node_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);
+    if (!node_sp)
+      return {};
+  }
+
+  assert(node_sp);
+
+  return node_sp->GetCompilerType().GetTypeTemplateArgument(0).GetPointeeType();
 }
 
 lldb::ValueObjectSP lldb_private::formatters::
@@ -136,36 +165,12 @@ lldb::ValueObjectSP lldb_private::formatters::
     ValueObjectSP hash_sp = node_sp->GetChildMemberWithName("__hash_");
     if (!hash_sp || !value_sp) {
       if (!m_element_type) {
-        auto p1_sp = m_backend.GetChildAtNamePath({"__table_", "__p1_"});
-        if (!p1_sp)
-          return nullptr;
-
-        ValueObjectSP first_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);
-        if (!first_sp)
+        m_node_type = GetNodeType();
+        if (!m_node_type)
           return nullptr;
 
-        m_element_type = first_sp->GetCompilerType();
-        m_element_type = m_element_type.GetTypeTemplateArgument(0);
-        m_element_type = m_element_type.GetPointeeType();
-        m_node_type = m_element_type;
-        m_element_type = m_element_type.GetTypeTemplateArgument(0);
-        // This synthetic provider is used for both unordered_(multi)map and
-        // unordered_(multi)set. For unordered_map, the element type has an
-        // additional type layer, an internal struct (`__hash_value_type`)
-        // that wraps a std::pair. Peel away the internal wrapper type - whose
-        // structure is of no value to users, to expose the std::pair. This
-        // matches the structure returned by the std::map synthetic provider.
-        if (isUnorderedMap(m_backend.GetTypeName())) {
-          std::string name;
-          CompilerType field_type = m_element_type.GetFieldAtIndex(
-              0, name, nullptr, nullptr, nullptr);
-          CompilerType actual_type = field_type.GetTypedefedType();
-          if (isStdTemplate(actual_type.GetTypeName(), "pair"))
-            m_element_type = actual_type;
-        }
+        m_element_type = GetElementType(m_node_type);
       }
-      if (!m_node_type)
-        return nullptr;
       node_sp = m_next_element->Cast(m_node_type.GetPointerType())
               ->Dereference(error);
       if (!node_sp || error.Fail())
@@ -217,6 +222,49 @@ lldb::ValueObjectSP lldb_private::formatters::
                                    m_element_type);
 }
 
+llvm::Expected<size_t>
+lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
+    CalculateNumChildrenImpl(ValueObject &table) {
+  if (auto size_sp = table.GetChildMemberWithName("__size_"))
+    return size_sp->GetValueAsUnsigned(0);
+
+  ValueObjectSP p2_sp = table.GetChildMemberWithName("__p2_");
+  if (!p2_sp)
+    return llvm::createStringError(
+        "Unexpected std::unordered_map layout: __p2_ member not found.");
+
+  if (!isOldCompressedPairLayout(*p2_sp))
+    return llvm::createStringError("Unexpected std::unordered_map layout: old "
+                                   "__compressed_pair layout not found.");
+
+  ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp);
+
+  if (!num_elements_sp)
+    return llvm::createStringError(
+        "Unexpected std::unordered_map layout: failed to retrieve first member "
+        "in old __compressed_pair layout.");
+
+  return num_elements_sp->GetValueAsUnsigned(0);
+}
+
+static ValueObjectSP GetTreePointer(ValueObject &table) {
+  ValueObjectSP tree_sp = table.GetChildMemberWithName("__first_node_");
+  if (!tree_sp) {
+    ValueObjectSP p1_sp = table.GetChildMemberWithName("__p1_");
+    if (!p1_sp)
+      return nullptr;
+
+    if (!isOldCompressedPairLayout(*p1_sp))
+      return nullptr;
+
+    tree_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);
+    if (!tree_sp)
+      return nullptr;
+  }
+
+  return tree_sp->GetChildMemberWithName("__next_");
+}
+
 lldb::ChildCacheState
 lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() {
   m_num_elements = 0;
@@ -226,27 +274,19 @@ lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::Update() {
   if (!table_sp)
     return lldb::ChildCacheState::eRefetch;
 
-  ValueObjectSP p2_sp = table_sp->GetChildMemberWithName("__p2_");
-  if (!p2_sp)
-    return lldb::ChildCacheState::eRefetch;
-
-  ValueObjectSP num_elements_sp = GetFirstValueOfLibCXXCompressedPair(*p2_sp);
-  if (!num_elements_sp)
-    return lldb::ChildCacheState::eRefetch;
-
-  ValueObjectSP p1_sp = table_sp->GetChildMemberWithName("__p1_");
-  if (!p1_sp)
+  ValueObjectSP tree_sp = GetTreePointer(*table_sp);
+  if (!tree_sp)
     return lldb::ChildCacheState::eRefetch;
 
-  ValueObjectSP value_sp = GetFirstValueOfLibCXXCompressedPair(*p1_sp);
-  if (!value_sp)
-    return lldb::ChildCacheState::eRefetch;
+  m_tree = tree_sp.get();
 
-  m_tree = value_sp->GetChildMemberWithName("__next_").get();
-  if (m_tree == nullptr)
+  if (auto num_elems_or_err = CalculateNumChildrenImpl(*table_sp))
+    m_num_elements = *num_elems_or_err;
+  else {
+    LLDB_LOG_ERRORV(GetLog(LLDBLog::DataFormatters),
+                    num_elems_or_err.takeError(), "{0}");
     return lldb::ChildCacheState::eRefetch;
-
-  m_num_elements = num_elements_sp->GetValueAsUnsigned(0);
+  }
 
   if (m_num_elements > 0)
     m_next_element = m_tree;
diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
index 461fed35164b4..3609219e70551 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxVector.cpp
@@ -11,6 +11,8 @@
 #include "lldb/Core/ValueObject.h"
 #include "lldb/DataFormatters/FormattersHelpers.h"
 #include "lldb/Utility/ConstString.h"
+#include "lldb/lldb-enumerations.h"
+#include "lldb/lldb-forward.h"
 #include <optional>
 
 using namespace lldb;
@@ -116,20 +118,29 @@ lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::GetChildAtIndex(
                                       m_element_type);
 }
 
+static ValueObjectSP GetDataPointer(ValueObject &root) {
+  if (auto cap_sp = root.GetChildMemberWithName("__cap_"))
+    return cap_sp;
+
+  ValueObjectSP cap_sp = root.GetChildMemberWithName("__end_cap_");
+  if (!cap_sp)
+    return nullptr;
+
+  if (!isOldCompressedPairLayout(*cap_sp))
+    return nullptr;
+
+  return GetFirstValueOfLibCXXCompressedPair(*cap_sp);
+}
+
 lldb::ChildCacheState
 lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() {
   m_start = m_finish = nullptr;
-  ValueObjectSP data_type_finder_sp(
-      m_backend.GetChildMemberWithName("__end_cap_"));
-  if (!data_type_finder_sp)
-    return lldb::ChildCacheState::eRefetch;
+  ValueObjectSP data_sp(GetDataPointer(m_backend));
 
-  data_type_finder_sp =
-      GetFirstValueOfLibCXXCompressedPair(*data_type_finder_sp);
-  if (!data_type_finder_sp)
+  if (!data_sp)
     return lldb::ChildCacheState::eRefetch;
 
-  m_element_type = data_type_finder_sp->GetCompilerType().GetPointeeType();
+  m_element_type = data_sp->GetCompilerType().GetPointeeType();
   if (std::optional<uint64_t> size = m_element_type.GetByteSize(nullptr)) {
     m_element_size = *size;
 
@@ -216,17 +227,6 @@ lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex(
   return retval_sp;
 }
 
-/*(std::__1::vector<std::__1::allocator<bool> >) vBool = {
- __begin_ = 0x00000001001000e0
- __size_ = 56
- __cap_alloc_ = {
- std::__1::__libcpp_compressed_pair_imp<unsigned long,
- std::__1::allocator<unsigned long> > = {
- __first_ = 1
- }
- }
- }*/
-
 lldb::ChildCacheState
 lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::Update() {
   m_children.clear();
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py
index 270aab1b75122..6ecf10e1213de 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py
@@ -62,7 +62,7 @@ def cleanup():
         self.expect(
             "frame variable numbers_list --raw",
             matching=False,
-            substrs=["size=0", "{}"],
+            substrs=["size=0"]
         )
 
         if stdlib_type == USE_LIBSTDCPP:

>From 2d281edeb2a5d97e40d6b7c2de73742635702a2f Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Thu, 18 Jul 2024 09:14:26 -0500
Subject: [PATCH 2/3] fixup! remove TODO

---
 lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp | 2 --
 1 file changed, 2 deletions(-)

diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
index b89afd6a388ab..af3e41b0daf8a 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxMap.cpp
@@ -263,8 +263,6 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::
   if (!node_sp)
     return 0;
 
-  // TODO: or should this just be: assert
-  // (!isOldCompressedPairLayout(*node_sp));
   if (!isOldCompressedPairLayout(*node_sp))
     return llvm::createStringError("Unexpected std::map layout: expected "
                                    "old __compressed_pair layout.");

>From 7b8352807cc3e7deb6f3d41b8382f7d848493a07 Mon Sep 17 00:00:00 2001
From: Michael Buch <michaelbuch12 at gmail.com>
Date: Thu, 18 Jul 2024 09:15:28 -0500
Subject: [PATCH 3/3] fixup! python format

---
 .../generic/list/TestDataFormatterGenericList.py              | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py
index 6ecf10e1213de..8546b1f3b6431 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py
@@ -60,9 +60,7 @@ def cleanup():
         self.runCmd("type format add -f hex int")
 
         self.expect(
-            "frame variable numbers_list --raw",
-            matching=False,
-            substrs=["size=0"]
+            "frame variable numbers_list --raw", matching=False, substrs=["size=0"]
         )
 
         if stdlib_type == USE_LIBSTDCPP:



More information about the lldb-commits mailing list