[Lldb-commits] [lldb] a2ec18e - [lldb] From unordered_map synthetic provider, return std::pair children

Dave Lee via lldb-commits lldb-commits at lists.llvm.org
Fri Sep 2 08:54:01 PDT 2022


Author: Dave Lee
Date: 2022-09-02T08:53:46-07:00
New Revision: a2ec18ee043f7c8caa0e9e329721182b9f1a5dcd

URL: https://github.com/llvm/llvm-project/commit/a2ec18ee043f7c8caa0e9e329721182b9f1a5dcd
DIFF: https://github.com/llvm/llvm-project/commit/a2ec18ee043f7c8caa0e9e329721182b9f1a5dcd.diff

LOG: [lldb] From unordered_map synthetic provider, return std::pair children

Change the behavior of the libc++ `unordered_map` synthetic provider to present
children as `std::pair` values, just like `std::map` does.

The synthetic provider for libc++ `std::unordered_map` has returned children
that expose a level of internal structure (over top of the key/value pair). For
example, given an unordered map initialized with `{{1,2}, {3, 4}}`, the output
is:

```
(std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, std::allocator<std::pair<const int, int> > >) map = size=2 {
  [0] = {
    __cc = (first = 3, second = 4)
  }
  [1] = {
    __cc = (first = 1, second = 2)
  }
}
```

It's not ideal/necessary to have the numbered children embdedded in the `__cc`
field.

Note: the numbered children have type
`std::__hash_node<std::__hash_value_type<Key, T>, void *>::__node_value_type`,
and the `__cc` fields have type `std::__hash_value_type<Key, T>::value_type`.

Compare this output to `std::map`:

```
(std::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >) map = size=2 {
  [0] = (first = 1, second = 2)
  [1] = (first = 3, second = 4)
```

Where the numbered children have type `std::pair<const Key, T>`.

This changes the behavior of the synthetic provider for `unordered_map` to also
present children as `pairs`, just like `std::map`.

It appears the synthetic provider implementation for `unordered_map` was meant
to provide this behavior, but was maybe incomplete (see
d22a94377f7554a7e9df050f6dfc3ee42384e3fe). It has both an `m_node_type` and an
`m_element_type`, but uses only the former. The latter is exactly the type
needed for the children pairs. With this existing code, it's not much of a
change to make this work.

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

Added: 
    

Modified: 
    lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
    lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py

Removed: 
    


################################################################################
diff  --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
index 85c04ea728f56..90e6e124090e9 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxUnorderedMap.cpp
@@ -13,10 +13,12 @@
 #include "lldb/Core/ValueObjectConstResult.h"
 #include "lldb/DataFormatters/FormattersHelpers.h"
 #include "lldb/Target/Target.h"
+#include "lldb/Utility/ConstString.h"
 #include "lldb/Utility/DataBufferHeap.h"
 #include "lldb/Utility/Endian.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/Stream.h"
+#include "llvm/ADT/StringRef.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -65,6 +67,19 @@ size_t lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::
   return m_num_elements;
 }
 
+static bool isStdTemplate(ConstString type_name, llvm::StringRef type) {
+  llvm::StringRef name = type_name.GetStringRef();
+  // The type name may or may not be prefixed with `std::` or `std::__1::`.
+  if (name.consume_front("std::"))
+    name.consume_front("__1::");
+  return name.consume_front(type) && name.startswith("<");
+}
+
+static bool isUnorderedMap(ConstString type_name) {
+  return isStdTemplate(type_name, "unordered_map") ||
+         isStdTemplate(type_name, "unordered_multimap");
+}
+
 lldb::ValueObjectSP lldb_private::formatters::
     LibcxxStdUnorderedMapSyntheticFrontEnd::GetChildAtIndex(size_t idx) {
   if (idx >= CalculateNumChildren())
@@ -118,10 +133,20 @@ lldb::ValueObjectSP lldb_private::formatters::
         m_element_type = m_element_type.GetPointeeType();
         m_node_type = m_element_type;
         m_element_type = m_element_type.GetTypeTemplateArgument(0);
-        std::string name;
-        m_element_type =
-            m_element_type.GetFieldAtIndex(0, name, nullptr, nullptr, nullptr);
-        m_element_type = m_element_type.GetTypedefedType();
+        // 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;
+        }
       }
       if (!m_node_type)
         return nullptr;
@@ -153,7 +178,7 @@ lldb::ValueObjectSP lldb_private::formatters::
   ExecutionContext exe_ctx = val_hash.first->GetExecutionContextRef().Lock(
       thread_and_frame_only_if_stopped);
   return CreateValueObjectFromData(stream.GetString(), data, exe_ctx,
-                                   val_hash.first->GetCompilerType());
+                                   m_element_type);
 }
 
 bool lldb_private::formatters::LibcxxStdUnorderedMapSyntheticFrontEnd::

diff  --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py
index 6bb14417ea2d4..7730e780bd478 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py
@@ -47,12 +47,17 @@ def cleanup():
             "corrupt_map", ['%s::unordered_map' %
                     ns, 'size=0 {}'])
 
+        must_not_contain__cc = r'(?s)^(?!.*\b__cc = )'
+
         self.look_for_content_and_continue(
-            "map", ['%s::unordered_map' %
-                    ns, 'size=5 {', 'hello', 'world', 'this', 'is', 'me'])
+            "map", ['%s::unordered_map' % ns,
+                    must_not_contain__cc,
+                    'size=5 {', 'hello', 'world', 'this', 'is', 'me'])
 
         self.look_for_content_and_continue(
-            "mmap", ['%s::unordered_multimap' % ns, 'size=6 {', 'first = 3', 'second = "this"',
+            "mmap", ['%s::unordered_multimap' % ns,
+                     must_not_contain__cc,
+                     'size=6 {', 'first = 3', 'second = "this"',
                      'first = 2', 'second = "hello"'])
 
         self.look_for_content_and_continue(


        


More information about the lldb-commits mailing list