mib created this revision.
mib added reviewers: jingham, JDevlieghere, bulbazord.
mib added a project: LLDB.
Herald added a subscriber: emaste.
Herald added a project: All.
mib requested review of this revision.
Herald added a subscriber: lldb-commits.

This patch augments lldb's event listeners with a new passthrough mode.

As the name suggests, this mode allows events to be passed to an
additional listener to perform event monitoring, without interferring
with the event life cycle.

One of our use case for this, is to be able to listen to public process
events while making sure the events will still be delivered to the
default process listener (the debugger listener in most cases).

Signed-off-by: Med Ismail Bennani <medismail.benn...@gmail.com>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D148397

Files:
  lldb/include/lldb/API/SBAttachInfo.h
  lldb/include/lldb/API/SBLaunchInfo.h
  lldb/include/lldb/Target/Process.h
  lldb/include/lldb/Utility/Broadcaster.h
  lldb/include/lldb/Utility/Listener.h
  lldb/include/lldb/Utility/ProcessInfo.h
  lldb/source/API/SBAttachInfo.cpp
  lldb/source/API/SBLaunchInfo.cpp
  lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
  lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
  lldb/source/Target/Target.cpp
  lldb/source/Utility/Broadcaster.cpp
  lldb/source/Utility/Listener.cpp

Index: lldb/source/Utility/Listener.cpp
===================================================================
--- lldb/source/Utility/Listener.cpp
+++ lldb/source/Utility/Listener.cpp
@@ -35,7 +35,7 @@
 
 Listener::Listener(const char *name)
     : m_name(name), m_broadcasters(), m_broadcasters_mutex(), m_events(),
-      m_events_mutex() {
+      m_events_mutex(), m_is_passthrough() {
   Log *log = GetLog(LLDBLog::Object);
   if (log != nullptr)
     LLDB_LOGF(log, "%p Listener::Listener('%s')", static_cast<void *>(this),
@@ -302,7 +302,8 @@
       // to return it so it should be okay to get the next event off the queue
       // here - and it might be useful to do that in the "DoOnRemoval".
       lock.unlock();
-      event_sp->DoOnRemoval();
+      if (!m_is_passthrough)
+        event_sp->DoOnRemoval();
     }
     return true;
   }
Index: lldb/source/Utility/Broadcaster.cpp
===================================================================
--- lldb/source/Utility/Broadcaster.cpp
+++ lldb/source/Utility/Broadcaster.cpp
@@ -228,6 +228,8 @@
                       &m_broadcaster, event_type))
       return;
     hijacking_listener_sp->AddEvent(event_sp);
+    if (m_passthrough_listener)
+      m_passthrough_listener->AddEvent(event_sp);
   } else {
     for (auto &pair : GetListeners()) {
       if (!(pair.second & event_type))
@@ -237,6 +239,8 @@
         continue;
 
       pair.first->AddEvent(event_sp);
+      if (m_passthrough_listener)
+        m_passthrough_listener->AddEvent(event_sp);
     }
   }
 }
Index: lldb/source/Target/Target.cpp
===================================================================
--- lldb/source/Target/Target.cpp
+++ lldb/source/Target/Target.cpp
@@ -3193,6 +3193,8 @@
     // Since we didn't have a platform launch the process, launch it here.
     if (m_process_sp) {
       m_process_sp->HijackProcessEvents(launch_info.GetHijackListener());
+      m_process_sp->SetPassthroughListener(
+          launch_info.GetPassthroughListener());
       error = m_process_sp->Launch(launch_info);
     }
   }
Index: lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
===================================================================
--- lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
+++ lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
@@ -423,6 +423,8 @@
 
         if (process_sp) {
           process_sp->HijackProcessEvents(launch_info.GetHijackListener());
+          process_sp->SetPassthroughListener(
+              launch_info.GetPassthroughListener());
 
           error = process_sp->ConnectRemote(connect_url.c_str());
           // Retry the connect remote one time...
@@ -515,6 +517,8 @@
               ListenerSP listener_sp = attach_info.GetHijackListener();
               if (listener_sp)
                 process_sp->HijackProcessEvents(listener_sp);
+              process_sp->SetPassthroughListener(
+                  attach_info.GetPassthroughListener());
               error = process_sp->Attach(attach_info);
             }
 
Index: lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
===================================================================
--- lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
+++ lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
@@ -401,6 +401,8 @@
           attach_info.SetHijackListener(listener_sp);
         }
         process_sp->HijackProcessEvents(listener_sp);
