mgorny updated this revision to Diff 331982.
mgorny added a comment.

Now using the new thingie for threads. `getTIDForPID()` temporarily commented 
out but I plan to use it to distinguish `clone()` for new thread from `clone()` 
for new process.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D98822/new/

https://reviews.llvm.org/D98822

Files:
  lldb/include/lldb/Host/common/NativeProcessProtocol.h
  lldb/include/lldb/Host/linux/Host.h
  lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
  lldb/source/Host/linux/Host.cpp
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
  lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h

Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -201,6 +201,8 @@
 
   PacketResult Handle_g(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_qSupported_LLGS(StringExtractorGDBRemote &packet);
+
   void SetCurrentThreadID(lldb::tid_t tid);
 
   lldb::tid_t GetCurrentThreadID() const;
@@ -219,6 +221,9 @@
 
   static std::string XMLEncodeAttributeValue(llvm::StringRef value);
 
+  llvm::SmallVector<std::string, 16>
+  HandleFeatures(const llvm::ArrayRef<llvm::StringRef> client_features) override;
+
 private:
   llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> BuildTargetXml();
 
@@ -255,6 +260,9 @@
                                       bool allow_any = false,
                                       bool allow_all = false);
 
+  // Call SetEnabledExtensions() with appropriate flags on the process.
+  void SetEnabledExtensions(NativeProcessProtocol& process);
+
   // For GDBRemoteCommunicationServerLLGS only
   GDBRemoteCommunicationServerLLGS(const GDBRemoteCommunicationServerLLGS &) =
       delete;
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -254,6 +254,8 @@
     m_debugged_process_up = std::move(*process_or);
   }
 
+  SetEnabledExtensions(*m_debugged_process_up);
+
   // Handle mirroring of inferior stdout/stderr over the gdb-remote protocol as
   // needed. llgs local-process debugging may specify PTY paths, which will
   // make these file actions non-null process launch -i/e/o will also make
@@ -321,6 +323,7 @@
     return status;
   }
   m_debugged_process_up = std::move(*process_or);
+  SetEnabledExtensions(*m_debugged_process_up);
 
   // Setup stdout/stderr mapping from inferior.
   auto terminal_fd = m_debugged_process_up->GetTerminalFileDescriptor();
@@ -3703,3 +3706,29 @@
 
   return tid;
 }
+
+llvm::SmallVector<std::string, 16>
+GDBRemoteCommunicationServerLLGS::HandleFeatures(
+    const llvm::ArrayRef<llvm::StringRef> client_features) {
+  auto ret =
+      GDBRemoteCommunicationServerCommon::HandleFeatures(client_features);
+  if (m_debugged_process_up)
+    SetEnabledExtensions(*m_debugged_process_up);
+  return ret;
+}
+
+void GDBRemoteCommunicationServerLLGS::SetEnabledExtensions(
+    NativeProcessProtocol &process) {
+  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+  NativeProcessProtocol::Extension flags;
+  if (m_fork_events_supported)
+    flags |= NativeProcessProtocol::Extension::fork;
+  if (m_vfork_events_supported)
+    flags |= NativeProcessProtocol::Extension::vfork;
+
+  llvm::Error error = process.SetEnabledExtensions(flags);
+  if (error)
+    LLDB_LOG_ERROR(log, std::move(error),
+                   "Enabling protocol extensions failed: {0}");
+}
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
@@ -38,6 +38,8 @@
   uint32_t m_proc_infos_index;
   bool m_thread_suffix_supported;
   bool m_list_threads_in_stop_reply;
+  bool m_fork_events_supported;
+  bool m_vfork_events_supported;
 
   PacketResult Handle_A(StringExtractorGDBRemote &packet);
 
@@ -145,6 +147,11 @@
   virtual FileSpec FindModuleFile(const std::string &module_path,
                                   const ArchSpec &arch);
 
+  // Process client_features (qSupported) and return an array of server features
+  // to be returned in response.
+  virtual llvm::SmallVector<std::string, 16>
+  HandleFeatures(const llvm::ArrayRef<llvm::StringRef> client_features);
+
 private:
   ModuleSpec GetModuleInfo(llvm::StringRef module_path, llvm::StringRef triple);
 };
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -61,7 +61,8 @@
     : GDBRemoteCommunicationServer(comm_name, listener_name),
       m_process_launch_info(), m_process_launch_error(), m_proc_infos(),
       m_proc_infos_index(0), m_thread_suffix_supported(false),
