[formatters] List and forward_list capping_size determination and application

This diff is adding the capping_size determination for the list and forward list, to limit the number of children to be displayed. Also it modifies and unifies tests for libcxx and libstdcpp list data formatter.

Reviewed By: wallace

Differential Revision: https://reviews.llvm.org/D114433
This commit is contained in:
Danil Stefaniuc 2021-11-23 14:11:42 -08:00 committed by Walter Erquinigo
parent 4961fcfbcf
commit 9a9d9a9b00
17 changed files with 123 additions and 323 deletions

View File

@ -412,6 +412,9 @@ public:
uint32_t
GetCodeByteSize ();
uint32_t
GetMaximumNumberOfChildrenToDisplay() const;
lldb::SBError
SetSectionLoadAddress (lldb::SBSection section,
lldb::addr_t section_base_addr);

View File

@ -6,6 +6,14 @@ import lldb.formatters.Logger
# implementation for your platform before relying on these formatters to do the right
# thing for your setup
def ForwardListSummaryProvider(valobj, dict):
list_capping_size = valobj.GetTarget().GetMaximumNumberOfChildrenToDisplay()
text = "size=" + str(valobj.GetNumChildren())
if valobj.GetNumChildren() > list_capping_size:
return "(capped) " + text
else:
return text
def StdOptionalSummaryProvider(valobj, dict):
has_value = valobj.GetNumChildren() > 0
# We add wrapping spaces for consistency with the libcxx formatter
@ -132,6 +140,7 @@ class AbstractListSynthProvider:
self.valobj = valobj
self.count = None
self.has_prev = has_prev
self.list_capping_size = self.valobj.GetTarget().GetMaximumNumberOfChildrenToDisplay()
logger >> "Providing synthetic children for a list named " + \
str(valobj.GetName())
@ -213,6 +222,8 @@ class AbstractListSynthProvider:
if not current.IsValid():
break
size = size + 1
if size >= self.list_capping_size:
break
return size
except:

View File

@ -336,6 +336,11 @@ public:
/// unit from the Architecture's code bus
uint32_t GetCodeByteSize();
/// Gets the target.max-children-count value
/// It should be used to limit the number of
/// children of large data structures to be displayed.
uint32_t GetMaximumNumberOfChildrenToDisplay() const;
/// Set the base load address for a module section.
///
/// \param[in] section

View File

@ -1745,6 +1745,16 @@ uint32_t SBTarget::GetCodeByteSize() {
return 0;
}
uint32_t SBTarget::GetMaximumNumberOfChildrenToDisplay() const {
LLDB_RECORD_METHOD_CONST_NO_ARGS(uint32_t, SBTarget, GetMaximumNumberOfChildrenToDisplay);
TargetSP target_sp(GetSP());
if(target_sp){
return target_sp->GetMaximumNumberOfChildrenToDisplay();
}
return 0;
}
uint32_t SBTarget::GetAddressByteSize() {
LLDB_RECORD_METHOD_NO_ARGS(uint32_t, SBTarget, GetAddressByteSize);
@ -2679,6 +2689,7 @@ void RegisterMethods<SBTarget>(Registry &R) {
LLDB_REGISTER_METHOD(const char *, SBTarget, GetTriple, ());
LLDB_REGISTER_METHOD(uint32_t, SBTarget, GetDataByteSize, ());
LLDB_REGISTER_METHOD(uint32_t, SBTarget, GetCodeByteSize, ());
LLDB_REGISTER_METHOD_CONST(uint32_t, SBTarget, GetMaximumNumberOfChildrenToDisplay,());
LLDB_REGISTER_METHOD(uint32_t, SBTarget, GetAddressByteSize, ());
LLDB_REGISTER_METHOD(lldb::SBModule, SBTarget, GetModuleAtIndex,
(uint32_t));

View File

@ -981,7 +981,7 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
RegularExpression("^std::(__cxx11::)?forward_list<.+>(( )?&)?$"),
TypeSummaryImplSP(
new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
new ScriptSummaryFormat(stl_summary_flags, "lldb.formatters.cpp.gnu_libstdcpp.ForwardListSummaryProvider")));
AddCXXSynthetic(
cpp_category_sp,

View File

@ -52,6 +52,38 @@ class TestDataFormatterGenericForwardList(TestBase):
'[4] = 55555',
'}'])
self.expect("settings show target.max-children-count", matching=True,
substrs=['target.max-children-count (int) = 256'])
self.expect("frame variable thousand_elts",matching=False,
substrs=[
'[256]',
'[333]',
'[444]',
'[555]',
'[666]',
'...'
])
self.runCmd(
"settings set target.max-children-count 3",
check=False)
self.expect("frame variable thousand_elts",matching=False,
substrs=[
'[3]',
'[4]',
'[5]',
])
self.expect("frame variable thousand_elts",matching=True,
substrs=[
'size=256',
'[0]',
'[1]',
'[2]',
'...'
])
@add_test_categories(["libstdcxx"])
def test_libstdcpp(self):
self.do_test(USE_LIBSTDCPP)