+        process_sp->SetPassthroughListener(
+            attach_info.GetPassthroughListener());
         error = process_sp->Attach(attach_info);
       }
     }
@@ -458,6 +460,7 @@
   LLDB_LOG(log, "successfully created process");
 
   process_sp->HijackProcessEvents(launch_info.GetHijackListener());
+  process_sp->SetPassthroughListener(launch_info.GetPassthroughListener());
 
   // Log file actions.
   if (log) {
Index: lldb/source/API/SBLaunchInfo.cpp
===================================================================
--- lldb/source/API/SBLaunchInfo.cpp
+++ lldb/source/API/SBLaunchInfo.cpp
@@ -17,6 +17,7 @@
 #include "lldb/API/SBStructuredData.h"
 #include "lldb/Core/StructuredDataImpl.h"
 #include "lldb/Host/ProcessLaunchInfo.h"
+#include "lldb/Utility/Listener.h"
 #include "lldb/Utility/ScriptedMetadata.h"
 
 using namespace lldb;
@@ -387,3 +388,21 @@
   metadata_sp = std::make_shared<ScriptedMetadata>(class_name, dict_sp);
   m_opaque_sp->SetScriptedMetadata(metadata_sp);
 }
+
+SBListener SBLaunchInfo::GetPassthroughListener() {
+  LLDB_INSTRUMENT_VA(this);
+
+  return SBListener(m_opaque_sp->GetPassthroughListener());
+}
+
+void SBLaunchInfo::SetPassthroughListener(SBListener &listener) {
+  LLDB_INSTRUMENT_VA(this, listener);
+
+  ListenerSP listener_sp = listener.GetSP();
+  if (listener_sp && listener.IsValid())
+    listener_sp->SetPassthrough(true);
+  else
+    listener_sp = nullptr;
+
+  m_opaque_sp->SetPassthroughListener(listener_sp);
+}
Index: lldb/source/API/SBAttachInfo.cpp
===================================================================
--- lldb/source/API/SBAttachInfo.cpp
+++ lldb/source/API/SBAttachInfo.cpp
@@ -254,6 +254,24 @@
   m_opaque_sp->SetListener(listener.GetSP());
 }
 
+SBListener SBAttachInfo::GetPassthroughListener() {
+  LLDB_INSTRUMENT_VA(this);
+
+  return SBListener(m_opaque_sp->GetPassthroughListener());
+}
+
+void SBAttachInfo::SetPassthroughListener(SBListener &listener) {
+  LLDB_INSTRUMENT_VA(this, listener);
+
+  ListenerSP listener_sp = listener.GetSP();
+  if (listener_sp && listener.IsValid())
+    listener_sp->SetPassthrough(true);
+  else
+    listener_sp = nullptr;
+
+  m_opaque_sp->SetPassthroughListener(listener_sp);
+}
+
 const char *SBAttachInfo::GetScriptedProcessClassName() const {
   LLDB_INSTRUMENT_VA(this);
 
Index: lldb/include/lldb/Utility/ProcessInfo.h
===================================================================
--- lldb/include/lldb/Utility/ProcessInfo.h
+++ lldb/include/lldb/Utility/ProcessInfo.h
@@ -110,6 +110,14 @@
     m_hijack_listener_sp = listener_sp;
   }
 
+  lldb::ListenerSP GetPassthroughListener() const {
+    return m_passthrough_listener_sp;
+  }
+
+  void SetPassthroughListener(const lldb::ListenerSP &listener_sp) {
+    m_passthrough_listener_sp = listener_sp;
+  }
+
 protected:
   FileSpec m_executable;
   std::string m_arg0; // argv[0] if supported. If empty, then use m_executable.
@@ -124,6 +132,7 @@
   lldb::ScriptedMetadataSP m_scripted_metadata_sp = nullptr;
   lldb::ListenerSP m_listener_sp = nullptr;
   lldb::ListenerSP m_hijack_listener_sp = nullptr;
+  lldb::ListenerSP m_passthrough_listener_sp = nullptr;
 };
 
 // ProcessInstanceInfo
Index: lldb/include/lldb/Utility/Listener.h
===================================================================
--- lldb/include/lldb/Utility/Listener.h
+++ lldb/include/lldb/Utility/Listener.h
@@ -95,6 +95,10 @@
 
   size_t HandleBroadcastEvent(lldb::EventSP &event_sp);
 