-      m_list_threads_in_stop_reply(false) {
+      m_list_threads_in_stop_reply(false), m_fork_events_supported(false),
+      m_vfork_events_supported(false) {
   RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_A,
                                 &GDBRemoteCommunicationServerCommon::Handle_A);
   RegisterMemberFunctionHandler(
@@ -831,26 +832,11 @@
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerCommon::Handle_qSupported(
     StringExtractorGDBRemote &packet) {
-  StreamGDBRemote response;
-
-  // Features common to lldb-platform and llgs.
-  uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet
-                                         // size--debugger can always use less
-  response.Printf("PacketSize=%x", max_packet_size);
-
-  response.PutCString(";QStartNoAckMode+");
-  response.PutCString(";QThreadSuffixSupported+");
-  response.PutCString(";QListThreadsInStopReply+");
-  response.PutCString(";qEcho+");
-  response.PutCString(";qXfer:features:read+");
-#if defined(__linux__) || defined(__NetBSD__) || defined(__FreeBSD__)
-  response.PutCString(";QPassSignals+");
-  response.PutCString(";qXfer:auxv:read+");
-  response.PutCString(";qXfer:libraries-svr4:read+");
-#endif
-  response.PutCString(";multiprocess+");
-
-  return SendPacketNoLock(response.GetString());
+  // Parse client-indicated features.
+  llvm::StringRef view = packet.GetStringRef();
+  llvm::SmallVector<llvm::StringRef, 4> client_features;
+  view.split(client_features, ';');
+  return SendPacketNoLock(llvm::join(HandleFeatures(client_features), ";"));
 }
 
 GDBRemoteCommunication::PacketResult
@@ -1312,3 +1298,36 @@
 
   return matched_module_spec;
 }
+
+llvm::SmallVector<std::string, 16>
+GDBRemoteCommunicationServerCommon::HandleFeatures(
+    const llvm::ArrayRef<llvm::StringRef> client_features) {
+  // Features common to lldb-platform and llgs.
+  uint32_t max_packet_size = 128 * 1024; // 128KBytes is a reasonable max packet
+                                         // size--debugger can always use less
+
+  llvm::SmallVector<std::string, 16> server_features{{
+    llvm::formatv("PacketSize={0}", max_packet_size),
+    "QStartNoAckMode+",
+    "QThreadSuffixSupported+",
+    "qEcho+",
+    "qXfer:features:read+",
+#if defined(__linux__) || defined(__NetBSD__) || defined(__FreeBSD__)
+    "QPassSignals+",
+    "qXfer:auxv:read+",
+    "qXfer:libraries-svr4:read+",
+#endif
+    "multiprocess+",
+    "fork-events+",
+    "vfork-events+",
+  }};
+
+  for (const auto &x : client_features) {
+    if (x == "fork-events+")
+      m_fork_events_supported = true;
+    else if (x == "vfork-events+")
+      m_vfork_events_supported = true;
+  }
+
+  return server_features;
+}
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -559,6 +559,8 @@
   LazyBool m_supports_QPassSignals;
   LazyBool m_supports_error_string_reply;
   LazyBool m_supports_multiprocess;
+  LazyBool m_supports_fork_events;
+  LazyBool m_supports_vfork_events;
 
   bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
       m_supports_qUserName : 1, m_supports_qGroupName : 1,
Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -90,6 +90,8 @@
       m_supports_QPassSignals(eLazyBoolCalculate),
       m_supports_error_string_reply(eLazyBoolCalculate),
       m_supports_multiprocess(eLazyBoolCalculate),
+      m_supports_fork_events(eLazyBoolCalculate),
+      m_supports_vfork_events(eLazyBoolCalculate),
       m_supports_qProcessInfoPID(true), m_supports_qfProcessInfo(true),
       m_supports_qUserName(true), m_supports_qGroupName(true),
       m_supports_qThreadStopInfo(true), m_supports_z0(true),
@@ -294,6 +296,8 @@
     m_attach_or_wait_reply = eLazyBoolCalculate;
     m_avoid_g_packets = eLazyBoolCalculate;
     m_supports_multiprocess = eLazyBoolCalculate;
+    m_supports_fork_events = eLazyBoolCalculate;
+    m_supports_vfork_events = eLazyBoolCalculate;
     m_supports_qXfer_auxv_read = eLazyBoolCalculate;
     m_supports_qXfer_libraries_read = eLazyBoolCalculate;
     m_supports_qXfer_libraries_svr4_read = eLazyBoolCalculate;
@@ -345,12 +349,16 @@
   m_supports_qXfer_features_read = eLazyBoolNo;
   m_supports_qXfer_memory_map_read = eLazyBoolNo;
   m_supports_multiprocess = eLazyBoolNo;
+  m_supports_fork_events = eLazyBoolNo;
+  m_supports_vfork_events = eLazyBoolNo;
   m_max_packet_size = UINT64_MAX; // It's supposed to always be there, but if
                                   // not, we assume no limit
 
   // build the qSupported packet
   std::vector<std::string> features = {"xmlRegisters=i386,arm,mips,arc",
-                                       "multiprocess+"};
+                                       "multiprocess+",
+                                       "fork-events+",
+                                       "vfork-events+"};
   StreamString packet;
   packet.PutCString("qSupported");
   for (uint32_t i = 0; i < features.size(); ++i) {
@@ -442,6 +450,16 @@
     else
       m_supports_multiprocess = eLazyBoolNo;
 
+    if (::strstr(response_cstr, "fork-events+"))
+      m_supports_fork_events = eLazyBoolYes;
+    else
+      m_supports_fork_events = eLazyBoolNo;
+
+    if (::strstr(response_cstr, "vfork-events+"))
+      m_supports_vfork_events = eLazyBoolYes;
+    else
+      m_supports_vfork_events = eLazyBoolNo;
+
     const char *packet_size_str = ::strstr(response_cstr, "PacketSize=");
     if (packet_size_str) {
       StringExtractorGDBRemote packet_response(packet_size_str +
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -128,6 +128,8 @@
 
   bool SupportHardwareSingleStepping() const;
 
+  llvm::Error SetEnabledExtensions(Extension flags) override;
+
 protected:
   llvm::Expected<llvm::ArrayRef<uint8_t>>
   GetSoftwareBreakpointTrapOpcode(size_t size_hint) override;
@@ -146,6 +148,8 @@
   /// Inferior memory (allocated by us) and its size.
   llvm::DenseMap<lldb::addr_t, lldb::addr_t> m_allocated_memory;
 
+  Extension m_enabled_extensions;
+
   // Private Instance Methods
   NativeProcessLinux(::pid_t pid, int terminal_fd, NativeDelegate &delegate,
                      const ArchSpec &arch, MainLoop &mainloop,
@@ -154,7 +158,7 @@
   // Returns a list of process threads that we have attached to.
   static llvm::Expected<std::vector<::pid_t>> Attach(::pid_t pid);
 
-  static Status SetDefaultPtraceOpts(const lldb::pid_t);
+  static Status SetPtraceOpts(const lldb::pid_t, Extension extensions = {});
 
   void MonitorCallback(lldb::pid_t pid, bool exited, WaitStatus status);
 
@@ -248,6 +252,21 @@
 
   lldb::user_id_t m_pt_proces_trace_id = LLDB_INVALID_UID;
   TraceOptions m_pt_process_trace_config;
+
+  struct CloneInfo {
+    uint32_t event;
+    lldb::tid_t parent_tid;
+  };
+
+  // Map of child processes that have been signaled once, and we are
+  // waiting for the second signal.
+  llvm::DenseMap<lldb::pid_t, llvm::Optional<CloneInfo>> m_pending_pid_map;
+
+  // Handle a clone()-like event.  If received by parent, clone_info contains
+  // additional info.  Returns true if the event is handled, or false if it
+  // is pending second notification.
+  bool MonitorClone(lldb::pid_t child_pid,
+                    llvm::Optional<CloneInfo> clone_info);
 };
 
 } // namespace process_linux
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -30,6 +30,9 @@
 #include "lldb/Host/PseudoTerminal.h"
 #include "lldb/Host/ThreadLauncher.h"
 #include "lldb/Host/common/NativeRegisterContext.h"
+#if 0
+#include "lldb/Host/linux/Host.h"
+#endif
 #include "lldb/Host/linux/Ptrace.h"
 #include "lldb/Host/linux/Uio.h"
 #include "lldb/Host/posix/ProcessLauncherPosixFork.h"
@@ -247,7 +250,7 @@
   LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid,
            Info.GetArchitecture().GetArchitectureName());
 
-  status = SetDefaultPtraceOpts(pid);
+  status = SetPtraceOpts(pid);
   if (status.Fail()) {
     LLDB_LOG(log, "failed to set default ptrace options: {0}", status);
     return status.ToError();
@@ -286,7 +289,8 @@
                                        NativeDelegate &delegate,
                                        const ArchSpec &arch, MainLoop &mainloop,
                                        llvm::ArrayRef<::pid_t> tids)
-    : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch) {
+    : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch),
+      m_enabled_extensions() {
   if (m_terminal_fd != -1) {
     Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
     assert(status.Success());
@@ -351,7 +355,7 @@
               std::error_code(errno, std::generic_category()));
         }
 
