1d0f5039eSDavid Spickett""" 2d0f5039eSDavid SpickettTest the use of the global module cache in lldb 3d0f5039eSDavid Spickett""" 4d0f5039eSDavid Spickett 5d0f5039eSDavid Spickettimport lldb 6d0f5039eSDavid Spickett 7d0f5039eSDavid Spickettfrom lldbsuite.test.decorators import * 8d0f5039eSDavid Spickettfrom lldbsuite.test.lldbtest import * 9d0f5039eSDavid Spickettfrom lldbsuite.test import lldbutil 10d0f5039eSDavid Spickettimport os 11d0f5039eSDavid Spickettimport shutil 12d0f5039eSDavid Spickettfrom pathlib import Path 13d0f5039eSDavid Spickettimport time 14d0f5039eSDavid Spickett 15096c530aSJonas Devlieghere 16d0f5039eSDavid Spickettclass GlobalModuleCacheTestCase(TestBase): 17d0f5039eSDavid Spickett # NO_DEBUG_INFO_TESTCASE = True 18d0f5039eSDavid Spickett 19d0f5039eSDavid Spickett def check_counter_var(self, thread, value): 20d0f5039eSDavid Spickett frame = thread.frames[0] 21d0f5039eSDavid Spickett var = frame.FindVariable("counter") 22d0f5039eSDavid Spickett self.assertTrue(var.GetError().Success(), "Got counter variable") 23d0f5039eSDavid Spickett self.assertEqual(var.GetValueAsUnsigned(), value, "This was one-print") 24d0f5039eSDavid Spickett 25d0f5039eSDavid Spickett def copy_to_main(self, src, dst): 26d0f5039eSDavid Spickett # We are relying on the source file being newer than the .o file from 27d0f5039eSDavid Spickett # a previous build, so sleep a bit here to ensure that the touch is later. 28d0f5039eSDavid Spickett time.sleep(2) 29d0f5039eSDavid Spickett try: 301830fadbScmtice # Make sure dst is writeable before trying to write to it. 311830fadbScmtice subprocess.run( 321830fadbScmtice ["chmod", "777", dst], 331830fadbScmtice stdin=None, 341830fadbScmtice capture_output=False, 351830fadbScmtice encoding="utf-8", 361830fadbScmtice ) 37d0f5039eSDavid Spickett shutil.copy(src, dst) 38d0f5039eSDavid Spickett except: 39d0f5039eSDavid Spickett self.fail(f"Could not copy {src} to {dst}") 40d0f5039eSDavid Spickett Path(dst).touch() 41d0f5039eSDavid Spickett 42d0f5039eSDavid Spickett # The rerun tests indicate rerunning on Windows doesn't really work, so 43d0f5039eSDavid Spickett # this one won't either. 44d0f5039eSDavid Spickett @skipIfWindows 45a8af51dfSDavid Spickett # On Arm and AArch64 Linux, this test attempts to pop a thread plan when 46a8af51dfSDavid Spickett # we only have the base plan remaining. Skip it until we can figure out 47a8af51dfSDavid Spickett # the bug this is exposing (https://github.com/llvm/llvm-project/issues/76057). 48a8af51dfSDavid Spickett @skipIf(oslist=["linux"], archs=["arm", "aarch64"]) 49d0f5039eSDavid Spickett def test_OneTargetOneDebugger(self): 50d0f5039eSDavid Spickett self.do_test(True, True) 51d0f5039eSDavid Spickett 52d0f5039eSDavid Spickett # This behaves as implemented but that behavior is not desirable. 53d0f5039eSDavid Spickett # This test tests for the desired behavior as an expected fail. 54d0f5039eSDavid Spickett @skipIfWindows 55d0f5039eSDavid Spickett @expectedFailureAll 5643a5c4a1SDavid Spickett @skipIf(oslist=["linux"], archs=["arm", "aarch64"]) 57d0f5039eSDavid Spickett def test_TwoTargetsOneDebugger(self): 58d0f5039eSDavid Spickett self.do_test(False, True) 59d0f5039eSDavid Spickett 60d0f5039eSDavid Spickett @skipIfWindows 61d0f5039eSDavid Spickett @expectedFailureAll 6243a5c4a1SDavid Spickett @skipIf(oslist=["linux"], archs=["arm", "aarch64"]) 63d0f5039eSDavid Spickett def test_OneTargetTwoDebuggers(self): 64d0f5039eSDavid Spickett self.do_test(True, False) 65d0f5039eSDavid Spickett 66d0f5039eSDavid Spickett def do_test(self, one_target, one_debugger): 67d0f5039eSDavid Spickett # Make sure that if we have one target, and we run, then 68d0f5039eSDavid Spickett # change the binary and rerun, the binary (and any .o files 69d0f5039eSDavid Spickett # if using dwarf in .o file debugging) get removed from the 70d0f5039eSDavid Spickett # shared module cache. They are no longer reachable. 71d0f5039eSDavid Spickett debug_style = self.getDebugInfo() 72d0f5039eSDavid Spickett 73d0f5039eSDavid Spickett # Before we do anything, clear the global module cache so we don't 74d0f5039eSDavid Spickett # see objects from other runs: 75d0f5039eSDavid Spickett lldb.SBDebugger.MemoryPressureDetected() 76d0f5039eSDavid Spickett 77d0f5039eSDavid Spickett # Set up the paths for our two versions of main.c: 78d0f5039eSDavid Spickett main_c_path = os.path.join(self.getBuildDir(), "main.c") 79d0f5039eSDavid Spickett one_print_path = os.path.join(self.getSourceDir(), "one-print.c") 80d0f5039eSDavid Spickett two_print_path = os.path.join(self.getSourceDir(), "two-print.c") 81d0f5039eSDavid Spickett main_filespec = lldb.SBFileSpec(main_c_path) 82d0f5039eSDavid Spickett 83d0f5039eSDavid Spickett # First copy the one-print.c to main.c in the build folder and 84d0f5039eSDavid Spickett # build our a.out from there: 85d0f5039eSDavid Spickett self.copy_to_main(one_print_path, main_c_path) 86d0f5039eSDavid Spickett self.build(dictionary={"C_SOURCES": main_c_path, "EXE": "a.out"}) 87d0f5039eSDavid Spickett 88d0f5039eSDavid Spickett (target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint( 89d0f5039eSDavid Spickett self, "return counter;", main_filespec 90d0f5039eSDavid Spickett ) 91d0f5039eSDavid Spickett 92d0f5039eSDavid Spickett # Make sure we ran the version we intended here: 93d0f5039eSDavid Spickett self.check_counter_var(thread, 1) 94d0f5039eSDavid Spickett process.Kill() 95d0f5039eSDavid Spickett 96d0f5039eSDavid Spickett # Now copy two-print.c over main.c, rebuild, and rerun: 97d0f5039eSDavid Spickett # os.unlink(target.GetExecutable().fullpath) 98d0f5039eSDavid Spickett self.copy_to_main(two_print_path, main_c_path) 99d0f5039eSDavid Spickett 100d0f5039eSDavid Spickett self.build(dictionary={"C_SOURCES": main_c_path, "EXE": "a.out"}) 101d0f5039eSDavid Spickett error = lldb.SBError() 102d0f5039eSDavid Spickett if one_debugger: 103d0f5039eSDavid Spickett if one_target: 104d0f5039eSDavid Spickett (_, process, thread, _) = lldbutil.run_to_breakpoint_do_run( 105d0f5039eSDavid Spickett self, target, bkpt 106d0f5039eSDavid Spickett ) 107d0f5039eSDavid Spickett else: 108d0f5039eSDavid Spickett (target2, process2, thread, bkpt) = lldbutil.run_to_source_breakpoint( 109d0f5039eSDavid Spickett self, "return counter;", main_filespec 110d0f5039eSDavid Spickett ) 111d0f5039eSDavid Spickett else: 112d0f5039eSDavid Spickett if one_target: 113d0f5039eSDavid Spickett new_debugger = lldb.SBDebugger().Create() 114*68a5f5dbSIgor Kudrin new_debugger.SetSelectedPlatform(lldb.selected_platform) 11558e750bfSPavel Labath new_debugger.SetAsync(False) 116d0f5039eSDavid Spickett self.old_debugger = self.dbg 117d0f5039eSDavid Spickett self.dbg = new_debugger 118096c530aSJonas Devlieghere 119d0f5039eSDavid Spickett def cleanupDebugger(self): 120d0f5039eSDavid Spickett lldb.SBDebugger.Destroy(self.dbg) 121d0f5039eSDavid Spickett self.dbg = self.old_debugger 122d0f5039eSDavid Spickett self.old_debugger = None 123d0f5039eSDavid Spickett 124d0f5039eSDavid Spickett self.addTearDownHook(cleanupDebugger) 125d0f5039eSDavid Spickett (target2, process2, thread, bkpt) = lldbutil.run_to_source_breakpoint( 126d0f5039eSDavid Spickett self, "return counter;", main_filespec 127d0f5039eSDavid Spickett ) 128d0f5039eSDavid Spickett 129d0f5039eSDavid Spickett # In two-print.c counter will be 2: 130d0f5039eSDavid Spickett self.check_counter_var(thread, 2) 131d0f5039eSDavid Spickett 132d0f5039eSDavid Spickett # If we made two targets, destroy the first one, that should free up the 133d0f5039eSDavid Spickett # unreachable Modules: 134d0f5039eSDavid Spickett if not one_target: 135d0f5039eSDavid Spickett target.Clear() 136d0f5039eSDavid Spickett 137d0f5039eSDavid Spickett num_a_dot_out_entries = 1 138d0f5039eSDavid Spickett # For dSYM's there will be two lines of output, one for the a.out and one 139d0f5039eSDavid Spickett # for the dSYM. 140d0f5039eSDavid Spickett if debug_style == "dsym": 141d0f5039eSDavid Spickett num_a_dot_out_entries += 1 142d0f5039eSDavid Spickett 143d0f5039eSDavid Spickett error = self.check_image_list_result(num_a_dot_out_entries, 1) 144d0f5039eSDavid Spickett # Even if this fails, MemoryPressureDetected should fix this. 145d0f5039eSDavid Spickett lldb.SBDebugger.MemoryPressureDetected() 146d0f5039eSDavid Spickett error_after_mpd = self.check_image_list_result(num_a_dot_out_entries, 1) 147d0f5039eSDavid Spickett fail_msg = "" 148d0f5039eSDavid Spickett if error != "": 149d0f5039eSDavid Spickett fail_msg = "Error before MPD: " + error 150d0f5039eSDavid Spickett 151d0f5039eSDavid Spickett if error_after_mpd != "": 152d0f5039eSDavid Spickett fail_msg = fail_msg + "\nError after MPD: " + error_after_mpd 153d0f5039eSDavid Spickett if fail_msg != "": 154d0f5039eSDavid Spickett self.fail(fail_msg) 155d0f5039eSDavid Spickett 156d0f5039eSDavid Spickett def check_image_list_result(self, num_a_dot_out, num_main_dot_o): 157d0f5039eSDavid Spickett # Check the global module list, there should only be one a.out, and if we are 158d0f5039eSDavid Spickett # doing dwarf in .o file, there should only be one .o file. This returns 159d0f5039eSDavid Spickett # an error string on error - rather than asserting, so you can stage this 160d0f5039eSDavid Spickett # failing. 161d0f5039eSDavid Spickett image_cmd_result = lldb.SBCommandReturnObject() 162d0f5039eSDavid Spickett interp = self.dbg.GetCommandInterpreter() 163d0f5039eSDavid Spickett interp.HandleCommand("image list -g", image_cmd_result) 164d0f5039eSDavid Spickett if self.TraceOn(): 165d0f5039eSDavid Spickett print(f"Expected: a.out: {num_a_dot_out} main.o: {num_main_dot_o}") 166d0f5039eSDavid Spickett print(image_cmd_result) 167d0f5039eSDavid Spickett 168d0f5039eSDavid Spickett image_list_str = image_cmd_result.GetOutput() 169d0f5039eSDavid Spickett image_list = image_list_str.splitlines() 170d0f5039eSDavid Spickett found_a_dot_out = 0 171d0f5039eSDavid Spickett found_main_dot_o = 0 172d0f5039eSDavid Spickett 173d0f5039eSDavid Spickett for line in image_list: 174d0f5039eSDavid Spickett # FIXME: force this to be at the end of the string: 175d0f5039eSDavid Spickett if "a.out" in line: 176d0f5039eSDavid Spickett found_a_dot_out += 1 177d0f5039eSDavid Spickett if "main.o" in line: 178d0f5039eSDavid Spickett found_main_dot_o += 1 179d0f5039eSDavid Spickett 180d0f5039eSDavid Spickett if num_a_dot_out != found_a_dot_out: 181d0f5039eSDavid Spickett return f"Got {found_a_dot_out} number of a.out's, expected {num_a_dot_out}" 182d0f5039eSDavid Spickett 183d0f5039eSDavid Spickett if found_main_dot_o > 0 and num_main_dot_o != found_main_dot_o: 184096c530aSJonas Devlieghere return ( 185096c530aSJonas Devlieghere f"Got {found_main_dot_o} number of main.o's, expected {num_main_dot_o}" 186096c530aSJonas Devlieghere ) 187d0f5039eSDavid Spickett 188d0f5039eSDavid Spickett return "" 189