Replace the singleton "ShadowListener" with a primary and N secondary Listeners
Before the addition of the process "Shadow Listener" you could only have one Listener observing the Process Broadcaster. That was necessary because fetching the Process event is what switches the public process state, and for the execution control logic to be manageable you needed to keep other listeners from causing this to happen before the main process control engine was ready. Ismail added the notion of a "ShadowListener" - which allowed you ONE extra process listener. This patch inverts that setup by designating the first listener as primary - and giving it priority in fetching events. Differential Revision: https://reviews.llvm.org/D157556
This commit is contained in:
@@ -313,3 +313,121 @@ class EventAPITestCase(TestBase):
|
||||
self.assertEqual(
|
||||
self.state, "stopped", "Both expected state changed events received"
|
||||
)
|
||||
|
||||
def wait_for_next_event(self, expected_state, test_shadow = False):
|
||||
"""Wait for an event from self.primary & self.shadow listener.
|
||||
If test_shadow is true, we also check that the shadow listener only
|
||||
receives events AFTER the primary listener does."""
|
||||
# Waiting on the shadow listener shouldn't have events yet because
|
||||
# we haven't fetched them for the primary listener yet:
|
||||
event = lldb.SBEvent()
|
||||
|
||||
if test_shadow:
|
||||
success = self.shadow_listener.WaitForEvent(1, event)
|
||||
self.assertFalse(success, "Shadow listener doesn't pull events")
|
||||
|
||||
# But there should be an event for the primary listener:
|
||||
success = self.primary_listener.WaitForEvent(5, event)
|
||||
self.assertTrue(success, "Primary listener got the event")
|
||||
|
||||
state = lldb.SBProcess.GetStateFromEvent(event)
|
||||
restart = False
|
||||
if state == lldb.eStateStopped:
|
||||
restart = lldb.SBProcess.GetRestartedFromEvent(event)
|
||||
|
||||
if expected_state != None:
|
||||
self.assertEqual(state, expected_state, "Primary thread got the correct event")
|
||||
|
||||
# And after pulling that one there should be an equivalent event for the shadow
|
||||
# listener:
|
||||
success = self.shadow_listener.WaitForEvent(5, event)
|
||||
self.assertTrue(success, "Shadow listener got event too")
|
||||
self.assertEqual(state, lldb.SBProcess.GetStateFromEvent(event), "It was the same event")
|
||||
self.assertEqual(restart, lldb.SBProcess.GetRestartedFromEvent(event), "It was the same restarted")
|
||||
|
||||
return state, restart
|
||||
|
||||
def test_shadow_listener(self):
|
||||
self.build()
|
||||
exe = self.getBuildArtifact("a.out")
|
||||
|
||||
# Create a target by the debugger.
|
||||
target = self.dbg.CreateTarget(exe)
|
||||
self.assertTrue(target, VALID_TARGET)
|
||||
|
||||
# Now create a breakpoint on main.c by name 'c'.
|
||||
bkpt1 = target.BreakpointCreateByName("c", "a.out")
|
||||
self.trace("breakpoint:", bkpt1)
|
||||
self.assertTrue(bkpt1.GetNumLocations() == 1, VALID_BREAKPOINT)
|
||||
|
||||
self.primary_listener = lldb.SBListener("my listener")
|
||||
self.shadow_listener = lldb.SBListener("shadow listener")
|
||||
|
||||
self.cur_thread = None
|
||||
|
||||
error = lldb.SBError()
|
||||
launch_info = target.GetLaunchInfo()
|
||||
launch_info.SetListener(self.primary_listener)
|
||||
launch_info.SetShadowListener(self.shadow_listener)
|
||||
|
||||
self.runCmd("settings set target.process.extra-startup-command QSetLogging:bitmask=LOG_PROCESS|LOG_EXCEPTIONS|LOG_RNB_PACKETS|LOG_STEP;")
|
||||
self.dbg.SetAsync(True)
|
||||
|
||||
self.process = target.Launch(launch_info, error)
|
||||
self.assertSuccess(error, "Process launched successfully")
|
||||
|
||||
# Keep fetching events from the primary to trigger the do on removal and
|
||||
# then from the shadow listener, and make sure they match:
|
||||
|
||||
# Events in the launch sequence might be platform dependent, so don't
|
||||
# expect any particular event till we get the stopped:
|
||||
state = lldb.eStateInvalid
|
||||
while state != lldb.eStateStopped:
|
||||
state, restart = self.wait_for_next_event(None, False)
|
||||
|
||||
# Okay, we're now at a good stop, so try a next:
|
||||
self.cur_thread = self.process.threads[0]
|
||||
|
||||
# Make sure we're at our expected breakpoint:
|
||||
self.assertTrue(self.cur_thread.IsValid(), "Got a zeroth thread")
|
||||
self.assertEqual(self.cur_thread.stop_reason, lldb.eStopReasonBreakpoint)
|
||||
self.assertEqual(self.cur_thread.GetStopReasonDataCount(), 2, "Only one breakpoint/loc here")
|
||||
self.assertEqual(bkpt1.GetID(), self.cur_thread.GetStopReasonDataAtIndex(0), "Hit the right breakpoint")
|
||||
# Disable the first breakpoint so it doesn't get in the way...
|
||||
bkpt1.SetEnabled(False)
|
||||
|
||||
self.cur_thread.StepOver()
|
||||
# We'll run the test for "shadow listener blocked by primary listener
|
||||
# for the first couple rounds, then we'll skip the 1 second pause...
|
||||
self.wait_for_next_event(lldb.eStateRunning, True)
|
||||
self.wait_for_next_event(lldb.eStateStopped, True)
|
||||
|
||||
# Next try an auto-continue breakpoint and make sure the shadow listener got
|
||||
# the resumed info as well. Note that I'm not explicitly counting
|
||||
# running events here. At the point when I wrote this lldb sometimes
|
||||
# emits two running events in a row. Apparently the code to coalesce running
|
||||
# events isn't working. But that's not what this test is testing, we're really
|
||||
# testing that the primary & shadow listeners hear the same thing and in the
|
||||
# right order.
|
||||
|
||||
main_spec = lldb.SBFileSpec("main.c")
|
||||
bkpt2 = target.BreakpointCreateBySourceRegex("b.2. returns %d", main_spec)
|
||||
self.assertTrue(bkpt2.GetNumLocations() > 0, "BP2 worked")
|
||||
bkpt2.SetAutoContinue(True)
|
||||
|
||||
bkpt3 = target.BreakpointCreateBySourceRegex("a.3. returns %d", main_spec)
|
||||
self.assertTrue(bkpt3.GetNumLocations() > 0, "BP3 worked")
|
||||
|
||||
state = lldb.eStateStopped
|
||||
restarted = False
|
||||
|
||||
# Put in a counter to make sure we don't spin forever if there is some
|
||||
# error in the logic.
|
||||
counter = 0
|
||||
while state != lldb.eStateExited:
|
||||
counter += 1
|
||||
self.assertLess(counter, 50, "Took more than 50 events to hit two breakpoints.")
|
||||
if state == lldb.eStateStopped and not restarted:
|
||||
self.process.Continue()
|
||||
state, restarted = self.wait_for_next_event(None, False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user