-        if ((status = SetDefaultPtraceOpts(tid)).Fail())
+        if ((status = SetPtraceOpts(tid)).Fail())
           return status.ToError();
 
         LLDB_LOG(log, "adding tid = {0}", tid);
@@ -375,7 +379,8 @@
   return std::move(tids);
 }
 
-Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) {
+Status NativeProcessLinux::SetPtraceOpts(lldb::pid_t pid,
+                                         Extension extensions) {
   long ptrace_opts = 0;
 
   // Have the child raise an event on exit.  This is used to keep the child in
@@ -383,14 +388,17 @@
   ptrace_opts |= PTRACE_O_TRACEEXIT;
 
   // Have the tracer trace threads which spawn in the inferior process.
-  // TODO: if we want to support tracing the inferiors' child, add the
-  // appropriate ptrace flags here (PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK)
   ptrace_opts |= PTRACE_O_TRACECLONE;
 
   // Have the tracer notify us before execve returns (needed to disable legacy
   // SIGTRAP generation)
   ptrace_opts |= PTRACE_O_TRACEEXEC;
 
+  // Have the tracer trace forked children if the fork-events extension
+  // is supported.
+  if ((extensions & Extension::fork) == Extension::fork)
+    ptrace_opts |= PTRACE_O_TRACEFORK;
+
   return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts);
 }
 
