16cf66801SMed Ismail Bennani""" 26cf66801SMed Ismail BennaniTest the functionality of interactive scripted processes 36cf66801SMed Ismail Bennani""" 46cf66801SMed Ismail Bennani 56cf66801SMed Ismail Bennaniimport lldb 66cf66801SMed Ismail Bennaniimport lldbsuite.test.lldbutil as lldbutil 7f8d6542eSMed Ismail Bennanifrom lldbsuite.test.decorators import * 86cf66801SMed Ismail Bennanifrom lldbsuite.test.lldbtest import * 96cf66801SMed Ismail Bennaniimport json, os 106cf66801SMed Ismail Bennani 116cf66801SMed Ismail Bennani 126cf66801SMed Ismail Bennaniclass TestInteractiveScriptedProcess(TestBase): 136cf66801SMed Ismail Bennani NO_DEBUG_INFO_TESTCASE = True 146cf66801SMed Ismail Bennani 15d3a6b931SMed Ismail Bennani def setUp(self): 16d3a6b931SMed Ismail Bennani # Call super's setUp(). 17d3a6b931SMed Ismail Bennani TestBase.setUp(self) 18d3a6b931SMed Ismail Bennani # Build and load test program 196cf66801SMed Ismail Bennani self.build() 206cf66801SMed Ismail Bennani self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) 216cf66801SMed Ismail Bennani self.main_source_file = lldb.SBFileSpec("main.cpp") 226cf66801SMed Ismail Bennani self.script_module = "interactive_scripted_process" 236cf66801SMed Ismail Bennani self.script_file = self.script_module + ".py" 24d3a6b931SMed Ismail Bennani 25*2e7aa2eeSJim Ingham # These tests are flakey and sometimes timeout. They work most of the time 26*2e7aa2eeSJim Ingham # so the basic event flow is right, but somehow the handling is off. 27d0d902dfSMed Ismail Bennani @skipUnlessDarwin 28c0d1128fSJonas Devlieghere @skipIfDarwin 29d3a6b931SMed Ismail Bennani def test_passthrough_launch(self): 30d3a6b931SMed Ismail Bennani """Test a simple pass-through process launch""" 316cf66801SMed Ismail Bennani self.passthrough_launch() 326cf66801SMed Ismail Bennani 33d3a6b931SMed Ismail Bennani lldbutil.run_break_set_by_source_regexp(self, "also break here") 34d3a6b931SMed Ismail Bennani self.assertEqual(self.mux_target.GetNumBreakpoints(), 2) 35d3a6b931SMed Ismail Bennani error = self.mux_process.Continue() 36d3a6b931SMed Ismail Bennani self.assertSuccess(error, "Resuming multiplexer scripted process") 37d3a6b931SMed Ismail Bennani self.assertTrue(self.mux_process.IsValid(), "Got a valid process") 38d3a6b931SMed Ismail Bennani 39d3a6b931SMed Ismail Bennani event = lldbutil.fetch_next_event( 40*2e7aa2eeSJim Ingham self, self.dbg.GetListener(), self.mux_process.GetBroadcaster(), timeout=20 41*2e7aa2eeSJim Ingham ) 42*2e7aa2eeSJim Ingham self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning) 43*2e7aa2eeSJim Ingham event = lldbutil.fetch_next_event( 44*2e7aa2eeSJim Ingham self, self.dbg.GetListener(), self.mux_process.GetBroadcaster() 45*2e7aa2eeSJim Ingham ) 46*2e7aa2eeSJim Ingham self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateStopped) 47*2e7aa2eeSJim Ingham 48*2e7aa2eeSJim Ingham event = lldbutil.fetch_next_event( 49d3a6b931SMed Ismail Bennani self, self.mux_process_listener, self.mux_process.GetBroadcaster() 50d3a6b931SMed Ismail Bennani ) 51d3a6b931SMed Ismail Bennani self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning) 52d3a6b931SMed Ismail Bennani event = lldbutil.fetch_next_event( 53d3a6b931SMed Ismail Bennani self, self.mux_process_listener, self.mux_process.GetBroadcaster() 54d3a6b931SMed Ismail Bennani ) 55d3a6b931SMed Ismail Bennani self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateStopped) 56d3a6b931SMed Ismail Bennani 57d0d902dfSMed Ismail Bennani @skipUnlessDarwin 58c0d1128fSJonas Devlieghere @skipIfDarwin 59d3a6b931SMed Ismail Bennani def test_multiplexed_launch(self): 60d3a6b931SMed Ismail Bennani """Test a multiple interactive scripted process debugging""" 61d3a6b931SMed Ismail Bennani self.passthrough_launch() 62d3a6b931SMed Ismail Bennani self.assertEqual(self.dbg.GetNumTargets(), 2) 63d3a6b931SMed Ismail Bennani 64d3a6b931SMed Ismail Bennani driving_target = self.mux_process.GetScriptedImplementation().driving_target 65d3a6b931SMed Ismail Bennani self.assertTrue(driving_target.IsValid(), "Driving target is invalid") 66d3a6b931SMed Ismail Bennani 67d3a6b931SMed Ismail Bennani # Create a target for the multiplexed even scripted process 68d3a6b931SMed Ismail Bennani even_target = self.duplicate_target(driving_target) 69d3a6b931SMed Ismail Bennani self.assertTrue( 70d3a6b931SMed Ismail Bennani even_target.IsValid(), 71d3a6b931SMed Ismail Bennani "Couldn't duplicate driving target to launch multiplexed even scripted process", 72d3a6b931SMed Ismail Bennani ) 73d3a6b931SMed Ismail Bennani 74d3a6b931SMed Ismail Bennani class_name = f"{self.script_module}.MultiplexedScriptedProcess" 75d3a6b931SMed Ismail Bennani dictionary = {"driving_target_idx": self.dbg.GetIndexOfTarget(self.mux_target)} 76d3a6b931SMed Ismail Bennani 77d3a6b931SMed Ismail Bennani dictionary["parity"] = 0 78d3a6b931SMed Ismail Bennani muxed_launch_info = self.get_launch_info(class_name, dictionary) 79d3a6b931SMed Ismail Bennani 80d3a6b931SMed Ismail Bennani # Launch Even Child Scripted Process 81d3a6b931SMed Ismail Bennani error = lldb.SBError() 82d3a6b931SMed Ismail Bennani even_process = even_target.Launch(muxed_launch_info, error) 83d3a6b931SMed Ismail Bennani self.assertTrue( 84d3a6b931SMed Ismail Bennani even_process, "Couldn't launch multiplexed even scripted process" 85d3a6b931SMed Ismail Bennani ) 86d3a6b931SMed Ismail Bennani self.multiplex(even_process) 87d3a6b931SMed Ismail Bennani 88d3a6b931SMed Ismail Bennani # Check that the even process started running 89d3a6b931SMed Ismail Bennani event = lldbutil.fetch_next_event( 90d3a6b931SMed Ismail Bennani self, self.dbg.GetListener(), even_process.GetBroadcaster() 91d3a6b931SMed Ismail Bennani ) 92d3a6b931SMed Ismail Bennani self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning) 93d3a6b931SMed Ismail Bennani # Check that the even process stopped 94d3a6b931SMed Ismail Bennani event = lldbutil.fetch_next_event( 95d3a6b931SMed Ismail Bennani self, self.dbg.GetListener(), even_process.GetBroadcaster() 96d3a6b931SMed Ismail Bennani ) 97d3a6b931SMed Ismail Bennani self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateStopped) 98d3a6b931SMed Ismail Bennani 99d3a6b931SMed Ismail Bennani self.assertTrue(even_process.IsValid(), "Got a valid process") 100d3a6b931SMed Ismail Bennani self.assertState( 101d3a6b931SMed Ismail Bennani even_process.GetState(), lldb.eStateStopped, "Process is stopped" 102d3a6b931SMed Ismail Bennani ) 103d3a6b931SMed Ismail Bennani 104d3a6b931SMed Ismail Bennani # Create a target for the multiplexed odd scripted process 105d3a6b931SMed Ismail Bennani odd_target = self.duplicate_target(driving_target) 106d3a6b931SMed Ismail Bennani self.assertTrue( 107d3a6b931SMed Ismail Bennani odd_target.IsValid(), 108d3a6b931SMed Ismail Bennani "Couldn't duplicate driving target to launch multiplexed odd scripted process", 109d3a6b931SMed Ismail Bennani ) 110d3a6b931SMed Ismail Bennani 111d3a6b931SMed Ismail Bennani dictionary["parity"] = 1 112d3a6b931SMed Ismail Bennani muxed_launch_info = self.get_launch_info(class_name, dictionary) 113d3a6b931SMed Ismail Bennani 114d3a6b931SMed Ismail Bennani # Launch Odd Child Scripted Process 115d3a6b931SMed Ismail Bennani error = lldb.SBError() 116d3a6b931SMed Ismail Bennani odd_process = odd_target.Launch(muxed_launch_info, error) 117d3a6b931SMed Ismail Bennani self.assertTrue(odd_process, "Couldn't launch multiplexed odd scripted process") 118d3a6b931SMed Ismail Bennani self.multiplex(odd_process) 119d3a6b931SMed Ismail Bennani 120d3a6b931SMed Ismail Bennani # Check that the odd process started running 121d3a6b931SMed Ismail Bennani event = lldbutil.fetch_next_event( 122d3a6b931SMed Ismail Bennani self, self.dbg.GetListener(), odd_process.GetBroadcaster() 123d3a6b931SMed Ismail Bennani ) 124d3a6b931SMed Ismail Bennani self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning) 125d3a6b931SMed Ismail Bennani # Check that the odd process stopped 126d3a6b931SMed Ismail Bennani event = lldbutil.fetch_next_event( 127d3a6b931SMed Ismail Bennani self, self.dbg.GetListener(), odd_process.GetBroadcaster() 128d3a6b931SMed Ismail Bennani ) 129d3a6b931SMed Ismail Bennani self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateStopped) 130d3a6b931SMed Ismail Bennani 131d3a6b931SMed Ismail Bennani self.assertTrue(odd_process.IsValid(), "Got a valid process") 132d3a6b931SMed Ismail Bennani self.assertState( 133d3a6b931SMed Ismail Bennani odd_process.GetState(), lldb.eStateStopped, "Process is stopped" 134d3a6b931SMed Ismail Bennani ) 135d3a6b931SMed Ismail Bennani 136d3a6b931SMed Ismail Bennani # Set a breakpoint on the odd child process 137d3a6b931SMed Ismail Bennani bkpt = odd_target.BreakpointCreateBySourceRegex( 138d3a6b931SMed Ismail Bennani "also break here", self.main_source_file 139d3a6b931SMed Ismail Bennani ) 140d3a6b931SMed Ismail Bennani self.assertEqual(odd_target.GetNumBreakpoints(), 1) 141d3a6b931SMed Ismail Bennani self.assertTrue(bkpt, "Second breakpoint set on child scripted process") 142d3a6b931SMed Ismail Bennani self.assertEqual(bkpt.GetNumLocations(), 1, "Second breakpoint has 1 location") 143d3a6b931SMed Ismail Bennani 144d3a6b931SMed Ismail Bennani # Verify that the breakpoint was also set on the multiplexer & real target 145d3a6b931SMed Ismail Bennani self.assertEqual(self.mux_target.GetNumBreakpoints(), 2) 146d3a6b931SMed Ismail Bennani bkpt = self.mux_target.GetBreakpointAtIndex(1) 147d3a6b931SMed Ismail Bennani self.assertEqual( 148d3a6b931SMed Ismail Bennani bkpt.GetNumLocations(), 1, "Second breakpoint set on mux scripted process" 149d3a6b931SMed Ismail Bennani ) 150d3a6b931SMed Ismail Bennani self.assertTrue(bkpt.MatchesName("multiplexed_scripted_process_421")) 151d3a6b931SMed Ismail Bennani 152d3a6b931SMed Ismail Bennani self.assertGreater(driving_target.GetNumBreakpoints(), 1) 153d3a6b931SMed Ismail Bennani 154d3a6b931SMed Ismail Bennani # Resume execution on child process 155d3a6b931SMed Ismail Bennani error = odd_process.Continue() 156d3a6b931SMed Ismail Bennani self.assertSuccess(error, "Resuming odd child scripted process") 157d3a6b931SMed Ismail Bennani self.assertTrue(odd_process.IsValid(), "Got a valid process") 158d3a6b931SMed Ismail Bennani 159d3a6b931SMed Ismail Bennani # Since all the execution is asynchronous, the order in which events 160d3a6b931SMed Ismail Bennani # arrive is non-deterministic, so we need a data structure to make sure 161d3a6b931SMed Ismail Bennani # we received both the running and stopped event for each target. 162d3a6b931SMed Ismail Bennani 163d3a6b931SMed Ismail Bennani # Initialize the execution event "bingo book", that maps a process index 164d3a6b931SMed Ismail Bennani # to a dictionary that contains flags that are not set for the process 165d3a6b931SMed Ismail Bennani # events that we care about (running & stopped) 166d3a6b931SMed Ismail Bennani 167d3a6b931SMed Ismail Bennani execution_events = { 168d3a6b931SMed Ismail Bennani 1: {lldb.eStateRunning: False, lldb.eStateStopped: False}, 169d3a6b931SMed Ismail Bennani 2: {lldb.eStateRunning: False, lldb.eStateStopped: False}, 170d3a6b931SMed Ismail Bennani 3: {lldb.eStateRunning: False, lldb.eStateStopped: False}, 171d3a6b931SMed Ismail Bennani } 172d3a6b931SMed Ismail Bennani 173d3a6b931SMed Ismail Bennani def fetch_process_event(self, execution_events): 174d3a6b931SMed Ismail Bennani event = lldbutil.fetch_next_event( 175d3a6b931SMed Ismail Bennani self, 176d3a6b931SMed Ismail Bennani self.dbg.GetListener(), 177d3a6b931SMed Ismail Bennani lldb.SBProcess.GetBroadcasterClass(), 178d3a6b931SMed Ismail Bennani match_class=True, 179d3a6b931SMed Ismail Bennani ) 180d3a6b931SMed Ismail Bennani state = lldb.SBProcess.GetStateFromEvent(event) 181d3a6b931SMed Ismail Bennani self.assertIn(state, [lldb.eStateRunning, lldb.eStateStopped]) 182d3a6b931SMed Ismail Bennani event_process = lldb.SBProcess.GetProcessFromEvent(event) 183d3a6b931SMed Ismail Bennani self.assertTrue(event_process.IsValid()) 184d3a6b931SMed Ismail Bennani event_target = event_process.GetTarget() 185d3a6b931SMed Ismail Bennani event_target_idx = self.dbg.GetIndexOfTarget(event_target) 186d3a6b931SMed Ismail Bennani self.assertFalse( 187d3a6b931SMed Ismail Bennani execution_events[event_target_idx][state], 188d3a6b931SMed Ismail Bennani "Event already received for this process", 189d3a6b931SMed Ismail Bennani ) 190d3a6b931SMed Ismail Bennani execution_events[event_target_idx][state] = True 191d3a6b931SMed Ismail Bennani 192*2e7aa2eeSJim Ingham for _ in range((self.dbg.GetNumTargets() - 1) * 2): 193*2e7aa2eeSJim Ingham fetch_process_event(self, execution_events) 194*2e7aa2eeSJim Ingham 195*2e7aa2eeSJim Ingham for target_index, event_states in execution_events.items(): 196*2e7aa2eeSJim Ingham for state, is_set in event_states.items(): 197*2e7aa2eeSJim Ingham self.assertTrue(is_set, f"Target {target_index} has state {state} set") 198*2e7aa2eeSJim Ingham 199d3a6b931SMed Ismail Bennani event = lldbutil.fetch_next_event( 200d3a6b931SMed Ismail Bennani self, self.mux_process_listener, self.mux_process.GetBroadcaster() 201d3a6b931SMed Ismail Bennani ) 202d3a6b931SMed Ismail Bennani self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning) 203d3a6b931SMed Ismail Bennani 204d3a6b931SMed Ismail Bennani event = lldbutil.fetch_next_event( 205d3a6b931SMed Ismail Bennani self, self.mux_process_listener, self.mux_process.GetBroadcaster() 206d3a6b931SMed Ismail Bennani ) 207d3a6b931SMed Ismail Bennani self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateStopped) 208d3a6b931SMed Ismail Bennani 2096cf66801SMed Ismail Bennani def duplicate_target(self, driving_target): 2106cf66801SMed Ismail Bennani exe = driving_target.executable.fullpath 2116cf66801SMed Ismail Bennani triple = driving_target.triple 2126cf66801SMed Ismail Bennani return self.dbg.CreateTargetWithFileAndTargetTriple(exe, triple) 2136cf66801SMed Ismail Bennani 2146cf66801SMed Ismail Bennani def get_launch_info(self, class_name, script_dict): 2156cf66801SMed Ismail Bennani structured_data = lldb.SBStructuredData() 2166cf66801SMed Ismail Bennani structured_data.SetFromJSON(json.dumps(script_dict)) 2176cf66801SMed Ismail Bennani 2186cf66801SMed Ismail Bennani launch_info = lldb.SBLaunchInfo(None) 2196cf66801SMed Ismail Bennani launch_info.SetProcessPluginName("ScriptedProcess") 2206cf66801SMed Ismail Bennani launch_info.SetScriptedProcessClassName(class_name) 2216cf66801SMed Ismail Bennani launch_info.SetScriptedProcessDictionary(structured_data) 2226cf66801SMed Ismail Bennani return launch_info 2236cf66801SMed Ismail Bennani 224d3a6b931SMed Ismail Bennani def multiplex(self, muxed_process): 225d3a6b931SMed Ismail Bennani muxed_process.GetScriptedImplementation().multiplexer = ( 226d3a6b931SMed Ismail Bennani self.mux_process.GetScriptedImplementation() 227d3a6b931SMed Ismail Bennani ) 228d3a6b931SMed Ismail Bennani self.mux_process.GetScriptedImplementation().multiplexed_processes[ 229d3a6b931SMed Ismail Bennani muxed_process.GetProcessID() 230d3a6b931SMed Ismail Bennani ] = muxed_process 231d3a6b931SMed Ismail Bennani 2326cf66801SMed Ismail Bennani def passthrough_launch(self): 2336cf66801SMed Ismail Bennani """Test that a simple passthrough wrapper functions correctly""" 2346cf66801SMed Ismail Bennani # First build the real target: 2356cf66801SMed Ismail Bennani self.assertEqual(self.dbg.GetNumTargets(), 1) 2366cf66801SMed Ismail Bennani real_target_id = 0 2376cf66801SMed Ismail Bennani real_target = self.dbg.GetTargetAtIndex(real_target_id) 2386cf66801SMed Ismail Bennani lldbutil.run_break_set_by_source_regexp(self, "Break here") 2396cf66801SMed Ismail Bennani self.assertEqual(real_target.GetNumBreakpoints(), 1) 2406cf66801SMed Ismail Bennani 2416cf66801SMed Ismail Bennani # Now source in the scripted module: 2426cf66801SMed Ismail Bennani script_path = os.path.join(self.getSourceDir(), self.script_file) 2436cf66801SMed Ismail Bennani self.runCmd(f"command script import '{script_path}'") 2446cf66801SMed Ismail Bennani 245d3a6b931SMed Ismail Bennani self.mux_target = self.duplicate_target(real_target) 246d3a6b931SMed Ismail Bennani self.assertTrue(self.mux_target.IsValid(), "duplicate target succeeded") 2476cf66801SMed Ismail Bennani 2486cf66801SMed Ismail Bennani mux_class = f"{self.script_module}.MultiplexerScriptedProcess" 2496cf66801SMed Ismail Bennani script_dict = {"driving_target_idx": real_target_id} 2506cf66801SMed Ismail Bennani mux_launch_info = self.get_launch_info(mux_class, script_dict) 251d3a6b931SMed Ismail Bennani self.mux_process_listener = lldb.SBListener( 2526cf66801SMed Ismail Bennani "lldb.test.interactive-scripted-process.listener" 2536cf66801SMed Ismail Bennani ) 254d3a6b931SMed Ismail Bennani mux_launch_info.SetShadowListener(self.mux_process_listener) 2556cf66801SMed Ismail Bennani 2566cf66801SMed Ismail Bennani self.dbg.SetAsync(True) 2576cf66801SMed Ismail Bennani error = lldb.SBError() 258d3a6b931SMed Ismail Bennani self.mux_process = self.mux_target.Launch(mux_launch_info, error) 2596cf66801SMed Ismail Bennani self.assertSuccess(error, "Launched multiplexer scripted process") 260d3a6b931SMed Ismail Bennani self.assertTrue(self.mux_process.IsValid(), "Got a valid process") 2616cf66801SMed Ismail Bennani 2626cf66801SMed Ismail Bennani # Check that the real process started running 2636cf66801SMed Ismail Bennani event = lldbutil.fetch_next_event( 264d3a6b931SMed Ismail Bennani self, self.dbg.GetListener(), self.mux_process.GetBroadcaster() 2656cf66801SMed Ismail Bennani ) 2666cf66801SMed Ismail Bennani self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning) 267*2e7aa2eeSJim Ingham # Check that the mux process started running 268*2e7aa2eeSJim Ingham event = lldbutil.fetch_next_event( 269*2e7aa2eeSJim Ingham self, self.mux_process_listener, self.mux_process.GetBroadcaster() 270*2e7aa2eeSJim Ingham ) 271*2e7aa2eeSJim Ingham self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning) 2726cf66801SMed Ismail Bennani 2736cf66801SMed Ismail Bennani # Check that the real process stopped 2746cf66801SMed Ismail Bennani event = lldbutil.fetch_next_event( 275d3a6b931SMed Ismail Bennani self, self.dbg.GetListener(), self.mux_process.GetBroadcaster() 2766cf66801SMed Ismail Bennani ) 2776cf66801SMed Ismail Bennani self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateStopped) 2786cf66801SMed Ismail Bennani # Check that the mux process stopped 2796cf66801SMed Ismail Bennani event = lldbutil.fetch_next_event( 280d3a6b931SMed Ismail Bennani self, self.mux_process_listener, self.mux_process.GetBroadcaster() 2816cf66801SMed Ismail Bennani ) 2826cf66801SMed Ismail Bennani self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateStopped) 2836cf66801SMed Ismail Bennani 2846cf66801SMed Ismail Bennani real_process = real_target.GetProcess() 2856cf66801SMed Ismail Bennani self.assertTrue(real_process.IsValid(), "Got a valid process") 2866cf66801SMed Ismail Bennani self.assertState( 2876cf66801SMed Ismail Bennani real_process.GetState(), lldb.eStateStopped, "Process is stopped" 2886cf66801SMed Ismail Bennani ) 2896cf66801SMed Ismail Bennani 2906cf66801SMed Ismail Bennani # This is a passthrough, so the two processes should have the same state: 2916cf66801SMed Ismail Bennani # Check that we got the right threads: 2926cf66801SMed Ismail Bennani self.assertEqual( 2936cf66801SMed Ismail Bennani len(real_process.threads), 294d3a6b931SMed Ismail Bennani len(self.mux_process.threads), 2956cf66801SMed Ismail Bennani "Same number of threads", 2966cf66801SMed Ismail Bennani ) 2976cf66801SMed Ismail Bennani for id in range(len(real_process.threads)): 2986cf66801SMed Ismail Bennani real_pc = real_process.threads[id].frame[0].pc 299d3a6b931SMed Ismail Bennani mux_pc = self.mux_process.threads[id].frame[0].pc 3006cf66801SMed Ismail Bennani self.assertEqual(real_pc, mux_pc, f"PC's equal for {id}") 301