[Lldb-commits] [lldb] e3dea5c - [formatters] Add a formatter for libstdc++ optional
Walter Erquinigo via lldb-commits
lldb-commits at lists.llvm.org
Mon Nov 22 15:36:59 PST 2021
Author: Walter Erquinigo
Date: 2021-11-22T15:36:46-08:00
New Revision: e3dea5cf0e326366ab95a49d167fde8b0816e292
URL: https://github.com/llvm/llvm-project/commit/e3dea5cf0e326366ab95a49d167fde8b0816e292
DIFF: https://github.com/llvm/llvm-project/commit/e3dea5cf0e326366ab95a49d167fde8b0816e292.diff
LOG: [formatters] Add a formatter for libstdc++ optional
Besides adding the formatter and the summary, this makes the libcxx
tests also work for this case.
This is the polished version of https://reviews.llvm.org/D114266,
authored by Danil Stefaniuc.
Differential Revision: https://reviews.llvm.org/D114403
Added:
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/Makefile
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/main.cpp
Modified:
lldb/bindings/interface/SBValue.i
lldb/examples/synthetic/gnu_libstdcpp.py
lldb/include/lldb/API/SBValue.h
lldb/source/API/SBValue.cpp
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
Removed:
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/Makefile
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/TestDataFormatterLibcxxOptional.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/main.cpp
################################################################################
diff --git a/lldb/bindings/interface/SBValue.i b/lldb/bindings/interface/SBValue.i
index dd012e667a20c..bc66a4ae28f82 100644
--- a/lldb/bindings/interface/SBValue.i
+++ b/lldb/bindings/interface/SBValue.i
@@ -410,6 +410,9 @@ public:
bool
SetData (lldb::SBData &data, lldb::SBError& error);
+ lldb::SBValue
+ Clone(const char *new_name);
+
lldb::addr_t
GetLoadAddress();
diff --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py
index 9272b814f78d8..27fca0a5d3271 100644
--- a/lldb/examples/synthetic/gnu_libstdcpp.py
+++ b/lldb/examples/synthetic/gnu_libstdcpp.py
@@ -8,6 +8,34 @@
# You are encouraged to look at the STL implementation for your platform
# before relying on these formatters to do the right thing for your setup
+def StdOptionalSummaryProvider(valobj, dict):
+ has_value = valobj.GetNumChildren() > 0
+ # We add wrapping spaces for consistency with the libcxx formatter
+ return " Has Value=" + ("true" if has_value else "false") + " "
+
+
+class StdOptionalSynthProvider:
+ def __init__(self, valobj, dict):
+ self.valobj = valobj
+
+ def update(self):
+ try:
+ self.payload = self.valobj.GetChildMemberWithName('_M_payload')
+ self.value = self.payload.GetChildMemberWithName('_M_payload')
+ self.count = self.payload.GetChildMemberWithName('_M_engaged').GetValueAsUnsigned(0)
+ except:
+ self.count = 0
+ return False
+
+
+ def num_children(self):
+ return self.count
+
+ def get_child_index(self, name):
+ return 0
+
+ def get_child_at_index(self, index):
+ return self.value.Clone('Value')
"""
This formatter can be applied to all
@@ -26,15 +54,15 @@ def get_object_kind(self, valobj):
def extract_type(self):
type = self.valobj.GetType()
# type of std::pair<key, value> is the first template
- # argument type of the 4th template argument to std::map and
- # 3rd template argument for std::set. That's why
+ # argument type of the 4th template argument to std::map and
+ # 3rd template argument for std::set. That's why
# we need to know kind of the object
template_arg_num = 4 if self.kind == "map" else 3
allocator_type = type.GetTemplateArgumentType(template_arg_num)
data_type = allocator_type.GetTemplateArgumentType(0)
return data_type
- def update(self):
+ def update(self):
# preemptively setting this to None - we might end up changing our mind
# later
self.count = None
@@ -64,12 +92,12 @@ def get_child_at_index(self, index):
return None
try:
offset = index
- current = self.next
+ current = self.next
while offset > 0:
current = current.GetChildMemberWithName('_M_nxt')
offset = offset - 1
return current.CreateChildAtOffset( '[' + str(index) + ']', self.skip_size, self.data_type)
-
+
except:
logger >> "Cannot get child"
return None
@@ -115,7 +143,7 @@ def is_valid(self, node):
else:
logger >> "synthetic value is not valid"
return valid
-
+
def value(self, node):
logger = lldb.formatters.Logger.Logger()
value = node.GetValueAsUnsigned()
@@ -161,7 +189,7 @@ def num_children_impl(self):
# After a std::list has been initialized, both next and prev will
# be non-NULL
next_val = self.next.GetValueAsUnsigned(0)
- if next_val == 0:
+ if next_val == 0:
return 0
if self.has_loop():
return 0
@@ -172,14 +200,14 @@ def num_children_impl(self):
if next_val == self.node_address:
return 0
if next_val == prev_val:
- return 1
+ return 1
size = 1
current = self.next
while current.GetChildMemberWithName(
'_M_next').GetValueAsUnsigned(0) != self.get_end_of_list_address():
size = size + 1
current = current.GetChildMemberWithName('_M_next')
- return size
+ return size
except:
logger >> "Error determining the size"
return 0
@@ -190,7 +218,7 @@ def get_child_index(self, name):
return int(name.lstrip('[').rstrip(']'))
except:
return -1
-
+
def get_child_at_index(self, index):
logger = lldb.formatters.Logger.Logger()
logger >> "Fetching child " + str(index)
@@ -247,7 +275,7 @@ def updateNodes(self):
def has_children(self):
return True
-
+
'''
Method is used to identify if a node traversal has reached its end
and is mandatory to be overriden in each AbstractListSynthProvider subclass
@@ -368,7 +396,7 @@ def update(self):
self.count = 0
except:
pass
- return False
+ return False
class StdVBoolImplementation(object):
diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index 69be02545b35d..a8578abec6b7e 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -246,6 +246,12 @@ class LLDB_API SBValue {
bool SetData(lldb::SBData &data, lldb::SBError &error);
+ /// Creates a copy of the SBValue with a new name and setting the current
+ /// SBValue as its parent. It should be used when we want to change the
+ /// name of a SBValue without modifying the actual SBValue itself
+ /// (e.g. sythetic child provider).
+ lldb::SBValue Clone(const char *new_name);
+
lldb::SBDeclaration GetDeclaration();
/// Find out if a SBValue might have children.
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index 9faee102c5e3d..e3325b8d36fa9 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -1431,6 +1431,18 @@ bool SBValue::SetData(lldb::SBData &data, SBError &error) {
return ret;
}
+lldb::SBValue SBValue::Clone(const char *new_name) {
+ LLDB_RECORD_METHOD(lldb::SBValue, SBValue, Clone, (const char *), new_name);
+
+ ValueLocker locker;
+ lldb::ValueObjectSP value_sp(GetSP(locker));
+
+ if (value_sp)
+ return lldb::SBValue(value_sp->Clone(ConstString(new_name)));
+ else
+ return lldb::SBValue();
+}
+
lldb::SBDeclaration SBValue::GetDeclaration() {
LLDB_RECORD_METHOD_NO_ARGS(lldb::SBDeclaration, SBValue, GetDeclaration);
@@ -1656,6 +1668,7 @@ void RegisterMethods<SBValue>(Registry &R) {
LLDB_REGISTER_METHOD(lldb::SBData, SBValue, GetData, ());
LLDB_REGISTER_METHOD(bool, SBValue, SetData,
(lldb::SBData &, lldb::SBError &));
+ LLDB_REGISTER_METHOD(lldb::SBValue, SBValue, Clone, (const char *));
LLDB_REGISTER_METHOD(lldb::SBDeclaration, SBValue, GetDeclaration, ());
LLDB_REGISTER_METHOD(lldb::SBWatchpoint, SBValue, Watch,
(bool, bool, bool, lldb::SBError &));
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 3182294abd448..0cb80e06df498 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -913,6 +913,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
SyntheticChildrenSP(new ScriptedSyntheticChildren(
stl_deref_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
+ cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
+ RegularExpression("^std::optional<.+>(( )?&)?$"),
+ SyntheticChildrenSP(new ScriptedSyntheticChildren(
+ stl_synth_flags,
+ "lldb.formatters.cpp.gnu_libstdcpp.StdOptionalSynthProvider")));
cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
RegularExpression("^std::multiset<.+> >(( )?&)?$"),
SyntheticChildrenSP(new ScriptedSyntheticChildren(
@@ -933,8 +938,14 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
SyntheticChildrenSP(new ScriptedSyntheticChildren(
stl_synth_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider")));
+
stl_summary_flags.SetDontShowChildren(false);
stl_summary_flags.SetSkipPointers(false);
+ cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
+ RegularExpression("^std::optional<.+>(( )?&)?$"),
+ TypeSummaryImplSP(new ScriptSummaryFormat(
+ stl_summary_flags,
+ "lldb.formatters.cpp.gnu_libstdcpp.StdOptionalSummaryProvider")));
cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
RegularExpression("^std::bitset<.+>(( )?&)?$"),
TypeSummaryImplSP(
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/Makefile
similarity index 84%
rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/Makefile
rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/Makefile
index 23496eb206572..2ee2cf4d2c05c 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/Makefile
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/Makefile
@@ -1,6 +1,4 @@
CXX_SOURCES := main.cpp
-USE_LIBCPP := 1
-
CXXFLAGS_EXTRAS := -std=c++17 -fno-exceptions
include Makefile.rules
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/TestDataFormatterLibcxxOptional.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
similarity index 75%
rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/TestDataFormatterLibcxxOptional.py
rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
index 27c8d7f474ed1..557875132a656 100644
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/TestDataFormatterLibcxxOptional.py
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/TestDataFormatterGenericOptional.py
@@ -1,29 +1,18 @@
-"""
-Test lldb data formatter subsystem.
-"""
-
-
-
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+USE_LIBSTDCPP = "USE_LIBSTDCPP"
+USE_LIBCPP = "USE_LIBCPP"
-class LibcxxOptionalDataFormatterTestCase(TestBase):
+class GenericOptionalDataFormatterTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
- @add_test_categories(["libc++"])
- ## Clang 7.0 is the oldest Clang that can reliably parse newer libc++ versions
- ## with -std=c++17.
- @skipIf(oslist=no_match(["macosx"]), compiler="clang", compiler_version=['<', '7.0'])
- ## We are skipping gcc version less that 5.1 since this test requires -std=c++17
- @skipIf(compiler="gcc", compiler_version=['<', '5.1'])
-
- def test_with_run_command(self):
+ def do_test_with_run_command(self, stdlib_type):
"""Test that that file and class static variables display correctly."""
- self.build()
+ self.build(dictionary={stdlib_type: "1"})
self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
bkpt = self.target().FindBreakpointByID(
@@ -45,7 +34,7 @@ def test_with_run_command(self):
## detected we have a sufficient libc++ version to support optional
## false means we do not and therefore should skip the test
if output.find("(bool) has_optional = false") != -1 :
- self.skipTest( "Optional not supported" )
+ self.skipTest( "Optional not supported" )
lldbutil.continue_to_breakpoint(self.process(), bkpt)
@@ -71,3 +60,21 @@ def test_with_run_command(self):
substrs=['(optional_string) ostring = Has Value=true {',
'Value = "hello"',
'}'])
+
+ @add_test_categories(["libc++"])
+ ## Clang 7.0 is the oldest Clang that can reliably parse newer libc++ versions
+ ## with -std=c++17.
+ @skipIf(oslist=no_match(["macosx"]), compiler="clang", compiler_version=['<', '7.0'])
+ ## We are skipping gcc version less that 5.1 since this test requires -std=c++17
+ @skipIf(compiler="gcc", compiler_version=['<', '5.1'])
+ def test_with_run_command_libcpp(self):
+ self.do_test_with_run_command(USE_LIBCPP)
+
+ @add_test_categories(["libstdcxx"])
+ ## Clang 7.0 is the oldest Clang that can reliably parse newer libc++ versions
+ ## with -std=c++17.
+ @skipIf(compiler="clang", compiler_version=['<', '7.0'])
+ ## We are skipping gcc version less that 5.1 since this test requires -std=c++17
+ @skipIf(compiler="gcc", compiler_version=['<', '5.1'])
+ def test_with_run_command_libstdcpp(self):
+ self.do_test_with_run_command(USE_LIBSTDCPP)
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/main.cpp
new file mode 100644
index 0000000000000..8f30df4626636
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/main.cpp
@@ -0,0 +1,41 @@
+#include <cstdio>
+#include <string>
+#include <vector>
+
+// If we have libc++ 4.0 or greater we should have <optional>
+// According to libc++ C++1z status page
+// https://libcxx.llvm.org/cxx1z_status.html
+#if !defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 4000
+#include <optional>
+#define HAVE_OPTIONAL 1
+#else
+#define HAVE_OPTIONAL 0
+#endif
+
+int main() {
+ bool has_optional = HAVE_OPTIONAL;
+
+ printf("%d\n", has_optional); // break here
+
+#if HAVE_OPTIONAL == 1
+ using int_vect = std::vector<int>;
+ using optional_int = std::optional<int>;
+ using optional_int_vect = std::optional<int_vect>;
+ using optional_string = std::optional<std::string>;
+
+ optional_int number_not_engaged;
+ optional_int number_engaged = 42;
+
+ printf("%d\n", *number_engaged);
+
+ optional_int_vect numbers{{1, 2, 3, 4}};
+
+ printf("%d %d\n", numbers.value()[0], numbers.value()[1]);
+
+ optional_string ostring = "hello";
+
+ printf("%s\n", ostring->c_str());
+#endif
+
+ return 0; // break here
+}
diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/main.cpp
deleted file mode 100644
index 16bb98c61056a..0000000000000
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/main.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#include <cstdio>
-#include <string>
-#include <vector>
-
-// If we have libc++ 4.0 or greater we should have <optional>
-// According to libc++ C++1z status page https://libcxx.llvm.org/cxx1z_status.html
-#if _LIBCPP_VERSION >= 4000
-#include <optional>
-#define HAVE_OPTIONAL 1
-#else
-#define HAVE_OPTIONAL 0
-#endif
-
-
-int main()
-{
- bool has_optional = HAVE_OPTIONAL ;
-
- printf( "%d\n", has_optional ) ; // break here
-
-#if HAVE_OPTIONAL == 1
- using int_vect = std::vector<int> ;
- using optional_int = std::optional<int> ;
- using optional_int_vect = std::optional<int_vect> ;
- using optional_string = std::optional<std::string> ;
-
- optional_int number_not_engaged ;
- optional_int number_engaged = 42 ;
-
- printf( "%d\n", *number_engaged) ;
-
- optional_int_vect numbers{{1,2,3,4}} ;
-
- printf( "%d %d\n", numbers.value()[0], numbers.value()[1] ) ;
-
- optional_string ostring = "hello" ;
-
- printf( "%s\n", ostring->c_str() ) ;
-#endif
-
- return 0; // break here
-}
More information about the lldb-commits
mailing list