View File

@ -2,6 +2,9 @@
int main() {
std::forward_list<int> empty{}, one_elt{47},
five_elts{1, 22, 333, 4444, 55555};
five_elts{1, 22, 333, 4444, 55555}, thousand_elts{};
for(int i = 0; i<1000;i++){
thousand_elts.push_front(i);
}
return 0; // break here
}

View File

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

View File

@ -9,8 +9,10 @@ from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
USE_LIBSTDCPP = "USE_LIBSTDCPP"
USE_LIBCPP = "USE_LIBCPP"
class StdListDataFormatterTestCase(TestBase):
class GenericListDataFormatterTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@ -24,11 +26,9 @@ class StdListDataFormatterTestCase(TestBase):
self.final_line = line_number(
'main.cpp', '// Set final break point at this line.')
@add_test_categories(["libstdcxx"])
@expectedFailureAll(bugnumber="llvm.org/pr50861", compiler="gcc")
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)
lldbutil.run_break_set_by_file_and_line(
@ -205,3 +205,11 @@ class StdListDataFormatterTestCase(TestBase):
self.assertTrue(
self.frame().FindVariable("text_list").MightHaveChildren(),
"text_list.MightHaveChildren() says False for non empty!")
@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)

View File

@ -10,16 +10,16 @@ from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
USE_LIBSTDCPP = "USE_LIBSTDCPP"
USE_LIBCPP = "USE_LIBCPP"
class LibcxxListDataFormatterTestCase(TestBase):
class GenericListDataFormatterTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
NO_DEBUG_INFO_TESTCASE = True
@add_test_categories(["libc++"])
@expectedFailureAndroid(bugnumber="llvm.org/pr32592")
def test_with_run_command(self):
self.build()
def do_test_with_run_command(self, stdlib_type):
self.build(dictionary={stdlib_type: "1"})
exe = self.getBuildArtifact("a.out")
target = self.dbg.CreateTarget(exe)
self.assertTrue(target and target.IsValid(), "Target is valid")
@ -41,7 +41,7 @@ class LibcxxListDataFormatterTestCase(TestBase):
# verify our list is displayed correctly
self.expect(
"frame variable *numbers_list",
"frame variable numbers_list",
substrs=[
'[0] = 1',
'[1] = 2',
@ -58,7 +58,7 @@ class LibcxxListDataFormatterTestCase(TestBase):
# The list is now inconsistent. However, we should be able to get the first three
# elements at least (and most importantly, not crash).
self.expect(
"frame variable *numbers_list",
"frame variable numbers_list",
substrs=[
'[0] = 1',
'[1] = 2',
@ -67,3 +67,11 @@ class LibcxxListDataFormatterTestCase(TestBase):
# Run to completion.
process.Continue()
self.assertEqual(process.GetState(), lldb.eStateExited, PROCESS_EXITED)
@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)

View File

@ -0,0 +1,28 @@
// Evil hack: To simulate memory corruption, we want to fiddle with some internals of std::list.
// Make those accessible to us.
#define private public
#define protected public
#include <list>
#include <stdio.h>
#include <assert.h>
int main()
{
std::list<int> numbers_list{1,2,3,4,5,6,7,8,9,10};
printf("// Set break point at this line.");
std::list<int>::iterator it1=numbers_list.begin();
while (it1 != numbers_list.end()){
*it1++;
}
*it1++;
*it1++;
*it1++;
assert(*it1 == 3);
*it1++;
*it1++;
assert(*it1 == 5);
// Any attempt to free the list will probably crash the program. Let's just leak it.
return 0; // Set second break point at this line.
}

View File

@ -1,218 +0,0 @@
"""
Test lldb data formatter subsystem.
"""
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class LibcxxListDataFormatterTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line number to break at.
self.line = line_number('main.cpp', '// Set break point at this line.')
self.line2 = line_number('main.cpp',
'// Set second break point at this line.')
self.line3 = line_number('main.cpp',
'// Set third break point at this line.')
self.line4 = line_number('main.cpp',
'// Set fourth break point at this line.')
@add_test_categories(["libc++"])
def test_with_run_command(self):
"""Test that that file and class static variables display correctly."""
self.build()
self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
lldbutil.run_break_set_by_file_and_line(
self, "main.cpp", self.line, num_expected_locations=-1)
lldbutil.run_break_set_by_file_and_line(
self, "main.cpp", self.line2, num_expected_locations=-1)
lldbutil.run_break_set_by_file_and_line(
self, "main.cpp", self.line3, num_expected_locations=-1)
lldbutil.run_break_set_by_file_and_line(
self, "main.cpp", self.line4, num_expected_locations=-1)
self.runCmd("run", RUN_SUCCEEDED)
lldbutil.skip_if_library_missing(
self, self.target(), lldbutil.PrintableRegex("libc\+\+"))
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs=['stopped',
'stop reason = breakpoint'])
# This is the function to remove the custom formats in order to have a
# clean slate for the next test case.
def cleanup():
self.runCmd('type format clear', check=False)
self.runCmd('type summary clear', check=False)
self.runCmd('type filter clear', check=False)
self.runCmd('type synth clear', check=False)
self.runCmd(
"settings set target.max-children-count 256",
check=False)
# Execute the cleanup function during test case tear down.
self.addTearDownHook(cleanup)
self.runCmd("frame variable numbers_list --show-types")
self.runCmd(
"type summary add std::int_list std::string_list int_list string_list --summary-string \"list has ${svar%#} items\" -e")
self.runCmd("type format add -f hex int")
self.expect("frame variable numbers_list --raw", matching=False,
substrs=['list has 0 items',
'{}'])
self.expect("frame variable numbers_list",
substrs=['list has 0 items',
'{}'])
self.expect("p numbers_list",
substrs=['list has 0 items',
'{}'])
self.runCmd("n") # This gets up past the printf
self.runCmd("n") # Now advance over the first push_back.
self.expect("frame variable numbers_list",
substrs=['list has 1 items',
'[0] = ',
'0x12345678'])
self.runCmd("n")
self.runCmd("n")
self.runCmd("n")
self.expect("frame variable numbers_list",
substrs=['list has 4 items',
'[0] = ',
'0x12345678',
'[1] =',
'0x11223344',
'[2] =',
'0xbeeffeed',
'[3] =',
'0x00abba00'])
self.runCmd("n")
self.runCmd("n")
self.expect("frame variable numbers_list",
substrs=['list has 6 items',
'[0] = ',
'0x12345678',
'0x11223344',
'0xbeeffeed',
'0x00abba00',
'[4] =',
'0x0abcdef0',
'[5] =',
'0x0cab0cab'])
self.expect("p numbers_list",
substrs=['list has 6 items',
'[0] = ',
'0x12345678',
'0x11223344',
'0xbeeffeed',
'0x00abba00',
'[4] =',
'0x0abcdef0',
'[5] =',
'0x0cab0cab'])
# check access-by-index
self.expect("frame variable numbers_list[0]",
substrs=['0x12345678'])
self.expect("frame variable numbers_list[1]",
substrs=['0x11223344'])
self.runCmd("n")
self.expect("frame variable numbers_list",
substrs=['list has 0 items',
'{}'])
self.runCmd("n")
self.runCmd("n")
self.runCmd("n")
self.runCmd("n")
self.expect("frame variable numbers_list",
substrs=['list has 4 items',
'[0] = ', '1',
'[1] = ', '2',
'[2] = ', '3',
'[3] = ', '4'])
ListPtr = self.frame().FindVariable("list_ptr")
self.assertTrue(ListPtr.GetChildAtIndex(
0).GetValueAsUnsigned(0) == 1, "[0] = 1")
# check that MightHaveChildren() gets it right
self.assertTrue(
self.frame().FindVariable("numbers_list").MightHaveChildren(),
"numbers_list.MightHaveChildren() says False for non empty!")
self.runCmd("type format delete int")
self.runCmd("c")
self.expect("frame variable text_list",
substrs=['list has 3 items',
'[0]', 'goofy',
'[1]', 'is',
'[2]', 'smart'])
# check that MightHaveChildren() gets it right
self.assertTrue(
self.frame().FindVariable("text_list").MightHaveChildren(),
"text_list.MightHaveChildren() says False for non empty!")
self.expect("p text_list",
substrs=['list has 3 items',
'\"goofy\"',
'\"is\"',
'\"smart\"'])
self.runCmd("n") # This gets us past the printf
self.runCmd("n")
self.runCmd("n")
# check access-by-index
self.expect("frame variable text_list[0]",
substrs=['goofy'])
self.expect("frame variable text_list[3]",
substrs=['!!!'])
self.runCmd("continue")
# check that the list provider correctly updates if elements move
countingList = self.frame().FindVariable("countingList")
countingList.SetPreferDynamicValue(True)
countingList.SetPreferSyntheticValue(True)
self.assertTrue(countingList.GetChildAtIndex(
0).GetValueAsUnsigned(0) == 3141, "list[0] == 3141")
self.assertTrue(countingList.GetChildAtIndex(
1).GetValueAsUnsigned(0) == 3141, "list[1] == 3141")
self.runCmd("continue")
self.assertEqual(
countingList.GetChildAtIndex(0).GetValueAsUnsigned(0), 3141,
"uniqued list[0] == 3141")
self.assertEqual(
countingList.GetChildAtIndex(1).GetValueAsUnsigned(0), 3142,
"uniqued list[1] == 3142")

View File

@ -1,35 +0,0 @@
// Evil hack: To simulate memory corruption, we want to fiddle with some internals of std::list.
// Make those accessible to us.
#define private public
#define protected public
#include <list>
#include <stdio.h>
#include <assert.h>
typedef std::list<int> int_list;
int main()
{
#ifdef LLDB_USING_LIBCPP
int_list *numbers_list = new int_list{1,2,3,4,5,6,7,8,9,10};
printf("// Set break point at this line.");
#if _LIBCPP_VERSION >= 3800
auto *third_elem = numbers_list->__end_.__next_->__next_->__next_;
assert(third_elem->__as_node()->__value_ == 3);
auto *fifth_elem = third_elem->__next_->__next_;
assert(fifth_elem->__as_node()->__value_ == 5);
#else
auto *third_elem = numbers_list->__end_.__next_->__next_->__next_;
assert(third_elem->__value_ == 3);
auto *fifth_elem = third_elem->__next_->__next_;
assert(fifth_elem->__value_ == 5);
#endif
fifth_elem->__next_ = third_elem;
#endif
// Any attempt to free the list will probably crash the program. Let's just leak it.
return 0; // Set second break point at this line.
}

View File

@ -1,44 +0,0 @@
#include <string>
#include <list>
#include <stdio.h>
typedef std::list<int> int_list;
typedef std::list<std::string> string_list;
int main()
{
int_list numbers_list;
std::list<int>* list_ptr = &numbers_list;
printf("// Set break point at this line.");
(numbers_list.push_back(0x12345678));
(numbers_list.push_back(0x11223344));
(numbers_list.push_back(0xBEEFFEED));
(numbers_list.push_back(0x00ABBA00));
(numbers_list.push_back(0x0ABCDEF0));
(numbers_list.push_back(0x0CAB0CAB));
numbers_list.clear();
(numbers_list.push_back(1));
(numbers_list.push_back(2));
(numbers_list.push_back(3));
(numbers_list.push_back(4));
string_list text_list;
(text_list.push_back(std::string("goofy")));
(text_list.push_back(std::string("is")));
(text_list.push_back(std::string("smart")));
printf("// Set second break point at this line.");
(text_list.push_back(std::string("!!!")));
std::list<int> countingList = {3141, 3142, 3142,3142,3142, 3142, 3142, 3141};
countingList.sort();
printf("// Set third break point at this line.");
countingList.unique();
printf("// Set fourth break point at this line.");
countingList.size();
return 0;
}

View File

@ -1,6 +0,0 @@
CXX_SOURCES := main.cpp
CFLAGS_EXTRAS := -O0
USE_LIBSTDCPP := 1
include Makefile.rules