danilashtefan updated this revision to Diff 389300.
danilashtefan added a comment.
List tests unification for libcxx and libstdcpp
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D114433/new/
https://reviews.llvm.org/D114433
Files:
lldb/bindings/interface/SBTarget.i
lldb/examples/synthetic/gnu_libstdcpp.py
lldb/include/lldb/API/SBTarget.h
lldb/source/API/SBTarget.cpp
lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/main.cpp
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/Makefile
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/Makefile
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/main.cpp
lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/main.cpp
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/main.cpp
@@ -0,0 +1,34 @@
+#include <list>
+#include <string>
+
+typedef std::list<int> int_list;
+typedef std::list<std::string> string_list;
+
+int main()
+{
+ int_list numbers_list;
+
+ numbers_list.push_back(0x12345678); // Set break point at this line.
+ 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")); // Optional break point at this line.
+ text_list.push_back(std::string("is"));
+ text_list.push_back(std::string("smart"));
+
+ text_list.push_back(std::string("!!!"));
+
+ return 0; // Set final break point at this line.
+}
+
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/main.cpp
@@ -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.
+}
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/TestDataFormatterGenericListLoop.py
@@ -0,0 +1,77 @@
+"""
+Test that the debugger handles loops in std::list (which can appear as a result of e.g. memory
+corruption).
+"""
+
+
+
+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 GenericListDataFormatterTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+ NO_DEBUG_INFO_TESTCASE = True
+
+ 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")
+
+ file_spec = lldb.SBFileSpec("main.cpp", False)
+ breakpoint1 = target.BreakpointCreateBySourceRegex(
+ '// Set break point at this line.', file_spec)
+ self.assertTrue(breakpoint1 and breakpoint1.IsValid())
+ breakpoint2 = target.BreakpointCreateBySourceRegex(
+ '// Set second break point at this line.', file_spec)
+ self.assertTrue(breakpoint2 and breakpoint2.IsValid())
+
+ # Run the program, it should stop at breakpoint 1.
+ process = target.LaunchSimple(
+ None, None, self.get_process_working_directory())
+ self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID)
+ self.assertEqual(
+ len(lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint1)), 1)
+
+ # verify our list is displayed correctly
+ self.expect(
+ "frame variable numbers_list",
+ substrs=[
+ '[0] = 1',
+ '[1] = 2',
+ '[2] = 3',
+ '[3] = 4',
+ '[5] = 6'])
+
+ # Continue to breakpoint 2.
+ process.Continue()
+ self.assertTrue(process and process.IsValid(), PROCESS_IS_VALID)
+ self.assertEqual(
+ len(lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint2)), 1)
+
+ # 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",
+ substrs=[
+ '[0] = 1',
+ '[1] = 2',
+ '[2] = 3'])
+
+ # 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)
\ No newline at end of file
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/loop/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/TestDataFormatterGenericList.py
@@ -0,0 +1,215 @@
+"""
+Test lldb data formatter subsystem.
+"""
+
+
+
+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 GenericListDataFormatterTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def setUp(self):
+ # Call super's setUp().
+ TestBase.setUp(self)
+ # Find the line numbers to break at for the different tests.
+ self.line = line_number('main.cpp', '// Set break point at this line.')
+ self.optional_line = line_number(
+ 'main.cpp', '// Optional break point at this line.')
+ self.final_line = line_number(
+ 'main.cpp', '// Set final break point at this line.')
+
+ def do_test_with_run_command(self, stdlib_type):
+ """Test that that file and class static variables display correctly."""
+ self.build(dictionary={stdlib_type: "1"})
+ 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)
+
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ # 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 format add -f hex int")
+
+ self.expect("frame variable numbers_list --raw", matching=False,
+ substrs=['size=0',
+ '{}'])
+ self.expect(
+ "frame variable &numbers_list._M_impl._M_node --raw",
+ matching=False,
+ substrs=[
+ 'size=0',
+ '{}'])
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=0',
+ '{}'])
+
+ self.expect("p numbers_list",
+ substrs=['size=0',
+ '{}'])
+
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=1',
+ '[0] = ',
+ '0x12345678'])
+
+ self.runCmd("n")
+ self.runCmd("n")
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=4',
+ '[0] = ',
+ '0x12345678',
+ '[1] =',
+ '0x11223344',
+ '[2] =',
+ '0xbeeffeed',
+ '[3] =',
+ '0x00abba00'])
+
+ self.runCmd("n")
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=6',
+ '[0] = ',
+ '0x12345678',
+ '0x11223344',
+ '0xbeeffeed',
+ '0x00abba00',
+ '[4] =',
+ '0x0abcdef0',
+ '[5] =',
+ '0x0cab0cab'])
+
+ self.expect("p numbers_list",
+ substrs=['size=6',
+ '[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'])
+
+ # but check that expression does not rely on us
+ self.expect("expression numbers_list[0]", matching=False, error=True,
+ substrs=['0x12345678'])
+
+ # check that MightHaveChildren() gets it right
+ self.assertTrue(
+ self.frame().FindVariable("numbers_list").MightHaveChildren(),
+ "numbers_list.MightHaveChildren() says False for non empty!")
+
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=0',
+ '{}'])
+
+ self.runCmd("n")
+ self.runCmd("n")
+ self.runCmd("n")
+ self.runCmd("n")
+
+ self.expect("frame variable numbers_list",
+ substrs=['size=4',
+ '[0] = ', '1',
+ '[1] = ', '2',
+ '[2] = ', '3',
+ '[3] = ', '4'])
+
+ self.runCmd("type format delete int")
+
+ self.runCmd("n")
+
+ self.expect("frame variable text_list",
+ substrs=['size=0',
+ '{}'])
+
+ lldbutil.run_break_set_by_file_and_line(
+ self, "main.cpp", self.final_line, num_expected_locations=-1)
+
+ self.runCmd("c", RUN_SUCCEEDED)
+
+ # The stop reason of the thread should be breakpoint.
+ self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
+ substrs=['stopped',
+ 'stop reason = breakpoint'])
+
+ self.expect("frame variable text_list",
+ substrs=['size=4',
+ '[0]', 'goofy',
+ '[1]', 'is',
+ '[2]', 'smart',
+ '[3]', '!!!'])
+
+ self.expect("p text_list",
+ substrs=['size=4',
+ '\"goofy\"',
+ '\"is\"',
+ '\"smart\"',
+ '\"!!!\"'])
+
+ # check access-by-index
+ self.expect("frame variable text_list[0]",
+ substrs=['goofy'])
+ self.expect("frame variable text_list[3]",
+ substrs=['!!!'])
+
+ # but check that expression does not rely on us
+ self.expect("expression text_list[0]", matching=False, error=True,
+ substrs=['goofy'])
+
+ # check that MightHaveChildren() gets it right
+ 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)
\ No newline at end of file
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/list/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/main.cpp
===================================================================
--- lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/main.cpp
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/main.cpp
@@ -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
}
Index: lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py
===================================================================
--- lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py
+++ lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/forward_list/TestDataFormatterGenericForwardList.py
@@ -52,6 +52,38 @@
'[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)
Index: lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
===================================================================
--- lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -961,7 +961,7 @@
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,
Index: lldb/source/API/SBTarget.cpp
===================================================================
--- lldb/source/API/SBTarget.cpp
+++ lldb/source/API/SBTarget.cpp
@@ -1745,6 +1745,16 @@
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 @@
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));
Index: lldb/include/lldb/API/SBTarget.h
===================================================================
--- lldb/include/lldb/API/SBTarget.h
+++ lldb/include/lldb/API/SBTarget.h
@@ -336,6 +336,11 @@
/// 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
Index: lldb/examples/synthetic/gnu_libstdcpp.py
===================================================================
--- lldb/examples/synthetic/gnu_libstdcpp.py
+++ lldb/examples/synthetic/gnu_libstdcpp.py
@@ -9,6 +9,14 @@
# 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
+
class AbstractListSynthProvider:
def __init__(self, valobj, dict, has_prev):
'''
@@ -20,6 +28,7 @@
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())
@@ -99,6 +108,9 @@
'_M_next').GetValueAsUnsigned(0) != self.get_end_of_list_address():
size = size + 1
current = current.GetChildMemberWithName('_M_next')
+ # In case of forward_list capping size is the limit of the children to be displayed
+ if not self.has_prev and size >= self.list_capping_size:
+ break
return size
except:
logger >> "Error determining the size"
Index: lldb/bindings/interface/SBTarget.i
===================================================================
--- lldb/bindings/interface/SBTarget.i
+++ lldb/bindings/interface/SBTarget.i
@@ -412,6 +412,9 @@
uint32_t
GetCodeByteSize ();
+ uint32_t
+ GetMaximumNumberOfChildrenToDisplay() const;
+
lldb::SBError
SetSectionLoadAddress (lldb::SBSection section,
lldb::addr_t section_base_addr);
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits