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 <jing...@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 <jing...@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 <jing...@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 <jing...@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++); _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits