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 @@ -684,3 +684,135 @@ return True _list_uses_loop_detector = True + +class StdDequeSynthProvider: + def __init__(self, valobj, d): + self.valobj = valobj + self.pointer_size = self.valobj.GetProcess().GetAddressByteSize() + self.count = None + self.block_size = -1 + self.element_size = -1 + self.find_block_size() + + + def find_block_size(self): + # in order to use the deque we must have the block size, or else + # it's impossible to know what memory addresses are valid + self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) + if not self.element_type.IsValid(): + return + self.element_size = self.element_type.GetByteSize() + # The block size (i.e. number of elements per subarray) is defined in + # this piece of code, so we need to replicate it. + # + # #define _GLIBCXX_DEQUE_BUF_SIZE 512 + # + # return (__size < _GLIBCXX_DEQUE_BUF_SIZE + # ? size_t(_GLIBCXX_DEQUE_BUF_SIZE / __size) : size_t(1)); + if self.element_size < 512: + self.block_size = 512 // self.element_size + else: + self.block_size = 1 + + def num_children(self): + if self.count is None: + return 0 + return self.count + + def has_children(self): + return True + + def get_child_index(self, name): + try: + return int(name.lstrip('[').rstrip(']')) + except: + return -1 + + def get_child_at_index(self, index): + if index < 0 or self.count is None: + return None + if index >= self.num_children(): + return None + try: + name = '[' + str(index) + ']' + # We first look for the element in the first subarray, + # which might be incomplete. + if index < self.first_node_size: + # The following statement is valid because self.first_elem is the pointer + # to the first element + return self.first_elem.CreateChildAtOffset(name, index * self.element_size, self.element_type) + + # Now the rest of the subarrays except for maybe the last one + # are going to be complete, so the final expression is simpler + i, j = divmod(index - self.first_node_size, self.block_size) + + # We first move to the beginning of the node/subarray were our element is + node = self.start_node.CreateChildAtOffset( + '', + (1 + i) * self.valobj.GetProcess().GetAddressByteSize(), + self.element_type.GetPointerType()) + return node.CreateChildAtOffset(name, j * self.element_size, self.element_type) + + except: + return None + + def update(self): + logger = lldb.formatters.Logger.Logger() + self.count = 0 + try: + # A deque is effectively a two-dim array, with fixed width. + # However, only a subset of this memory contains valid data + # since a deque may have some slack at the front and back in + # order to have O(1) insertion at both ends. + # The rows in active use are delimited by '_M_start' and + # '_M_finish'. + # + # To find the elements that are actually constructed, the 'start' + # variable tells which element in this NxM array is the 0th + # one. + if self.block_size < 0 or self.element_size < 0: + return False + + count = 0 + + impl = self.valobj.GetChildMemberWithName('_M_impl') + + # we calculate the size of the first node (i.e. first internal array) + self.start = impl.GetChildMemberWithName('_M_start') + self.start_node = self.start.GetChildMemberWithName('_M_node') + first_node_address = self.start_node.GetValueAsUnsigned(0) + first_node_last_elem = self.start.GetChildMemberWithName('_M_last').GetValueAsUnsigned(0) + self.first_elem = self.start.GetChildMemberWithName('_M_cur') + first_node_first_elem = self.first_elem.GetValueAsUnsigned(0) + + + finish = impl.GetChildMemberWithName('_M_finish') + last_node_address = finish.GetChildMemberWithName('_M_node').GetValueAsUnsigned(0) + last_node_first_elem = finish.GetChildMemberWithName('_M_first').GetValueAsUnsigned(0) + last_node_last_elem = finish.GetChildMemberWithName('_M_cur').GetValueAsUnsigned(0) + + if first_node_first_elem == 0 or first_node_last_elem == 0 or first_node_first_elem > first_node_last_elem: + return False + if last_node_first_elem == 0 or last_node_last_elem == 0 or last_node_first_elem > last_node_last_elem: + return False + + + if last_node_address == first_node_address: + self.first_node_size = (last_node_last_elem - first_node_first_elem) // self.element_size + count += self.first_node_size + else: + self.first_node_size = (first_node_last_elem - first_node_first_elem) // self.element_size + count += self.first_node_size + + # we calculate the size of the last node + finish = impl.GetChildMemberWithName('_M_finish') + last_node_address = finish.GetChildMemberWithName('_M_node').GetValueAsUnsigned(0) + count += (last_node_last_elem - last_node_first_elem) // self.element_size + + # we calculate the size of the intermediate nodes + num_intermediate_nodes = (last_node_address - first_node_address - 1) // self.valobj.GetProcess().GetAddressByteSize() + count += self.block_size * num_intermediate_nodes + self.count = count + except: + pass + return False diff --git a/lldb/examples/synthetic/libcxx.py b/lldb/examples/synthetic/libcxx.py --- a/lldb/examples/synthetic/libcxx.py +++ b/lldb/examples/synthetic/libcxx.py @@ -657,16 +657,15 @@ # static const difference_type __block_size = sizeof(value_type) < 256 ? 4096 / sizeof(value_type) : 16; # } if self.element_size < 256: - self.block_size = 4096 / self.element_size + self.block_size = 4096 // self.element_size else: self.block_size = 16 def num_children(self): - global _deque_capping_size logger = lldb.formatters.Logger.Logger() if self.count is None: return 0 - return min(self.count, _deque_capping_size) + return self.count def has_children(self): return True @@ -687,9 +686,10 @@ return None try: i, j = divmod(self.start + index, self.block_size) + return self.first.CreateValueFromExpression( '[' + str(index) + ']', '*(*(%s + %d) + %d)' % - (self.first.get_expr_path(), i, j)) + (self.map_begin.get_expr_path(), i, j)) except: return None @@ -727,12 +727,14 @@ '__start_').GetValueAsUnsigned(0) first = map_.GetChildMemberWithName('__first_') map_first = first.GetValueAsUnsigned(0) - map_begin = map_.GetChildMemberWithName( - '__begin_').GetValueAsUnsigned(0) + self.map_begin = map_.GetChildMemberWithName( + '__begin_') + map_begin = self.map_begin.GetValueAsUnsigned(0) map_end = map_.GetChildMemberWithName( '__end_').GetValueAsUnsigned(0) map_endcap = self._get_value_of_compressed_pair( map_.GetChildMemberWithName( '__end_cap_')) + # check consistency if not map_first <= map_begin <= map_end <= map_endcap: logger.write("map pointers are not monotonic") @@ -750,18 +752,7 @@ if junk: logger.write("begin-first doesnt align correctly") return - if not start_row * \ - self.block_size <= start < (start_row + 1) * self.block_size: - logger.write("0th element must be in the 'begin' row") - return - end_row = start_row + active_rows - if not count: - if active_rows: - logger.write("empty deque but begin!=end") - return - elif not (end_row - 1) * self.block_size <= start + count < end_row * self.block_size: - logger.write("nth element must be before the 'end' row") - return + logger.write( "update success: count=%r, start=%r, first=%r" % (count, start, first)) @@ -774,6 +765,7 @@ self.start = None self.map_first = None self.map_begin = None + return False class stdsharedptr_SynthProvider: @@ -873,4 +865,3 @@ _map_capping_size = 255 _list_capping_size = 255 _list_uses_loop_detector = True -_deque_capping_size = 255 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 @@ -903,6 +903,11 @@ SyntheticChildrenSP(new ScriptedSyntheticChildren( stl_synth_flags, "lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider"))); + cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add( + RegularExpression("^std::deque<.+>(( )?&)?$"), + SyntheticChildrenSP(new ScriptedSyntheticChildren( + stl_deref_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdDequeSynthProvider"))); cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add( RegularExpression("^std::set<.+> >(( )?&)?$"), SyntheticChildrenSP(new ScriptedSyntheticChildren( @@ -962,6 +967,10 @@ RegularExpression("^std::set<.+> >(( )?&)?$"), TypeSummaryImplSP( new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); + cpp_category_sp->GetRegexTypeSummariesContainer()->Add( + RegularExpression("^std::deque<.+>(( )?&)?$"), + TypeSummaryImplSP( + new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); cpp_category_sp->GetRegexTypeSummariesContainer()->Add( RegularExpression("^std::multimap<.+> >(( )?&)?$"), TypeSummaryImplSP( diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/Makefile b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/Makefile rename from lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/Makefile rename to lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/Makefile --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/Makefile +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/Makefile @@ -1,5 +1,3 @@ CXX_SOURCES := main.cpp -USE_LIBCPP := 1 - include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py @@ -0,0 +1,76 @@ +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 GenericDequeDataFormatterTestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + def findVariable(self, name): + var = self.frame().FindVariable(name) + self.assertTrue(var.IsValid()) + return var + + def getVariableType(self, name): + var = self.findVariable(name) + return var.GetType().GetDisplayTypeName() + + def check_size(self, var_name, size): + var = self.findVariable(var_name) + self.assertEqual(var.GetNumChildren(), size) + + + def do_test(self, stdlib_type): + self.build(dictionary={stdlib_type: '1'}) + lldbutil.run_to_source_breakpoint(self, "break here", + lldb.SBFileSpec("main.cpp")) + + self.expect_expr("empty", result_children=[]) + self.expect_expr("deque_1", result_children=[ + ValueCheck(name="[0]", value="1"), + ]) + self.expect_expr("deque_3", result_children=[ + ValueCheck(name="[0]", value="3"), + ValueCheck(name="[1]", value="1"), + ValueCheck(name="[2]", value="2") + ]) + + self.check_size("deque_200_small", 200) + for i in range(0, 100): + self.expect_var_path("deque_200_small[%d]"%(i), children=[ + ValueCheck(name="a", value=str(-99 + i)), + ValueCheck(name="b", value=str(-100 + i)), + ValueCheck(name="c", value=str(-101 + i)), + ]) + self.expect_var_path("deque_200_small[%d]"%(i + 100), children=[ + ValueCheck(name="a", value=str(i)), + ValueCheck(name="b", value=str(1 + i)), + ValueCheck(name="c", value=str(2 + i)), + ]) + + self.check_size("deque_200_large", 200) + for i in range(0, 100): + self.expect_var_path("deque_200_large[%d]"%(i), children=[ + ValueCheck(name="a", value=str(-99 + i)), + ValueCheck(name="b", value=str(-100 + i)), + ValueCheck(name="c", value=str(-101 + i)), + ValueCheck(name="d") + ]) + self.expect_var_path("deque_200_large[%d]"%(i + 100), children=[ + ValueCheck(name="a", value=str(i)), + ValueCheck(name="b", value=str(1 + i)), + ValueCheck(name="c", value=str(2 + i)), + ValueCheck(name="d") + ]) + + @add_test_categories(["libstdcxx"]) + def test_libstdcpp(self): + self.do_test(USE_LIBSTDCPP) + + @add_test_categories(["libc++"]) + def test_libcpp(self): + self.do_test(USE_LIBCPP) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/main.cpp new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/main.cpp @@ -0,0 +1,41 @@ +#include +#include + +struct Foo_small { + int a; + int b; + int c; + + Foo_small(int a, int b, int c) : a(a), b(b), c(c) {} +}; + +struct Foo_large { + int a; + int b; + int c; + char d[1000] = {0}; + + Foo_large(int a, int b, int c) : a(a), b(b), c(c) {} +}; + +template T fill(T deque) { + for (int i = 0; i < 100; i++) { + deque.push_back({i, i + 1, i + 2}); + deque.push_front({-i, -(i + 1), -(i + 2)}); + } + return deque; +} + +int main() { + std::deque empty; + std::deque deque_1 = {1}; + std::deque deque_3 = {3, 1, 2}; + + std::deque deque_200_small; + deque_200_small = fill>(deque_200_small); + + std::deque deque_200_large; + deque_200_large = fill>(deque_200_large); + + return empty.size() + deque_1.front() + deque_3.front(); // break here +} diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/TestDataFormatterLibcxxDeque.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/TestDataFormatterLibcxxDeque.py deleted file mode 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/TestDataFormatterLibcxxDeque.py +++ /dev/null @@ -1,25 +0,0 @@ -import lldb -from lldbsuite.test.decorators import * -from lldbsuite.test.lldbtest import * -from lldbsuite.test import lldbutil - - -class LibcxxDequeDataFormatterTestCase(TestBase): - - mydir = TestBase.compute_mydir(__file__) - - @add_test_categories(["libc++"]) - def test(self): - self.build() - lldbutil.run_to_source_breakpoint(self, "break here", - lldb.SBFileSpec("main.cpp")) - - self.expect_expr("empty", result_children=[]) - self.expect_expr("deque_1", result_children=[ - ValueCheck(name="[0]", value="1"), - ]) - self.expect_expr("deque_3", result_children=[ - ValueCheck(name="[0]", value="3"), - ValueCheck(name="[1]", value="1"), - ValueCheck(name="[2]", value="2") - ]) diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/main.cpp b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/main.cpp deleted file mode 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/main.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include - -int main() { - std::deque empty; - std::deque deque_1 = {1}; - std::deque deque_3 = {3, 1, 2}; - return empty.size() + deque_1.front() + deque_3.front(); // break here -}