+  void SetPassthrough(bool is_passthrough) {
+    m_is_passthrough = is_passthrough;
+  }
+
 private:
   // Classes that inherit from Listener can see and modify these
   struct BroadcasterInfo {
@@ -134,6 +138,7 @@
   std::mutex m_events_mutex; // Protects m_broadcasters and m_events
   std::condition_variable m_events_condition;
   broadcaster_manager_collection m_broadcaster_managers;
+  bool m_is_passthrough = false;
 
   void BroadcasterWillDestruct(Broadcaster *);
 
Index: lldb/include/lldb/Utility/Broadcaster.h
===================================================================
--- lldb/include/lldb/Utility/Broadcaster.h
+++ lldb/include/lldb/Utility/Broadcaster.h
@@ -406,6 +406,10 @@
 
   lldb::BroadcasterManagerSP GetManager();
 
+  virtual void SetPassthroughListener(lldb::ListenerSP listener_sp) {
+    m_broadcaster_sp->m_passthrough_listener = listener_sp;
+  }
+
 protected:
   /// BroadcasterImpl contains the actual Broadcaster implementation.  The
   /// Broadcaster makes a BroadcasterImpl which lives as long as it does.  The
@@ -513,6 +517,10 @@
     /// for now this is just for private hijacking.
     std::vector<uint32_t> m_hijacking_masks;
 
+    /// A optional listener that all private events get also broadcasted to,
+    /// on top the hijacked / default listeners.
+    lldb::ListenerSP m_passthrough_listener = nullptr;
+
   private:
     BroadcasterImpl(const BroadcasterImpl &) = delete;
     const BroadcasterImpl &operator=(const BroadcasterImpl &) = delete;
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -373,6 +373,10 @@
     return GetStaticBroadcasterClass();
   }
 
+  void SetPassthroughListener(lldb::ListenerSP listener_sp) override {
+    Broadcaster::SetPassthroughListener(listener_sp);
+  }
+
 /// A notification structure that can be used by clients to listen
 /// for changes in a process's lifetime.
 ///
Index: lldb/include/lldb/API/SBLaunchInfo.h
===================================================================
--- lldb/include/lldb/API/SBLaunchInfo.h
+++ lldb/include/lldb/API/SBLaunchInfo.h
@@ -92,6 +92,25 @@
   /// allows a different listener to be used to listen for process events.
   void SetListener(SBListener &listener);
 
+  /// Get the passthrough listener that receive public process events,
+  /// additionally to the default process event listener.
+  ///
+  /// If no listener has been set via a call to
+  /// SBLaunchInfo::SetPassthroughListener(), then an invalid SBListener will
+  /// be returned (SBListener::IsValid() will return false). If a listener
+  /// has been set, then the valid listener object will be returned.
+  SBListener GetPassthroughListener();
+
+  /// Set the passthrough listener that will receive public process events,
+  /// additionally to the default process event listener.
+  ///
+  /// By default a process have no passthrough event listener.
+  /// Calling this function allows public process events to be broadcasted to an
+  /// additional listener on top of the default process event listener.
+  /// If the `listener` argument is invalid (SBListener::IsValid() will
+  /// return false), this will clear the passthrough listener.
+  void SetPassthroughListener(SBListener &listener);
+
   uint32_t GetNumArguments();
 
   const char *GetArgumentAtIndex(uint32_t idx);
Index: lldb/include/lldb/API/SBAttachInfo.h
===================================================================
--- lldb/include/lldb/API/SBAttachInfo.h
+++ lldb/include/lldb/API/SBAttachInfo.h
@@ -168,6 +168,25 @@
   /// allows a different listener to be used to listen for process events.
   void SetListener(SBListener &listener);
 
+  /// Get the passthrough listener that receive public process events,
+  /// additionally to the default process event listener.
+  ///
+  /// If no listener has been set via a call to
+  /// SBLaunchInfo::SetPassthroughListener(), then an invalid SBListener will
+  /// be returned (SBListener::IsValid() will return false). If a listener
+  /// has been set, then the valid listener object will be returned.
+  SBListener GetPassthroughListener();
+
+  /// Set the passthrough listener that will receive public process events,
+  /// additionally to the default process event listener.
+  ///
+  /// By default a process have no passthrough event listener.
+  /// Calling this function allows public process events to be broadcasted to an
+  /// additional listener on top of the default process event listener.
+  /// If the `listener` argument is invalid (SBListener::IsValid() will
+  /// return false), this will clear the passthrough listener.
+  void SetPassthroughListener(SBListener &listener);
+
   const char *GetScriptedProcessClassName() const;
 
   void SetScriptedProcessClassName(const char *class_name);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to