diff --git a/lldb/bindings/interface/SBValue.i b/lldb/bindings/interface/SBValue.i --- a/lldb/bindings/interface/SBValue.i +++ b/lldb/bindings/interface/SBValue.i @@ -410,6 +410,9 @@ 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 --- 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 extract_type(self): type = self.valobj.GetType() # type of std::pair 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 @@ 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 @@ 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 @@ # 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 @@ 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 @@ 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 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 @@ 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 --- a/lldb/include/lldb/API/SBValue.h +++ b/lldb/include/lldb/API/SBValue.h @@ -246,6 +246,12 @@ bool SetData(lldb::SBData &data, lldb::SBError &error); + /// Creates a copy of the SBValue with a new name and setting the current + /// ValueObject as its parent. It should be used when we want to change the + /// name of a ValueObject without modifying the actual ValueObject 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 --- a/lldb/source/API/SBValue.cpp +++ b/lldb/source/API/SBValue.cpp @@ -1431,6 +1431,18 @@ 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 @@ 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 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -913,6 +913,11 @@ 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 @@ 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 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 --- 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 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 --- 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 @@ ## 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 @@ 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 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/optional/main.cpp @@ -0,0 +1,41 @@ +#include +#include +#include + +// If we have libc++ 4.0 or greater we should have +// According to libc++ C++1z status page +// https://libcxx.llvm.org/cxx1z_status.html +#if !defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 4000 +#include +#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; + using optional_int = std::optional; + using optional_int_vect = std::optional; + using optional_string = std::optional; + + 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 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/optional/main.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include -#include - -// If we have libc++ 4.0 or greater we should have -// According to libc++ C++1z status page https://libcxx.llvm.org/cxx1z_status.html -#if _LIBCPP_VERSION >= 4000 -#include -#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 ; - using optional_int = std::optional ; - using optional_int_vect = std::optional ; - using optional_string = std::optional ; - - 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 -}