@@ -444,11 +452,7 @@
     LLDB_LOG(log, "tid {0}, si_code: {1}, si_pid: {2}", pid, info.si_code,
              info.si_pid);
 
-    NativeThreadLinux &thread = AddThread(pid);
-
-    // Resume the newly created thread.
-    ResumeThread(thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
-    ThreadWasCreated(thread);
+    MonitorClone(pid, llvm::None);
     return;
   }
 
@@ -515,12 +519,6 @@
 void NativeProcessLinux::WaitForNewThread(::pid_t tid) {
   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
 
-  if (GetThreadByID(tid)) {
-    // We are already tracking the thread - we got the event on the new thread
-    // (see MonitorSignal) before this one. We are done.
-    return;
-  }
-
   // The thread is not tracked yet, let's wait for it to appear.
   int status = -1;
   LLDB_LOG(log,
@@ -550,10 +548,7 @@
   }
 
   LLDB_LOG(log, "pid = {0}: tracking new thread tid {1}", GetID(), tid);
-  NativeThreadLinux &new_thread = AddThread(tid);
-
-  ResumeThread(new_thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
-  ThreadWasCreated(new_thread);
+  MonitorClone(tid, llvm::None);
 }
 
 void NativeProcessLinux::MonitorSIGTRAP(const siginfo_t &info,
@@ -580,10 +575,12 @@
                "pid {0} received thread creation event but "
                "GetEventMessage failed so we don't know the new tid",
                thread.GetID());
-    } else
-      WaitForNewThread(event_message);
+      ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER);
+    } else {
+      if (!MonitorClone(event_message, {{PTRACE_EVENT_CLONE, thread.GetID()}}))
+        WaitForNewThread(event_message);
+    }
 
