[Lldb-commits] [lldb] Add a test for evicting unreachable modules from the global module cache (PR #74894)
via lldb-commits
lldb-commits at lists.llvm.org
Tue Dec 12 11:26:42 PST 2023
https://github.com/jimingham updated https://github.com/llvm/llvm-project/pull/74894
>From 438d35a7a7fca454718062583f91776ca018b2b1 Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Fri, 8 Dec 2023 14:43:14 -0800
Subject: [PATCH 1/4] Add a test for evicting unreachable modules from the
global module cache.
---
.../python_api/global_module_cache/Makefile | 1 +
.../TestGlobalModuleCache.py | 110 ++++++++++++++++++
.../global_module_cache/one-print.c | 8 ++
.../global_module_cache/two-print.c | 9 ++
4 files changed, 128 insertions(+)
create mode 100644 lldb/test/API/python_api/global_module_cache/Makefile
create mode 100644 lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
create mode 100644 lldb/test/API/python_api/global_module_cache/one-print.c
create mode 100644 lldb/test/API/python_api/global_module_cache/two-print.c
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..ff74d09a128183
--- /dev/null
+++ b/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
@@ -0,0 +1,110 @@
+"""
+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):
+ # 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()
+ (_, process, thread, _) = lldbutil.run_to_breakpoint_do_run(self, target, bkpt)
+ # In two-print.c counter will be 2:
+ self.check_counter_var(thread, 2)
+
+ 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
+
+ self.check_image_list_result(num_a_dot_out_entries, 1)
+
+ def check_image_list_result(self, num_a_dot_out, num_main_dot_o):
+ # Now look at 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:
+ image_cmd_result = lldb.SBCommandReturnObject()
+ interp = self.dbg.GetCommandInterpreter()
+ interp.HandleCommand("image list -g", 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
+
+ self.assertEqual(
+ num_a_dot_out, found_a_dot_out, "Got the right number of a.out's"
+ )
+ if found_main_dot_o > 0:
+ self.assertEqual(
+ num_main_dot_o, found_main_dot_o, "Got the right number of main.o's"
+ )
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..5a572ca7c65fec
--- /dev/null
+++ b/lldb/test/API/python_api/global_module_cache/one-print.c
@@ -0,0 +1,8 @@
+#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..ce6cb84c5c46eb
--- /dev/null
+++ b/lldb/test/API/python_api/global_module_cache/two-print.c
@@ -0,0 +1,9 @@
+#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;
+}
>From 684cee4a363c87c6b2cfa0776adb7c5923a0f6b6 Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Mon, 11 Dec 2023 15:54:38 -0800
Subject: [PATCH 2/4] Add a test for one target with the old file and one with
the new.
---
.../TestGlobalModuleCache.py | 22 +++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py b/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
index ff74d09a128183..db84c5a2d24d3d 100644
--- a/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
+++ b/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
@@ -36,6 +36,13 @@ def copy_to_main(self, src, dst):
# this one won't either.
@skipIfWindows
def test_OneTargetOneDebugger(self):
+ self.do_test_one_debugger(True)
+
+ @expectedFailureAll
+ def test_TwoTargetsOneDebugger(self):
+ self.do_test_one_debugger(False)
+
+ def do_test_one_debugger(self, one_target):
# 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
@@ -71,10 +78,21 @@ def test_OneTargetOneDebugger(self):
self.build(dictionary={"C_SOURCES": main_c_path, "EXE": "a.out"})
error = lldb.SBError()
- (_, process, thread, _) = lldbutil.run_to_breakpoint_do_run(self, target, bkpt)
+ 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
+ )
+
# 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.
@@ -82,7 +100,7 @@ def test_OneTargetOneDebugger(self):
num_a_dot_out_entries += 1
self.check_image_list_result(num_a_dot_out_entries, 1)
-
+
def check_image_list_result(self, num_a_dot_out, num_main_dot_o):
# Now look at 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:
>From 09269171e2ab3b1502ff8c25ba67dfcf72b9e509 Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Mon, 11 Dec 2023 16:50:33 -0800
Subject: [PATCH 3/4] Added a test for two debuggers, one target each. It also
fails.
---
.../TestGlobalModuleCache.py | 82 ++++++++++++++-----
1 file changed, 63 insertions(+), 19 deletions(-)
diff --git a/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py b/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
index db84c5a2d24d3d..d2c388b534cd6f 100644
--- a/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
+++ b/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
@@ -36,13 +36,22 @@ def copy_to_main(self, src, dst):
# this one won't either.
@skipIfWindows
def test_OneTargetOneDebugger(self):
- self.do_test_one_debugger(True)
+ 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_one_debugger(False)
-
- def do_test_one_debugger(self, one_target):
+ self.do_test(False, True)
+
+ @skipIfWindows
+ @expectedFailureAll
+ def test_OneTargetTwoDebuggers(self):
+ self.do_test(True, False)
+
+
+ def do_test(self, one_target, one_debugger):
# 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
@@ -78,12 +87,29 @@ def do_test_one_debugger(self, one_target):
self.build(dictionary={"C_SOURCES": main_c_path, "EXE": "a.out"})
error = lldb.SBError()
- if one_target:
- (_, process, thread, _) = lldbutil.run_to_breakpoint_do_run(self, target, bkpt)
+ 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:
- (target2, process2, thread, bkpt) = lldbutil.run_to_source_breakpoint(
- self, "return counter;", main_filespec
- )
+ 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)
@@ -99,14 +125,31 @@ def do_test_one_debugger(self, one_target):
if debug_style == "dsym":
num_a_dot_out_entries += 1
- self.check_image_list_result(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):
- # Now look at 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:
+ # 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
@@ -119,10 +162,11 @@ def check_image_list_result(self, num_a_dot_out, num_main_dot_o):
if "main.o" in line:
found_main_dot_o += 1
- self.assertEqual(
- num_a_dot_out, found_a_dot_out, "Got the right number of a.out's"
- )
- if found_main_dot_o > 0:
- self.assertEqual(
- num_main_dot_o, found_main_dot_o, "Got the right number of main.o's"
- )
+
+ 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 ""
>From 90119f1d3955029931939d529a9f4b3c113b8284 Mon Sep 17 00:00:00 2001
From: Jim Ingham <jingham at apple.com>
Date: Tue, 12 Dec 2023 11:26:09 -0800
Subject: [PATCH 4/4] busywork
---
.../global_module_cache/TestGlobalModuleCache.py | 7 ++-----
lldb/test/API/python_api/global_module_cache/one-print.c | 3 +--
lldb/test/API/python_api/global_module_cache/two-print.c | 3 +--
3 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py b/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
index d2c388b534cd6f..98ebdec0404eb4 100644
--- a/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
+++ b/lldb/test/API/python_api/global_module_cache/TestGlobalModuleCache.py
@@ -12,7 +12,6 @@
from pathlib import Path
import time
-
class GlobalModuleCacheTestCase(TestBase):
# NO_DEBUG_INFO_TESTCASE = True
@@ -50,7 +49,6 @@ def test_TwoTargetsOneDebugger(self):
def test_OneTargetTwoDebuggers(self):
self.do_test(True, False)
-
def do_test(self, one_target, one_debugger):
# Make sure that if we have one target, and we run, then
# change the binary and rerun, the binary (and any .o files
@@ -137,7 +135,7 @@ def cleanupDebugger(self):
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
@@ -161,12 +159,11 @@ def check_image_list_result(self, num_a_dot_out, num_main_dot_o):
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
index 5a572ca7c65fec..f008f36c2554e4 100644
--- a/lldb/test/API/python_api/global_module_cache/one-print.c
+++ b/lldb/test/API/python_api/global_module_cache/one-print.c
@@ -1,7 +1,6 @@
#include <stdio.h>
-int
-main() {
+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
index ce6cb84c5c46eb..96f68cbed83c60 100644
--- a/lldb/test/API/python_api/global_module_cache/two-print.c
+++ b/lldb/test/API/python_api/global_module_cache/two-print.c
@@ -1,7 +1,6 @@
#include <stdio.h>
-int
-main() {
+int main() {
int counter = 0;
printf("I print one time: %d.\n", counter++);
printf("I print two times: %d.\n", counter++);
More information about the lldb-commits
mailing list