[Lldb-commits] [PATCH] D157556: Replace the singleton "ShadowListener" with a primary and N secondary listeners
This revision was not accepted when it landed; it landed in state "Needs Revision". This revision was automatically updated to reflect the committed changes. Closed by commit rG2e7aa2ee34eb: Replace the singleton "ShadowListener" with a primary and N secondary Listeners (authored by jingham). Changed prior to commit: https://reviews.llvm.org/D157556?vs=550495&id=550806#toc Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D157556/new/ https://reviews.llvm.org/D157556 Files: lldb/include/lldb/Target/Process.h lldb/include/lldb/Utility/Broadcaster.h lldb/include/lldb/Utility/Event.h lldb/source/Target/Process.cpp lldb/source/Utility/Broadcaster.cpp lldb/source/Utility/Event.cpp lldb/source/Utility/Listener.cpp lldb/test/API/api/listeners/TestListener.py lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py lldb/test/API/python_api/event/TestEvents.py Index: lldb/test/API/python_api/event/TestEvents.py === --- lldb/test/API/python_api/event/TestEvents.py +++ lldb/test/API/python_api/event/TestEvents.py @@ -313,3 +313,121 @@ 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(
[Lldb-commits] [PATCH] D157556: Replace the singleton "ShadowListener" with a primary and N secondary listeners
bulbazord requested changes to this revision. bulbazord added a comment. This revision now requires changes to proceed. Thanks for all the work to clean up some of those interfaces! I know that was not straightforward. :) I left a few more comments about minor things I noticed while re-reading this. I'm going to request changes to put this back into your queue. Comment at: lldb/include/lldb/Utility/Event.h:233 +m_pending_listeners.push_back(pending_listener_sp); + }; + nit: trailing semicolon Comment at: lldb/source/Target/Process.cpp:620-621 - if (m_listener_sp->GetEventForBroadcaster(this, event_sp, + if (GetPrimaryListener()->GetEventForBroadcaster(this, event_sp, std::chrono::seconds(0)) && event_sp) Do you need to check for the validity of the primary listener before using it? It can point to nullptr from what I can tell Comment at: lldb/source/Utility/Broadcaster.cpp:59 +max_count++; + listeners.reserve(max_count); I wonder if we should be reserving at all considering we're using an `llvm::SmallVector<$type, 4>`. The point of a SmallVector is that on average it should be small and only sometimes should we be exceeding that hardcoded size. This means that if there are 8 listeners but only 2 are relevant, we're taking a SmallVector of size 4 and allocating space for 8 listeners (always triggering a memory allocation) even if it will only ever have 2 listeners in it. I know that's the existing behavior and you're just preserving it, but something to think about for the future. Comment at: lldb/source/Utility/Broadcaster.cpp:179-181 + // The primary listener listens for all event bits: + if (m_primary_listener_sp) +return true; This check is a bit redundant since `HasListeners` performs the exact same check Comment at: lldb/source/Utility/Broadcaster.cpp:198 + std::lock_guard guard(m_listeners_mutex); + for (auto it = m_listeners.begin(); it != m_listeners.end();) { Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D157556/new/ https://reviews.llvm.org/D157556 ___ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
[Lldb-commits] [PATCH] D157556: Replace the singleton "ShadowListener" with a primary and N secondary listeners
jingham marked 4 inline comments as done. jingham added a comment. I implemented Alex's suggestion for a GetListeners filter, but doing that triggered some bugs due to the fact that we weren't really pruning the m_listeners correctly as listeners come and go. I fixed some bugs there along with implementing this suggestion. Comment at: lldb/source/Utility/Broadcaster.cpp:154-157 + // The primary listener listens for all event bits: + if (m_primary_listener_sp) +return true; + clayborg wrote: > Does the primary listener need to listen to all event bits? I don't see a use case for saying "I want to be the one that gets first crack at this broadcaster" but only for some bits. Plus, if we do it that way, we'd really have to have a map of "primary listener -> event mask" and add a way to make yourself the primary listener for only some bits. We have this ability for HijackListeners, but we never use it, we always just hijack all bits as well... There's no reason you can't do it that way, but until we see a need for multiple primary listeners, I don't think the extra complexity is warranted. Comment at: lldb/source/Utility/Broadcaster.cpp:253 + continue; +if (pair.first != hijacking_listener_sp +&& pair.first != m_primary_listener_sp) bulbazord wrote: > nit: Checking to see if `pair.first` is `hijacking_listener_sp` is redundant. > If you've gotten to this point, `hijacking_listener_sp` must be pointing to > `nullptr`. Interestingly enough, that was effectively a check on empty listeners in m_listeners. There shouldn't have been any, but we weren't being very careful about pruning the list... Comment at: lldb/source/Utility/Broadcaster.cpp:254 +if (pair.first != hijacking_listener_sp +&& pair.first != m_primary_listener_sp) + event_sp->AddPendingListener(pair.first); bulbazord wrote: > Why might `pair.first` be `m_primary_listener_sp`? I would assume that if a > primary listener is set, it wouldn't be among the other listeners in a > broadcaster But I suppose nothing stops you from doing that. That's really an oversight in GetListeners. That correctly doesn't return the Hijack listener, since that is a hidden agent, but the primary listener only differs from the secondary listeners in how dispatch gets done. I fixed that in this patch. Comment at: lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py:38-44 +self, self.dbg.GetListener(), self.mux_process.GetBroadcaster() +) +self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning) +event = lldbutil.fetch_next_event( +self, self.dbg.GetListener(), self.mux_process.GetBroadcaster() +) +self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateStopped) bulbazord wrote: > This is to make sure that the primary listener is getting the run/stop events > before the `mux_process_listener` right? correct. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D157556/new/ https://reviews.llvm.org/D157556 ___ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
[Lldb-commits] [PATCH] D157556: Replace the singleton "ShadowListener" with a primary and N secondary listeners
jingham updated this revision to Diff 550495. jingham added a comment. Respond to review comments. In the course of implementing Alex's suggestion to add a filter to GetListeners, I noticed that there we were being lax in our dealing with removed listeners. RemoveListener didn't actually do that, it just removed the event bits, and the next pass through GetListeners would actually clear out the entry. Fixing that required a few other little fixes to get this working again. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D157556/new/ https://reviews.llvm.org/D157556 Files: lldb/include/lldb/Target/Process.h lldb/include/lldb/Utility/Broadcaster.h lldb/include/lldb/Utility/Event.h lldb/source/Target/Process.cpp lldb/source/Utility/Broadcaster.cpp lldb/source/Utility/Event.cpp lldb/source/Utility/Listener.cpp lldb/test/API/api/listeners/TestListener.py lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py lldb/test/API/python_api/event/TestEvents.py Index: lldb/test/API/python_api/event/TestEvents.py === --- lldb/test/API/python_api/event/TestEvents.py +++ lldb/test/API/python_api/event/TestEvents.py @@ -313,3 +313,121 @@ 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.s
[Lldb-commits] [PATCH] D157556: Replace the singleton "ShadowListener" with a primary and N secondary listeners
clayborg added inline comments. Comment at: lldb/source/Target/Process.cpp:372 +const int Process::g_all_event_bits = eBroadcastBitStateChanged + | eBroadcastBitInterrupt mib wrote: > nit: this could probably be `constexpr` yes, this can be a constexpr and be defined in only in the header file. No need to create storage for it by making it a real static variable Comment at: lldb/source/Utility/Broadcaster.cpp:154-157 + // The primary listener listens for all event bits: + if (m_primary_listener_sp) +return true; + Does the primary listener need to listen to all event bits? Comment at: lldb/source/Utility/Broadcaster.cpp:229-235 + ListenerSP primary_listener_sp = hijacking_listener_sp; + bool is_hijack = false; + + if (primary_listener_sp) +is_hijack = true; + else +primary_listener_sp = m_primary_listener_sp; mib wrote: > nit: We could simplify the logic here a little bit I agree this would clean this up nicely. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D157556/new/ https://reviews.llvm.org/D157556 ___ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
[Lldb-commits] [PATCH] D157556: Replace the singleton "ShadowListener" with a primary and N secondary listeners
JDevlieghere added inline comments. Comment at: lldb/include/lldb/Utility/Event.h:228 void Clear() { m_data_sp.reset(); } + + /// This is used by Broadcasters with Primary Listeners to store the other Nit: trailing whitespace Comment at: lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py:26 @skipUnlessDarwin -@skipIfDarwin +#@skipIfDarwin def test_passthrough_launch(self): bulbazord wrote: > Remove this comment? Remove? Comment at: lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py:56 @skipUnlessDarwin -@skipIfDarwin +#@skipIfDarwin def test_multiplexed_launch(self): bulbazord wrote: > delete? Remove? Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D157556/new/ https://reviews.llvm.org/D157556 ___ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
[Lldb-commits] [PATCH] D157556: Replace the singleton "ShadowListener" with a primary and N secondary listeners
bulbazord added inline comments. Comment at: lldb/include/lldb/Target/Process.h:348-357 eBroadcastBitStateChanged = (1 << 0), eBroadcastBitInterrupt = (1 << 1), eBroadcastBitSTDOUT = (1 << 2), eBroadcastBitSTDERR = (1 << 3), eBroadcastBitProfileData = (1 << 4), eBroadcastBitStructuredData = (1 << 5), }; I wonder if it might be better to add a new element to the enum, something like `eBroadcastBitAll = eBroadcastBitStageChanged | ... | eBroadcastBitStructuredData,`. If we have to add a new element to this enumeration, I'm not sure everyone will realize that `g_all_event_bits` needs to be updated in a separate file (even though it's right below the enum definition). Comment at: lldb/include/lldb/Target/Process.h:616-619 + void SetShadowListener(lldb::ListenerSP shadow_listener_sp) { +if (shadow_listener_sp) + AddListener(shadow_listener_sp, g_all_event_bits); + } Why was this moved down? Comment at: lldb/source/Utility/Broadcaster.cpp:246 +// listeners. +if (!is_hijack) { + for (auto &pair : GetListeners()) { mib wrote: > Then, as a follow-up to my suggestion, you could check `hijacking_listener_sp > ` instead of checking the boolean. Instead of having the bool flag for `is_hijack`, you can compare `primary_listener_sp` against `hijacking_listener_sp`. Then the setup above gets less complex, like so: ``` ListenerSP primary_listener_sp = hijacking_listener_sp; if (!primary_listener_sp) primary_listener_sp = m_primary_listener_sp; ``` Comment at: lldb/source/Utility/Broadcaster.cpp:247 +if (!is_hijack) { + for (auto &pair : GetListeners()) { +if (!(pair.second & event_type)) I wonder if it might be useful to introduce a variant of `GetListeners` which takes an `event_type` and only gives you back the ones you want. That would simplify this loop (and the one in the other branching path). You could also just pass that vector along to the `Event` instead of filtering and adding them one at a time (so you're not paying for potential vector resizing costs). Comment at: lldb/source/Utility/Broadcaster.cpp:253 + continue; +if (pair.first != hijacking_listener_sp +&& pair.first != m_primary_listener_sp) nit: Checking to see if `pair.first` is `hijacking_listener_sp` is redundant. If you've gotten to this point, `hijacking_listener_sp` must be pointing to `nullptr`. Comment at: lldb/source/Utility/Broadcaster.cpp:254 +if (pair.first != hijacking_listener_sp +&& pair.first != m_primary_listener_sp) + event_sp->AddPendingListener(pair.first); Why might `pair.first` be `m_primary_listener_sp`? I would assume that if a primary listener is set, it wouldn't be among the other listeners in a broadcaster But I suppose nothing stops you from doing that. Comment at: lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py:26 @skipUnlessDarwin -@skipIfDarwin +#@skipIfDarwin def test_passthrough_launch(self): Remove this comment? Comment at: lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py:38-44 +self, self.dbg.GetListener(), self.mux_process.GetBroadcaster() +) +self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateRunning) +event = lldbutil.fetch_next_event( +self, self.dbg.GetListener(), self.mux_process.GetBroadcaster() +) +self.assertState(lldb.SBProcess.GetStateFromEvent(event), lldb.eStateStopped) This is to make sure that the primary listener is getting the run/stop events before the `mux_process_listener` right? Comment at: lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py:56 @skipUnlessDarwin -@skipIfDarwin +#@skipIfDarwin def test_multiplexed_launch(self): delete? Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D157556/new/ https://reviews.llvm.org/D157556 ___ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
[Lldb-commits] [PATCH] D157556: Replace the singleton "ShadowListener" with a primary and N secondary listeners
mib accepted this revision. mib added a comment. This revision is now accepted and ready to land. LGTM with some comments :) Comment at: lldb/source/Target/Process.cpp:372 +const int Process::g_all_event_bits = eBroadcastBitStateChanged + | eBroadcastBitInterrupt nit: this could probably be `constexpr` Comment at: lldb/source/Utility/Broadcaster.cpp:229-235 + ListenerSP primary_listener_sp = hijacking_listener_sp; + bool is_hijack = false; + + if (primary_listener_sp) +is_hijack = true; + else +primary_listener_sp = m_primary_listener_sp; nit: We could simplify the logic here a little bit Comment at: lldb/source/Utility/Broadcaster.cpp:246 +// listeners. +if (!is_hijack) { + for (auto &pair : GetListeners()) { Then, as a follow-up to my suggestion, you could check `hijacking_listener_sp ` instead of checking the boolean. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D157556/new/ https://reviews.llvm.org/D157556 ___ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
[Lldb-commits] [PATCH] D157556: Replace the singleton "ShadowListener" with a primary and N secondary listeners
jingham created this revision. jingham added reviewers: mib, bulbazord, clayborg, JDevlieghere. Herald added a project: All. jingham requested review of this revision. Herald added a project: LLDB. Herald added a subscriber: lldb-commits. Allow a Broadcaster to have one "Primary" listener, and potentially many secondary Listeners. The primary listener is guaranteed to run the DoOnRemoval first, and only when that is complete will secondary Listeners receive the event. Prior to Ismail's addition of the notion of a "Secondary" listener, you could only safely observe process events from the Listener you passed in to the Process creation. That was because the act of pulling the process event off the event queue triggered the change of the public state of the process (thus keeping the event stream and the state in sync). Ismail added a privileged "shadow listener" that could also be informed of the event. That wasn't quite the right model, however, because the real singleton is the primary listener. That's what this patch implements. This isn't quite the full solution for the Process Listener. The goal is that the primary Process Listener not only gets to drive the event stream, but that the primary listener gets to react to each event before any other Listener can hear about it. That will allow the process execution logic to run before any other agent gets a chance to change the process state out from under it. For that, I'll need to add a receipt mechanism to the event delivery, and have the forwarding to the pending listeners happen only after the receipt is acknowledged. But that will be a follow on patch. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D157556 Files: lldb/include/lldb/Target/Process.h lldb/include/lldb/Utility/Broadcaster.h lldb/include/lldb/Utility/Event.h lldb/source/Target/Process.cpp lldb/source/Utility/Broadcaster.cpp lldb/source/Utility/Event.cpp lldb/source/Utility/Listener.cpp lldb/test/API/functionalities/interactive_scripted_process/TestInteractiveScriptedProcess.py lldb/test/API/python_api/event/TestEvents.py Index: lldb/test/API/python_api/event/TestEvents.py === --- lldb/test/API/python_api/event/TestEvents.py +++ lldb/test/API/python_api/event/TestEvents.py @@ -313,3 +313,121 @@ 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_