[Lldb-commits] [lldb] 1ec4330 - Implement data formatters for LibStdC++ std::variant (#68012)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Oct 2 17:46:45 PDT 2023
Author: jeffreytan81
Date: 2023-10-02T17:46:41-07:00
New Revision: 1ec4330f7e4d9b1ed1880c957e9e897f4eaf204f
URL: https://github.com/llvm/llvm-project/commit/1ec4330f7e4d9b1ed1880c957e9e897f4eaf204f
DIFF: https://github.com/llvm/llvm-project/commit/1ec4330f7e4d9b1ed1880c957e9e897f4eaf204f.diff
LOG: Implement data formatters for LibStdC++ std::variant (#68012)
This patch implements the data formatters for LibStdC++ `std::variant`.
---------
Co-authored-by: jeffreytan81 <jeffreytan at fb.com>
Added:
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/Makefile
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/main.cpp
Modified:
lldb/examples/synthetic/gnu_libstdcpp.py
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
Removed:
################################################################################
diff --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py
index 825b7f3787a010d..29c926167fb440c 100644
--- a/lldb/examples/synthetic/gnu_libstdcpp.py
+++ b/lldb/examples/synthetic/gnu_libstdcpp.py
@@ -892,3 +892,87 @@ 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..ad6d627938c0520 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -332,14 +332,12 @@ 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
- = 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);
@@ -372,7 +370,7 @@ bool CPlusPlusLanguage::MethodName::ContainsPath(llvm::StringRef path) {
return false;
if (haystack.empty() || !isalnum(haystack.back()))
return true;
-
+
return false;
}
@@ -388,7 +386,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 +1102,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 +1151,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..7a433fea5feca23
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libstdcpp/variant/TestDataFormatterLibStdcxxVariant.py
@@ -0,0 +1,69 @@
+"""
+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
+
+
+class LibStdcxxVariantDataFormatterTestCase(TestBase):
+ @add_test_categories(["libstdcxx"])
+ def test_with_run_command(self):
+ """Test LibStdC++ std::variant data formatter works correctly."""
+ self.build()
+
+ (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.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
+}
More information about the lldb-commits
mailing list