-    ResumeThread(thread, thread.GetState(), LLDB_INVALID_SIGNAL_NUMBER);
     break;
   }
 
@@ -649,6 +646,14 @@
     break;
   }
 
+  case (SIGTRAP | (PTRACE_EVENT_FORK << 8)): {
+    unsigned long data = 0;
+    if (GetEventMessage(thread.GetID(), &data).Fail())
+      data = 0;
+
+    MonitorClone(data, {{PTRACE_EVENT_FORK, thread.GetID()}});
+    break;
+  }
   case 0:
   case TRAP_TRACE:  // We receive this on single stepping.
   case TRAP_HWBKPT: // We receive this on watchpoint hit
@@ -858,6 +863,71 @@
   StopRunningThreads(thread.GetID());
 }
 
+bool NativeProcessLinux::MonitorClone(
+    lldb::pid_t child_pid,
+    llvm::Optional<NativeProcessLinux::CloneInfo> clone_info) {
+  Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+  LLDB_LOG(log, "clone, child_pid={0}, clone info?={1}", child_pid,
+           clone_info.hasValue());
+
+  auto find_it = m_pending_pid_map.find(child_pid);
+  if (find_it == m_pending_pid_map.end()) {
+    // not in the map, so this is the first signal for the PID
+    m_pending_pid_map.insert({child_pid, clone_info});
+    return false;
+  }
+
+  // second signal for the pid
+  assert(clone_info.hasValue() != find_it->second.hasValue());
+  if (!clone_info) {
+    // child signal does not indicate the event, so grab the one stored
+    // earlier
+    clone_info = find_it->second;
+  }
+
+  LLDB_LOG(log, "second signal for child_pid={0}, parent_tid={1}, event={2}",
+           child_pid, clone_info->parent_tid, clone_info->event);
+
+  auto parent_thread = GetThreadByID(clone_info->parent_tid);
+  assert(parent_thread);
+
+#if 0
+    llvm::Optional<lldb::pid_t> tgid = getPIDForTID(pid);
+    if (tgid && tgid.getValue() != GetID()) {
+      LLDB_LOG(log, "tid {0} belongs to a different tgid {1}, assuming child",
+               pid, tgid.getValue());
+      MonitorClone(pid, llvm::None);
+      return;
+    }
+#endif
+
+  switch (clone_info->event) {
+    case PTRACE_EVENT_CLONE: {
+      NativeThreadLinux &child_thread = AddThread(child_pid);
+      // Resume the newly created thread.
+      ResumeThread(child_thread, eStateRunning, LLDB_INVALID_SIGNAL_NUMBER);
+      ThreadWasCreated(child_thread);
+
+      // Resume the parent.
+      ResumeThread(*parent_thread, parent_thread->GetState(),
+                   LLDB_INVALID_SIGNAL_NUMBER);
+      break;
+    }
+    case PTRACE_EVENT_FORK: {
+      // for now, just detach the child and resume the parent
+      Detach(child_pid);
+      ResumeThread(*parent_thread, parent_thread->GetState(),
+                   LLDB_INVALID_SIGNAL_NUMBER);
+      break;
+    }
+    default:
+      assert(false && "unknown clone_info.event");
+  }
+
+  m_pending_pid_map.erase(child_pid);
+  return true;
+}
+
 bool NativeProcessLinux::SupportHardwareSingleStepping() const {
   if (m_arch.GetMachine() == llvm::Triple::arm || m_arch.IsMIPS())
     return false;
@@ -2021,3 +2091,16 @@
 
   return error;
 }
+
+llvm::Error NativeProcessLinux::SetEnabledExtensions(Extension flags) {
+  m_enabled_extensions = flags;
+  for (const auto &thread : m_threads) {
+    assert(thread && "thread list should not contain NULL threads");
+
+    Status status = SetPtraceOpts(thread->GetID(), flags);
+    if (status.Fail())
+      return status.ToError();
+  }
+
+  return llvm::Error::success();
+}
Index: lldb/source/Host/linux/Host.cpp
===================================================================
--- lldb/source/Host/linux/Host.cpp
+++ lldb/source/Host/linux/Host.cpp
@@ -27,6 +27,7 @@
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Host/HostInfo.h"
+#include "lldb/Host/linux/Host.h"
 #include "lldb/Host/linux/Support.h"
 #include "lldb/Utility/DataExtractor.h"
 
