[formatters] Add a libstdcpp formatter for forward_list and refactor list formatter

This diff adds a data formatter for libstdcpp's forward_list. Besides, it refactors the existing code by extracting the common functionality between libstdcpp forward_list and list formatters into the AbstractListSynthProvider class.

Reviewed By: wallace

Differential Revision: https://reviews.llvm.org/D113362
This commit is contained in:
Danil Stefaniuc 2021-11-09 21:22:14 -08:00 committed by Walter Erquinigo
parent af4dc633f8
commit 577c1eecf8
6 changed files with 105 additions and 34 deletions

View File

@ -9,12 +9,17 @@ import lldb.formatters.Logger
# before relying on these formatters to do the right thing for your setup # before relying on these formatters to do the right thing for your setup
class StdListSynthProvider: class AbstractListSynthProvider:
def __init__(self, valobj, dict, has_prev):
def __init__(self, valobj, dict): '''
:param valobj: The value object of the list
:param dict: A dict with metadata provided by LLDB
:param has_prev: Whether the list supports a 'prev' pointer besides a 'next' one
'''
logger = lldb.formatters.Logger.Logger() logger = lldb.formatters.Logger.Logger()
self.valobj = valobj self.valobj = valobj
self.count = None self.count = None
self.has_prev = has_prev
logger >> "Providing synthetic children for a list named " + \ logger >> "Providing synthetic children for a list named " + \
str(valobj.GetName()) str(valobj.GetName())
@ -24,13 +29,13 @@ class StdListSynthProvider:
def is_valid(self, node): def is_valid(self, node):
logger = lldb.formatters.Logger.Logger() logger = lldb.formatters.Logger.Logger()
valid = self.value(self.next_node(node)) != self.node_address valid = self.value(self.next_node(node)) != self.get_end_of_list_address()
if valid: if valid:
logger >> "%s is valid" % str(self.valobj.GetName()) logger >> "%s is valid" % str(self.valobj.GetName())
else: else:
logger >> "synthetic value is not valid" logger >> "synthetic value is not valid"
return valid return valid
def value(self, node): def value(self, node):
logger = lldb.formatters.Logger.Logger() logger = lldb.formatters.Logger.Logger()
value = node.GetValueAsUnsigned() value = node.GetValueAsUnsigned()
@ -73,26 +78,30 @@ class StdListSynthProvider:
def num_children_impl(self): def num_children_impl(self):
logger = lldb.formatters.Logger.Logger() logger = lldb.formatters.Logger.Logger()
try: try:
next_val = self.next.GetValueAsUnsigned(0)
prev_val = self.prev.GetValueAsUnsigned(0)
# After a std::list has been initialized, both next and prev will # After a std::list has been initialized, both next and prev will
# be non-NULL # be non-NULL
if next_val == 0 or prev_val == 0: next_val = self.next.GetValueAsUnsigned(0)
if next_val == 0:
return 0 return 0
if next_val == self.node_address:
return 0
if next_val == prev_val:
return 1
if self.has_loop(): if self.has_loop():
return 0 return 0
size = 2 if self.has_prev:
prev_val = self.prev.GetValueAsUnsigned(0)
if prev_val == 0:
return 0
if next_val == self.node_address:
return 0
if next_val == prev_val:
return 1
size = 1
current = self.next current = self.next
while current.GetChildMemberWithName( while current.GetChildMemberWithName(
'_M_next').GetValueAsUnsigned(0) != self.node_address: '_M_next').GetValueAsUnsigned(0) != self.get_end_of_list_address():
size = size + 1 size = size + 1
current = current.GetChildMemberWithName('_M_next') current = current.GetChildMemberWithName('_M_next')
return (size - 1) return size
except: except:
logger >> "Error determining the size"
return 0 return 0
def get_child_index(self, name): def get_child_index(self, name):
@ -101,7 +110,7 @@ class StdListSynthProvider:
return int(name.lstrip('[').rstrip(']')) return int(name.lstrip('[').rstrip(']'))
except: except:
return -1 return -1
def get_child_at_index(self, index): def get_child_at_index(self, index):
logger = lldb.formatters.Logger.Logger() logger = lldb.formatters.Logger.Logger()
logger >> "Fetching child " + str(index) logger >> "Fetching child " + str(index)
@ -115,9 +124,11 @@ class StdListSynthProvider:
while offset > 0: while offset > 0:
current = current.GetChildMemberWithName('_M_next') current = current.GetChildMemberWithName('_M_next')
offset = offset - 1 offset = offset - 1
# C++ lists store the data of a node after its pointers. In the case of a forward list, there's just one pointer (next), and
# in the case of a double-linked list, there's an additional pointer (prev).
return current.CreateChildAtOffset( return current.CreateChildAtOffset(
'[' + str(index) + ']', '[' + str(index) + ']',
2 * current.GetType().GetByteSize(), (2 if self.has_prev else 1) * current.GetType().GetByteSize(),
self.data_type) self.data_type)
except: except:
return None return None
@ -139,19 +150,60 @@ class StdListSynthProvider:
# later # later
self.count = None self.count = None
try: try:
impl = self.valobj.GetChildMemberWithName('_M_impl') self.impl = self.valobj.GetChildMemberWithName('_M_impl')
self.node = impl.GetChildMemberWithName('_M_node')
self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
self.next = self.node.GetChildMemberWithName('_M_next')
self.prev = self.node.GetChildMemberWithName('_M_prev')
self.data_type = self.extract_type() self.data_type = self.extract_type()
self.data_size = self.data_type.GetByteSize() self.data_size = self.data_type.GetByteSize()
self.updateNodes()
except: except:
pass pass
return False return False
'''
Method is used to extract the list pointers into the variables (e.g self.node, self.next, and optionally to self.prev)
and is mandatory to be overriden in each AbstractListSynthProvider subclass
'''
def updateNodes(self):
raise NotImplementedError
def has_children(self): def has_children(self):
return True 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
'''
def get_end_of_list_address(self):
raise NotImplementedError
class StdForwardListSynthProvider(AbstractListSynthProvider):
def __init__(self, valobj, dict):
has_prev = False
super().__init__(valobj, dict, has_prev)
def updateNodes(self):
self.node = self.impl.GetChildMemberWithName('_M_head')
self.next = self.node.GetChildMemberWithName('_M_next')
def get_end_of_list_address(self):
return 0
class StdListSynthProvider(AbstractListSynthProvider):
def __init__(self, valobj, dict):
has_prev = True
super().__init__(valobj, dict, has_prev)
def updateNodes(self):
self.node_address = self.valobj.AddressOf().GetValueAsUnsigned(0)
self.node = self.impl.GetChildMemberWithName('_M_node')
self.prev = self.node.GetChildMemberWithName('_M_prev')
self.next = self.node.GetChildMemberWithName('_M_next')
def get_end_of_list_address(self):
return self.node_address
class StdVectorSynthProvider: class StdVectorSynthProvider:

View File

@ -923,6 +923,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
SyntheticChildrenSP(new ScriptedSyntheticChildren( SyntheticChildrenSP(new ScriptedSyntheticChildren(
stl_synth_flags, stl_synth_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider")));
cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
RegularExpression("^std::(__cxx11::)?forward_list<.+>(( )?&)?$"),
SyntheticChildrenSP(new ScriptedSyntheticChildren(
stl_synth_flags,
"lldb.formatters.cpp.gnu_libstdcpp.StdForwardListSynthProvider")));
stl_summary_flags.SetDontShowChildren(false); stl_summary_flags.SetDontShowChildren(false);
stl_summary_flags.SetSkipPointers(false); stl_summary_flags.SetSkipPointers(false);
cpp_category_sp->GetRegexTypeSummariesContainer()->Add( cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
@ -953,6 +958,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
RegularExpression("^std::(__cxx11::)?list<.+>(( )?&)?$"), RegularExpression("^std::(__cxx11::)?list<.+>(( )?&)?$"),
TypeSummaryImplSP( TypeSummaryImplSP(
new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
RegularExpression("^std::(__cxx11::)?forward_list<.+>(( )?&)?$"),
TypeSummaryImplSP(
new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
AddCXXSynthetic( AddCXXSynthetic(
cpp_category_sp, cpp_category_sp,

View File

@ -1,4 +1,3 @@
CXX_SOURCES := main.cpp CXX_SOURCES := main.cpp
USE_LIBCPP := 1
include Makefile.rules include Makefile.rules

View File

@ -9,8 +9,10 @@ from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import * from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil from lldbsuite.test import lldbutil
USE_LIBSTDCPP = "USE_LIBSTDCPP"
USE_LIBCPP = "USE_LIBCPP"
class TestDataFormatterLibcxxForwardList(TestBase): class TestDataFormatterGenericForwardList(TestBase):
mydir = TestBase.compute_mydir(__file__) mydir = TestBase.compute_mydir(__file__)
@ -19,10 +21,10 @@ class TestDataFormatterLibcxxForwardList(TestBase):
self.line = line_number('main.cpp', '// break here') self.line = line_number('main.cpp', '// break here')
self.namespace = 'std' self.namespace = 'std'
@add_test_categories(["libc++"])
def test(self): def do_test(self, stdlib_type):
"""Test that std::forward_list is displayed correctly""" """Test that std::forward_list is displayed correctly"""
self.build() self.build(dictionary={stdlib_type: "1"})
lldbutil.run_to_source_breakpoint(self, '// break here', lldbutil.run_to_source_breakpoint(self, '// break here',
lldb.SBFileSpec("main.cpp", False)) lldb.SBFileSpec("main.cpp", False))
@ -49,3 +51,12 @@ class TestDataFormatterLibcxxForwardList(TestBase):
'[3] = 4444', '[3] = 4444',
'[4] = 55555', '[4] = 55555',
'}']) '}'])
@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)

View File

@ -0,0 +1,7 @@
#include <forward_list>
int main() {
std::forward_list<int> empty{}, one_elt{47},
five_elts{1, 22, 333, 4444, 55555};
return 0; // break here
}

View File

@ -1,7 +0,0 @@
#include <forward_list>
int main()
{
std::forward_list<int> empty{}, one_elt{47}, five_elts{1,22,333,4444,55555};
return 0; // break here
}