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
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to