[Lldb-commits] [lldb] 2ea3c8a - [formatters] Add a deque formatter for libstdcpp and fix the libcxx one
Walter Erquinigo via lldb-commits
lldb-commits at lists.llvm.org
Mon Dec 6 13:42:14 PST 2021
Author: Walter Erquinigo
Date: 2021-12-06T13:42:03-08:00
New Revision: 2ea3c8a50add5436cf939d59c3235408ca0255c1
URL: https://github.com/llvm/llvm-project/commit/2ea3c8a50add5436cf939d59c3235408ca0255c1
DIFF: https://github.com/llvm/llvm-project/commit/2ea3c8a50add5436cf939d59c3235408ca0255c1.diff
LOG: [formatters] Add a deque formatter for libstdcpp and fix the libcxx one
This adds the formatters for libstdcpp's deque as a python
implementation. It adds comprehensive tests for the two different
storage strategies deque uses. Besides that, this fixes a couple of bugs
in the libcxx implementation. Finally, both implementation run against
the same tests.
This is a minor improvement on top of Danil Stefaniuc's formatter.
Added:
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/Makefile
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/TestDataFormatterGenericDeque.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/main.cpp
Modified:
lldb/examples/synthetic/gnu_libstdcpp.py
lldb/examples/synthetic/libcxx.py
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
Removed:
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/Makefile
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/TestDataFormatterLibcxxDeque.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/main.cpp
################################################################################
diff --git a/lldb/examples/synthetic/gnu_libstdcpp.py b/lldb/examples/synthetic/gnu_libstdcpp.py
index 87db880288265..022071322d058 100644
--- a/lldb/examples/synthetic/gnu_libstdcpp.py
+++ b/lldb/examples/synthetic/gnu_libstdcpp.py
@@ -684,3 +684,135 @@ def has_children(self):
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
index 97593725a2467..531d26784afc2 100644
--- a/lldb/examples/synthetic/libcxx.py
+++ b/lldb/examples/synthetic/libcxx.py
@@ -657,16 +657,15 @@ def find_block_size(self):
# static const
diff erence_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 @@ def get_child_at_index(self, index):
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 @@ def update(self):
'__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 @@ def update(self):
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 @@ def update(self):
self.start = None
self.map_first = None
self.map_begin = None
+ return False
class stdsharedptr_SynthProvider:
@@ -873,4 +865,3 @@ def __lldb_init_module(debugger, dict):
_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
index f1925990e94ac..589fb3190ac6a 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -903,6 +903,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
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 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
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
similarity index 73%
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
index c5df567e01a2a..99998b20bcb05 100644
--- 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
index 0000000000000..a995d842d4d65
--- /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
index 0000000000000..b948fe1b4b375
--- /dev/null
+++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/deque/main.cpp
@@ -0,0 +1,41 @@
+#include <cstdio>
+#include <deque>
+
+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 <typename T> 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<int> empty;
+ std::deque<int> deque_1 = {1};
+ std::deque<int> deque_3 = {3, 1, 2};
+
+ std::deque<Foo_small> deque_200_small;
+ deque_200_small = fill<std::deque<Foo_small>>(deque_200_small);
+
+ std::deque<Foo_large> deque_200_large;
+ deque_200_large = fill<std::deque<Foo_large>>(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
index b9949288c9892..0000000000000
--- 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
index 43c3f374a0f98..0000000000000
--- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/deque/main.cpp
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <deque>
-
-int main() {
- std::deque<int> empty;
- std::deque<int> deque_1 = {1};
- std::deque<int> deque_3 = {3, 1, 2};
- return empty.size() + deque_1.front() + deque_3.front(); // break here
-}
More information about the lldb-commits
mailing list