[Lldb-commits] [lldb] d0f5039 - Reland "Add a test for evicting unreachable modules from the global module cache (#74894)"

David Spickett via lldb-commits lldb-commits at lists.llvm.org
Thu Dec 14 02:54:16 PST 2023


Author: David Spickett
Date: 2023-12-14T10:54:03Z
New Revision: d0f5039e5db5c2b9222f78e7242401061ab259dc

URL: https://github.com/llvm/llvm-project/commit/d0f5039e5db5c2b9222f78e7242401061ab259dc
DIFF: https://github.com/llvm/llvm-project/commit/d0f5039e5db5c2b9222f78e7242401061ab259dc.diff

LOG: Reland "Add a test for evicting unreachable modules from the global module cache (#74894)"

This reverts commit 35dacf2f51af251a74ac98ed29e7c454a619fcf1.

And relands the original change with two additions so I can debug the failure on Arm/AArch64:
* Enable lldb step logging in the tests.
* Assert that the current plan is not the base plan at the spot I believe is calling PopPlan.

These will be removed and replaced with a proper fix once I see some failures on the bots,
I couldn't reproduce it locally.

(also, no sign of it on the x86_64 bot)

Added: 
    lldb/test/API/python_api/global_module_cache/Makefile
    lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
    lldb/test/API/python_api/global_module_cache/one-print.c
    lldb/test/API/python_api/global_module_cache/two-print.c

