[Lldb-commits] [lldb] Implement data formatters for LibStdC++ std::variant (PR #68012)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Oct 2 14:31:58 PDT 2023
https://github.com/jeffreytan81 updated https://github.com/llvm/llvm-project/pull/68012
>From e7e47a211ebaaa0f6380810b6573fadde12ca02d Mon Sep 17 00:00:00 2001
From: jeffreytan81 <jeffreytan at fb.com>
Date: Mon, 2 Oct 2023 10:53:17 -0700
Subject: [PATCH 1/3] Implement data formatters for LibStdC++ std::variant
---
lldb/examples/synthetic/gnu_libstdcpp.py | 89 +++++++++++++++++++
.../Language/CPlusPlus/CPlusPlusLanguage.cpp | 18 +++-
.../libstdcpp/variant/Makefile | 5 ++
.../TestDataFormatterLibStdcxxVariant.py | 72 +++++++++++++++
.../libstdcpp/variant/main.cpp | 79 ++++++++++++++++
5 files changed, 259 insertions(+), 4 deletions(-)
create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/Makefile
create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py
create mode 100644 lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/main.cpp
diff --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py
index 825b7f3787a010d..7462db744674682 100644
--- a/lldb/examples/synthetic/gnu_libstdcpp.py
+++ b/lldb/examples/synthetic/gnu_libstdcpp.py
@@ -892,3 +892,92 @@ def update(self):
except:
pass
return False
+
+
+def VariantSummaryProvider(valobj, dict):
+ raw_obj = valobj.GetNonSyntheticValue()
+ index_obj = raw_obj.GetChildMemberWithName("_M_index")
+ data_obj = raw_obj.GetChildMemberWithName("_M_u")
+ if not (
+ index_obj
+ and index_obj.IsValid()
+ and data_obj
+ and data_obj.IsValid()
+ ):
+ return "<Can't find _M_index or _M_u>"
+
+ def get_variant_npos_value(index_byte_size):
+ if index_byte_size == 1:
+ return 0xFF
+ elif index_byte_size == 2:
+ return 0xFFFF
+ else:
+ return 0xFFFFFFFF
+
+ npos_value = get_variant_npos_value(index_obj.GetByteSize())
+ index = index_obj.GetValueAsUnsigned(0)
+ if index == npos_value:
+ return " No Value"
+
+ active_type = data_obj.GetType().GetTemplateArgumentType(index)
+ return f" Active Type = {active_type.GetDisplayTypeName()} "
+
+
+class VariantSynthProvider:
+ def __init__(self, valobj, dict):
+ self.raw_obj = valobj.GetNonSyntheticValue()
+ self.is_valid = False
+ self.index = None
+ self.data_obj = None
+
+ def update(self):
+ try:
+ self.index = self.raw_obj.GetChildMemberWithName(
+ "_M_index"
+ ).GetValueAsSigned(-1)
+ self.is_valid = self.index != -1
+ self.data_obj = self.raw_obj.GetChildMemberWithName("_M_u")
+ except:
+ self.is_valid = False
+ return False
+
+ def has_children(self):
+ return True
+
+ def num_children(self):
+ return 1 if self.is_valid else 0
+
+ def get_child_index(self, name):
+ return 0
+
+ def get_child_at_index(self, index):
+ if not self.is_valid:
+ return None
+ cur = 0
+ node = self.data_obj
+ while cur < self.index:
+ node = node.GetChildMemberWithName("_M_rest")
+ cur += 1
+
+ # _M_storage's type depends on variant field's type "_Type".
+ # 1. if '_Type' is literal type: _Type _M_storage.
+ # 2. otherwise, __gnu_cxx::__aligned_membuf<_Type> _M_storage.
+ #
+ # For 2. we have to cast it to underlying template _Type.
+
+ value = node.GetChildMemberWithName("_M_first").GetChildMemberWithName(
+ "_M_storage"
+ )
+ template_type = value.GetType().GetTemplateArgumentType(0)
+
+ # Literal type will return None for GetTemplateArgumentType(0)
+ if (
+ template_type
+ and "__gnu_cxx::__aligned_membuf" in value.GetType().GetDisplayTypeName()
+ and template_type.IsValid()
+ ):
+ value = value.Cast(template_type)
+
+ if value.IsValid():
+ return value.Clone("Value")
+ return None
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index c1743a5e0a418dd..d8a30729b6d02e1 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -332,11 +332,11 @@ bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) {
// If we can't parse the incoming name, then just check that it contains path.
if (m_parse_error)
return m_full.GetStringRef().contains(path);
-
+
llvm::StringRef identifier;
llvm::StringRef context;
std::string path_str = path.str();
- bool success
+ bool success
= CPlusPlusLanguage::ExtractContextAndIdentifier(path_str.c_str(),
context,
identifier);
@@ -372,7 +372,7 @@ bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) {
return false;
if (haystack.empty() || !isalnum(haystack.back()))
return true;
-
+
return false;
}
@@ -388,7 +388,7 @@ bool CPlusPlusLanguage::IsCPPMangledName(llvm::StringRef name) {
return true;
}
-bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path,
+bool CPlusPlusLanguage::DemangledNameContainsPath(llvm::StringRef path,
ConstString demangled) const {
MethodName demangled_name(demangled);
return demangled_name.ContainsPath(path);
@@ -1104,6 +1104,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
SyntheticChildrenSP(new ScriptedSyntheticChildren(
stl_synth_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider")));
+ cpp_category_sp->AddTypeSynthetic(
+ "^std::variant<.+>$", eFormatterMatchRegex,
+ SyntheticChildrenSP(new ScriptedSyntheticChildren(
+ stl_synth_flags,
+ "lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider")));
stl_summary_flags.SetDontShowChildren(false);
stl_summary_flags.SetSkipPointers(false);
@@ -1148,6 +1153,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
TypeSummaryImplSP(new ScriptSummaryFormat(
stl_summary_flags,
"lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider")));
+ cpp_category_sp->AddTypeSummary(
+ "^std::variant<.+>$", eFormatterMatchRegex,
+ TypeSummaryImplSP(new ScriptSummaryFormat(
+ stl_summary_flags,
+ "lldb.formatters.cpp.gnu_libstdcpp.VariantSummaryProvider")));
AddCXXSynthetic(
cpp_category_sp,
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/Makefile
new file mode 100644
index 000000000000000..104f82809c7a35b
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/Makefile
@@ -0,0 +1,5 @@
+CXX_SOURCES := main.cpp
+
+USE_LIBSTDCPP := 1
+CXXFLAGS_EXTRAS := -std=c++17
+include Makefile.rules
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py
new file mode 100644
index 000000000000000..88be87a5469e196
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py
@@ -0,0 +1,72 @@
+"""
+Test lldb data formatter for LibStdC++ std::variant.
+"""
+
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+USE_LIBSTDCPP = "USE_LIBSTDCPP"
+
+
+class LibStdcxxVariantDataFormatterTestCase(TestBase):
+ @add_test_categories(["libstdcxx"])
+ def test_with_run_command(self):
+ """Test LibStdC++ std::variant data formatter works correctly."""
+ self.build(dictionary={USE_LIBSTDCPP: "1"})
+
+ (self.target, self.process, _, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "// break here", lldb.SBFileSpec("main.cpp", False)
+ )
+
+ lldbutil.continue_to_breakpoint(self.process, bkpt)
+ self.assertEqual(3 + 4, 7)
+
+ self.expect(
+ "frame variable v1",
+ substrs=["v1 = Active Type = int {", "Value = 12", "}"],
+ )
+
+ self.expect(
+ "frame variable v1_ref",
+ substrs=["v1_ref = Active Type = int : {", "Value = 12", "}"],
+ )
+
+ self.expect(
+ "frame variable v_v1",
+ substrs=[
+ "v_v1 = Active Type = std::variant<int, double, char> {",
+ "Value = Active Type = int {",
+ "Value = 12",
+ "}",
+ "}",
+ ],
+ )
+
+ lldbutil.continue_to_breakpoint(self.process, bkpt)
+
+ self.expect(
+ "frame variable v1",
+ substrs=["v1 = Active Type = double {", "Value = 2", "}"],
+ )
+
+ lldbutil.continue_to_breakpoint(self.process, bkpt)
+
+ self.expect(
+ "frame variable v2",
+ substrs=["v2 = Active Type = double {", "Value = 2", "}"],
+ )
+
+ self.expect(
+ "frame variable v3",
+ substrs=["v3 = Active Type = char {", "Value = 'A'", "}"],
+ )
+
+ self.expect("frame variable v_no_value", substrs=["v_no_value = No Value"])
+
+ self.expect(
+ "frame variable v_many_types_no_value",
+ substrs=["v_many_types_no_value = No Value"],
+ )
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/main.cpp
new file mode 100644
index 000000000000000..545318f9358b673
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/main.cpp
@@ -0,0 +1,79 @@
+#include <cstdio>
+#include <string>
+#include <variant>
+#include <vector>
+
+struct S {
+ operator int() { throw 42; }
+};
+
+int main() {
+ bool has_variant = true;
+
+ printf("%d\n", has_variant); // break here
+
+ std::variant<int, double, char> v1;
+ std::variant<int, double, char> &v1_ref = v1;
+ std::variant<int, double, char> v2;
+ std::variant<int, double, char> v3;
+ std::variant<std::variant<int, double, char>> v_v1;
+ std::variant<int, double, char> v_no_value;
+ // The next variant has many types, meaning the type index does not fit in
+ // a byte and must be `unsigned short` instead of `unsigned char` when
+ // using the unstable libc++ ABI. With stable libc++ ABI, the type index
+ // is always just `unsigned int`.
+ std::variant<
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int, int, int, int,
+ int, int, int, int, int, int, int, int, int, int, int, int>
+ v_many_types_no_value;
+
+ v1 = 12; // v contains int
+ v_v1 = v1;
+ int i = std::get<int>(v1);
+ printf("%d\n", i); // break here
+
+ v2 = 2.0;
+ double d = std::get<double>(v2);
+ printf("%f\n", d);
+
+ v3 = 'A';
+ char c = std::get<char>(v3);
+ printf("%d\n", c);
+
+ // Checking v1 above and here to make sure we done maintain the incorrect
+ // state when we change its value.
+ v1 = 2.0;
+ d = std::get<double>(v1);
+ printf("%f\n", d); // break here
+
+ try {
+ v_no_value.emplace<0>(S());
+ } catch (...) {
+ }
+
+ printf("%zu\n", v_no_value.index());
+
+ try {
+ v_many_types_no_value.emplace<0>(S());
+ } catch (...) {
+ }
+
+ printf("%zu\n", v_many_types_no_value.index());
+
+ return 0; // break here
+}
>From 53d20815eadd368ff1185af520bcb4aa75c9e96e Mon Sep 17 00:00:00 2001
From: jeffreytan81 <jeffreytan at fb.com>
Date: Mon, 2 Oct 2023 14:18:14 -0700
Subject: [PATCH 2/3] Fix formatter
---
lldb/examples/synthetic/gnu_libstdcpp.py | 7 +------
.../Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp | 8 ++++----
2 files changed, 5 insertions(+), 10 deletions(-)
diff --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py
index 7462db744674682..29c926167fb440c 100644
--- a/lldb/examples/synthetic/gnu_libstdcpp.py
+++ b/lldb/examples/synthetic/gnu_libstdcpp.py
@@ -898,12 +898,7 @@ def VariantSummaryProvider(valobj, dict):
raw_obj = valobj.GetNonSyntheticValue()
index_obj = raw_obj.GetChildMemberWithName("_M_index")
data_obj = raw_obj.GetChildMemberWithName("_M_u")
- if not (
- index_obj
- and index_obj.IsValid()
- and data_obj
- and data_obj.IsValid()
- ):
+ if not (index_obj and index_obj.IsValid() and data_obj and data_obj.IsValid()):
return "<Can't find _M_index or _M_u>"
def get_variant_npos_value(index_byte_size):
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index d8a30729b6d02e1..a285864ca2e1229 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -1105,10 +1105,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
stl_synth_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider")));
cpp_category_sp->AddTypeSynthetic(
- "^std::variant<.+>$", eFormatterMatchRegex,
- SyntheticChildrenSP(new ScriptedSyntheticChildren(
- stl_synth_flags,
- "lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider")));
+ "^std::variant<.+>$", eFormatterMatchRegex,
+ SyntheticChildrenSP(new ScriptedSyntheticChildren(
+ stl_synth_flags,
+ "lldb.formatters.cpp.gnu_libstdcpp.VariantSynthProvider")));
stl_summary_flags.SetDontShowChildren(false);
stl_summary_flags.SetSkipPointers(false);
>From eebb06c9b893798a1f2b22502bf32dad48b644eb Mon Sep 17 00:00:00 2001
From: jeffreytan81 <jeffreytan at fb.com>
Date: Mon, 2 Oct 2023 14:31:36 -0700
Subject: [PATCH 3/3] Fix formatter again
---
.../source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index a285864ca2e1229..ad6d627938c0520 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -336,10 +336,8 @@ bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) {
llvm::StringRef identifier;
llvm::StringRef context;
std::string path_str = path.str();
- bool success
- = CPlusPlusLanguage::ExtractContextAndIdentifier(path_str.c_str(),
- context,
- identifier);
+ bool success = CPlusPlusLanguage::ExtractContextAndIdentifier(
+ path_str.c_str(), context, identifier);
if (!success)
return m_full.GetStringRef().contains(path);
More information about the lldb-commits
mailing list