@@ -53,7 +54,8 @@
 }
 
 static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,
-                          ProcessState &State, ::pid_t &TracerPid) {
+                          ProcessState &State, ::pid_t &TracerPid,
+                          ::pid_t &Tgid) {
   Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
 
   auto BufferOrError = getProcFile(Pid, "status");
@@ -107,6 +109,9 @@
     } else if (Line.consume_front("TracerPid:")) {
       Line = Line.ltrim();
       Line.consumeInteger(10, TracerPid);
+    } else if (Line.consume_front("Tgid:")) {
+      Line = Line.ltrim();
+      Line.consumeInteger(10, Tgid);
     }
   }
   return true;
@@ -204,6 +209,7 @@
 static bool GetProcessAndStatInfo(::pid_t pid,
                                   ProcessInstanceInfo &process_info,
                                   ProcessState &State, ::pid_t &tracerpid) {
+  ::pid_t tgid;
   tracerpid = 0;
   process_info.Clear();
 
@@ -214,7 +220,7 @@
   GetProcessEnviron(pid, process_info);
 
   // Get User and Group IDs and get tracer pid.
-  if (!GetStatusInfo(pid, process_info, State, tracerpid))
+  if (!GetStatusInfo(pid, process_info, State, tracerpid, tgid))
     return false;
 
   return true;
@@ -308,3 +314,13 @@
 Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
   return Status("unimplemented");
 }
+
+llvm::Optional<lldb::pid_t> lldb_private::getPIDForTID(lldb::pid_t tid) {
+  ::pid_t tracerpid, tgid = LLDB_INVALID_PROCESS_ID;
+  ProcessInstanceInfo process_info;
+  ProcessState state;
+
+  if (!GetStatusInfo(tid, process_info, state, tracerpid, tgid) || tgid == 0)
+    return llvm::None;
+  return tgid;
+}
Index: lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
+++ lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
@@ -904,6 +904,8 @@
         "qEcho",
         "QPassSignals",
         "multiprocess",
+        "fork-events",
+        "vfork-events",
     ]
 
     def parse_qSupported_response(self, context):
Index: lldb/include/lldb/Host/linux/Host.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Host/linux/Host.h
@@ -0,0 +1,23 @@
+//===-- Host.h --------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_HOST_LINUX_HOST_H
+#define LLDB_HOST_LINUX_HOST_H
+
+#include "llvm/ADT/Optional.h"
+
+#include "lldb/lldb-types.h"
+
+namespace lldb_private {
+
+// Get PID (i.e. the primary thread ID) corresponding to the specified TID.
+llvm::Optional<lldb::pid_t> getPIDForTID(lldb::pid_t tid);
+
+} // namespace lldb_private
+
+#endif // #ifndef LLDB_HOST_LINUX_HOST_H
Index: lldb/include/lldb/Host/common/NativeProcessProtocol.h
===================================================================
--- lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -30,6 +30,8 @@
 #include <vector>
 
 namespace lldb_private {
+LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE();
+
 class MemoryRegionInfo;
 class ResumeActionList;
 
@@ -397,6 +399,26 @@
     return llvm::make_error<UnimplementedError>();
   }
 
+  /// Extension flag constants, passed to SetEnabledExtension().
+  enum class Extension {
+    fork = (1u << 0),
+    vfork = (1u << 1),
+
+    LLVM_MARK_AS_BITMASK_ENUM(vfork)
+  };
+
+  /// Method called in order to propagate the bitmap of protocol
+  /// extensions supported by the client.
+  ///
+  /// \param[in] flags
+  ///     The bitmap of enabled extensions.
+  ///
+  /// \return An error if extension-related setup failed, success
+  /// otherwise.
+  virtual llvm::Error SetEnabledExtensions(Extension flags) {
+    return llvm::Error::success();
+  }
+
 protected:
   struct SoftwareBreakpoint {
     uint32_t ref_count;
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to