Modified: 
    lldb/source/Target/Thread.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index 865cee97e6d878..6d634f25edf8a4 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -831,6 +831,7 @@ bool Thread::ShouldStop(Event *event_ptr) {
             do {
               if (should_stop)
                 current_plan->WillStop();
+              assert(!current_plan->IsBasePlan() && "Cannot pop base plan!");
               PopPlan();
             } while ((current_plan = GetCurrentPlan()) != prev_plan_ptr);
             // Now, if the responsible plan was not "Okay to discard" then

diff  --git a/lldb/test/API/python_api/global_module_cache/Makefile b/lldb/test/API/python_api/global_module_cache/Makefile
new file mode 100644
index 00000000000000..22f1051530f871
--- /dev/null
+++ b/lldb/test/API/python_api/global_module_cache/Makefile
@@ -0,0 +1 @@
+include Makefile.rules

diff  --git a/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py b/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
new file mode 100644
index 00000000000000..b8675532e6394f
--- /dev/null
+++ b/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
@@ -0,0 +1,174 @@
+"""
+Test the use of the global module cache in lldb
+"""
+
+import lldb
+
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+import os
+import shutil
+from pathlib import Path
+import time
+
+class GlobalModuleCacheTestCase(TestBase):
+    # NO_DEBUG_INFO_TESTCASE = True
+
+    def check_counter_var(self, thread, value):
+        frame = thread.frames[0]
+        var = frame.FindVariable("counter")
+        self.assertTrue(var.GetError().Success(), "Got counter variable")
+        self.assertEqual(var.GetValueAsUnsigned(), value, "This was one-print")
+
+    def copy_to_main(self, src, dst):
+        # We are relying on the source file being newer than the .o file from
+        # a previous build, so sleep a bit here to ensure that the touch is later.
+        time.sleep(2)
+        try:
+            shutil.copy(src, dst)
+        except:
+            self.fail(f"Could not copy {src} to {dst}")
+        Path(dst).touch()
+
+    # The rerun tests indicate rerunning on Windows doesn't really work, so
+    # this one won't either.
+    @skipIfWindows
+    def test_OneTargetOneDebugger(self):
+        self.do_test(True, True)
+
+    # This behaves as implemented but that behavior is not desirable.
+    # This test tests for the desired behavior as an expected fail.
+    @skipIfWindows
+    @expectedFailureAll
+    def test_TwoTargetsOneDebugger(self):
+        self.do_test(False, True)
+
+    @skipIfWindows
+    @expectedFailureAll
+    def test_OneTargetTwoDebuggers(self):
+        self.do_test(True, False)
+
+    def do_test(self, one_target, one_debugger):
+        # Here to debug flakiness on Arm, remove later!
+        log_cmd_result = lldb.SBCommandReturnObject()
+        interp = self.dbg.GetCommandInterpreter()
+        interp.HandleCommand("log enable lldb step", log_cmd_result)
+
+        # Make sure that if we have one target, and we run, then
+        # change the binary and rerun, the binary (and any .o files
+        # if using dwarf in .o file debugging) get removed from the
+        # shared module cache.  They are no longer reachable.
+        debug_style = self.getDebugInfo()
+
+        # Before we do anything, clear the global module cache so we don't
+        # see objects from other runs:
+        lldb.SBDebugger.MemoryPressureDetected()
+
+        # Set up the paths for our two versions of main.c:
+        main_c_path = os.path.join(self.getBuildDir(), "main.c")
+        one_print_path = os.path.join(self.getSourceDir(), "one-print.c")
+        two_print_path = os.path.join(self.getSourceDir(), "two-print.c")
+        main_filespec = lldb.SBFileSpec(main_c_path)
+
+        # First copy the one-print.c to main.c in the build folder and
+        # build our a.out from there:
+        self.copy_to_main(one_print_path, main_c_path)
+        self.build(dictionary={"C_SOURCES": main_c_path, "EXE": "a.out"})
+
+        (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+            self, "return counter;", main_filespec
+        )
+
+        # Make sure we ran the version we intended here:
+        self.check_counter_var(thread, 1)
+        process.Kill()
+
+        # Now copy two-print.c over main.c, rebuild, and rerun:
+        # os.unlink(target.GetExecutable().fullpath)
+        self.copy_to_main(two_print_path, main_c_path)
+
+        self.build(dictionary={"C_SOURCES": main_c_path, "EXE": "a.out"})
+        error = lldb.SBError()
+        if one_debugger:
+            if one_target:
+                (_, process, thread, _) = lldbutil.run_to_breakpoint_do_run(
+                    self, target, bkpt
+                )
+            else:
+                (target2, process2, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+                    self, "return counter;", main_filespec
+                )
+        else:
+            if one_target:
+                new_debugger = lldb.SBDebugger().Create()
+                self.old_debugger = self.dbg
+                self.dbg = new_debugger
+                def cleanupDebugger(self):
+                    lldb.SBDebugger.Destroy(self.dbg)
+                    self.dbg = self.old_debugger
+                    self.old_debugger = None
+
+                self.addTearDownHook(cleanupDebugger)
+                (target2, process2, thread, bkpt) = lldbutil.run_to_source_breakpoint(
+                    self, "return counter;", main_filespec
+                )
+
+        # In two-print.c counter will be 2:
+        self.check_counter_var(thread, 2)
+
+        # If we made two targets, destroy the first one, that should free up the
+        # unreachable Modules:
+        if not one_target:
+            target.Clear()
+
+        num_a_dot_out_entries = 1
+        # For dSYM's there will be two lines of output, one for the a.out and one
+        # for the dSYM.
+        if debug_style == "dsym":
+            num_a_dot_out_entries += 1
+
+        error = self.check_image_list_result(num_a_dot_out_entries, 1)
+        # Even if this fails, MemoryPressureDetected should fix this.
+        lldb.SBDebugger.MemoryPressureDetected()
+        error_after_mpd = self.check_image_list_result(num_a_dot_out_entries, 1)
+        fail_msg = ""
+        if error != "":
+            fail_msg = "Error before MPD: " + error
+            
+        if error_after_mpd != "":
+            fail_msg = fail_msg + "\nError after MPD: " + error_after_mpd
+        if fail_msg != "":
+            self.fail(fail_msg)
+
+    def check_image_list_result(self, num_a_dot_out, num_main_dot_o):
+        # Check the global module list, there should only be one a.out, and if we are
+        # doing dwarf in .o file, there should only be one .o file.  This returns
+        # an error string on error - rather than asserting, so you can stage this
+        # failing.
+        image_cmd_result = lldb.SBCommandReturnObject()
+        interp = self.dbg.GetCommandInterpreter()
+        interp.HandleCommand("image list -g", image_cmd_result)
+        if self.TraceOn():
+            print(f"Expected: a.out: {num_a_dot_out} main.o: {num_main_dot_o}")
+            print(image_cmd_result)
+
+        image_list_str = image_cmd_result.GetOutput()
+        image_list = image_list_str.splitlines()
+        found_a_dot_out = 0
+        found_main_dot_o = 0
+
+        for line in image_list:
+            # FIXME: force this to be at the end of the string:
+            if "a.out" in line:
+                found_a_dot_out += 1
+            if "main.o" in line:
+                found_main_dot_o += 1
+        
+        if num_a_dot_out != found_a_dot_out:
+            return f"Got {found_a_dot_out} number of a.out's, expected {num_a_dot_out}"
+            
+        if found_main_dot_o > 0 and num_main_dot_o != found_main_dot_o:
+            return f"Got {found_main_dot_o} number of main.o's, expected {num_main_dot_o}"
+
+        return ""

diff  --git a/lldb/test/API/python_api/global_module_cache/one-print.c b/lldb/test/API/python_api/global_module_cache/one-print.c
new file mode 100644
index 00000000000000..f008f36c2554e4
--- /dev/null
+++ b/lldb/test/API/python_api/global_module_cache/one-print.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main() {
+  int counter = 0;
+  printf("I only print one time: %d.\n", counter++);
+  return counter;
+}

diff  --git a/lldb/test/API/python_api/global_module_cache/two-print.c b/lldb/test/API/python_api/global_module_cache/two-print.c
new file mode 100644
index 00000000000000..96f68cbed83c60
--- /dev/null
+++ b/lldb/test/API/python_api/global_module_cache/two-print.c
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int main() {
+  int counter = 0;
+  printf("I print one time: %d.\n", counter++);
+  printf("I print two times: %d.\n", counter++);
+  return counter;
+}


        


More information about the lldb-commits mailing list