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 @@ -9,6 +9,86 @@ # before relying on these formatters to do the right thing for your setup +""" + This formatter can be applied to all + unordered map-like structures (unordered_map, unordered_multimap, unordered_set, unordered_multiset) +""" +class StdUnorderedMapSynthProvider: + def __init__(self, valobj, dict): + self.valobj = valobj + self.count = None + self.kind = self.get_object_kind(valobj) + + def get_object_kind(self, valobj): + type_name = valobj.GetTypeName() + return "set" if "set" in type_name else "map" + + 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 + # 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): + # preemptively setting this to None - we might end up changing our mind + # later + self.count = None + try: + self.head = self.valobj.GetChildMemberWithName('_M_h') + self.before_begin = self.head.GetChildMemberWithName('_M_before_begin') + self.next = self.before_begin.GetChildMemberWithName('_M_nxt') + self.data_type = self.extract_type() + self.skip_size = self.next.GetType().GetByteSize() + self.data_size = self.data_type.GetByteSize() + except: + pass + return False + + def get_child_index(self, name): + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1 + + def get_child_at_index(self, index): + logger = lldb.formatters.Logger.Logger() + logger >> "Being asked to fetch child[" + str(index) + "]" + if index < 0: + return None + if index >= self.num_children(): + return None + try: + offset = index + 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 + + def num_children(self): + if self.count is None: + self.count = self.num_children_impl() + return self.count + + def num_children_impl(self): + logger = lldb.formatters.Logger.Logger() + try: + count = self.head.GetChildMemberWithName('_M_element_count').GetValueAsUnsigned(0) + return count + except: + logger >> "Could not determine the size" + return 0 + + class AbstractListSynthProvider: def __init__(self, valobj, dict, has_prev): ''' 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 @@ -918,6 +918,11 @@ SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_deref_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); + cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add( + RegularExpression("^std::unordered_(multi)?(map|set)<.+> >$"), + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdUnorderedMapSynthProvider"))); cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add( RegularExpression("^std::(__cxx11::)?list<.+>(( )?&)?$"), SyntheticChildrenSP(new ScriptedSyntheticChildren( @@ -954,6 +959,10 @@ RegularExpression("^std::multiset<.+> >(( )?&)?$"), TypeSummaryImplSP( new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->GetRegexTypeSummariesContainer()->Add( + RegularExpression("^std::unordered_(multi)?(map|set)<.+> >$"), + TypeSummaryImplSP( + new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); cpp_category_sp->GetRegexTypeSummariesContainer()->Add( RegularExpression("^std::(__cxx11::)?list<.+>(( )?&)?$"), TypeSummaryImplSP( diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/Makefile new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/TestDataFormatterUnordered.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/TestDataFormatterUnordered.py rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/TestDataFormatterUnordered.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/TestDataFormatterGenericUnordered.py @@ -9,18 +9,19 @@ from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil +USE_LIBSTDCPP = "USE_LIBSTDCPP" +USE_LIBCPP = "USE_LIBCPP" -class LibcxxUnorderedDataFormatterTestCase(TestBase): - +class GenericUnorderedDataFormatterTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) def setUp(self): TestBase.setUp(self) self.namespace = 'std' - @add_test_categories(["libc++"]) - def test_with_run_command(self): - self.build() + + def do_test_with_run_command(self, stdlib_type): + self.build(dictionary={stdlib_type: "1"}) self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) lldbutil.run_break_set_by_source_regexp( @@ -76,3 +77,11 @@ self.expect(("frame variable %s" % var_name), patterns=patterns) self.expect(("frame variable %s" % var_name), patterns=patterns) self.runCmd("continue") + + @add_test_categories(["libstdcxx"]) + def test_with_run_command_libstdcpp(self): + self.do_test_with_run_command(USE_LIBSTDCPP) + + @add_test_categories(["libc++"]) + def test_with_run_command_libcpp(self): + self.do_test_with_run_command(USE_LIBCPP) \ No newline at end of file diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/unordered/main.cpp @@ -0,0 +1,68 @@ +#include +#include +#include + +int g_the_foo = 0; + +int thefoo_rw(int arg = 1) { + if (arg < 0) + arg = 0; + if (!arg) + arg = 1; + g_the_foo += arg; + return g_the_foo; +} + +int main() { + std::unordered_map map; + map.emplace(1, "hello"); + map.emplace(2, "world"); + map.emplace(3, "this"); + map.emplace(4, "is"); + map.emplace(5, "me"); + thefoo_rw(); // Set break point at this line. + + std::unordered_multimap mmap; + mmap.emplace(1, "hello"); + mmap.emplace(2, "hello"); + mmap.emplace(2, "world"); + mmap.emplace(3, "this"); + mmap.emplace(3, "this"); + mmap.emplace(3, "this"); + thefoo_rw(); // Set break point at this line. + + std::unordered_set iset; + iset.emplace(1); + iset.emplace(2); + iset.emplace(3); + iset.emplace(4); + iset.emplace(5); + thefoo_rw(); // Set break point at this line. + + std::unordered_set sset; + sset.emplace("hello"); + sset.emplace("world"); + sset.emplace("this"); + sset.emplace("is"); + sset.emplace("me"); + thefoo_rw(); // Set break point at this line. + + std::unordered_multiset imset; + imset.emplace(1); + imset.emplace(2); + imset.emplace(2); + imset.emplace(3); + imset.emplace(3); + imset.emplace(3); + thefoo_rw(); // Set break point at this line. + + std::unordered_multiset smset; + smset.emplace("hello"); + smset.emplace("world"); + smset.emplace("world"); + smset.emplace("is"); + smset.emplace("is"); + thefoo_rw(); // Set break point at this line. + + return 0; +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/Makefile deleted file mode 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -CXX_SOURCES := main.cpp - -# Work around "exception specification in declaration does not match previous -# declaration" errors present in older libc++ releases. This error was fixed in -# the 3.8 release. -CFLAGS_EXTRAS := -fno-exceptions - -USE_LIBCPP := 1 -include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/main.cpp deleted file mode 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unordered/main.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include - -using std::string; - -#define intstr_map std::unordered_map -#define intstr_mmap std::unordered_multimap - -#define int_set std::unordered_set -#define str_set std::unordered_set -#define int_mset std::unordered_multiset -#define str_mset std::unordered_multiset - -int g_the_foo = 0; - -int thefoo_rw(int arg = 1) -{ - if (arg < 0) - arg = 0; - if (!arg) - arg = 1; - g_the_foo += arg; - return g_the_foo; -} - -int main() -{ - intstr_map map; - map.emplace(1,"hello"); - map.emplace(2,"world"); - map.emplace(3,"this"); - map.emplace(4,"is"); - map.emplace(5,"me"); - thefoo_rw(); // Set break point at this line. - - intstr_mmap mmap; - mmap.emplace(1,"hello"); - mmap.emplace(2,"hello"); - mmap.emplace(2,"world"); - mmap.emplace(3,"this"); - mmap.emplace(3,"this"); - mmap.emplace(3,"this"); - thefoo_rw(); // Set break point at this line. - - int_set iset; - iset.emplace(1); - iset.emplace(2); - iset.emplace(3); - iset.emplace(4); - iset.emplace(5); - thefoo_rw(); // Set break point at this line. - - str_set sset; - sset.emplace("hello"); - sset.emplace("world"); - sset.emplace("this"); - sset.emplace("is"); - sset.emplace("me"); - thefoo_rw(); // Set break point at this line. - - int_mset imset; - imset.emplace(1); - imset.emplace(2); - imset.emplace(2); - imset.emplace(3); - imset.emplace(3); - imset.emplace(3); - thefoo_rw(); // Set break point at this line. - - str_mset smset; - smset.emplace("hello"); - smset.emplace("world"); - smset.emplace("world"); - smset.emplace("is"); - smset.emplace("is"); - thefoo_rw(); // Set break point at this line. - - return 0; -}