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

Added breakpoint cleanup via gdb-remote protocol. Only minimal code coverage so 
far. If this approach is okay, I guess I'll add subprocess guards to all server 
commands.


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

https://reviews.llvm.org/D99864

Files:
  lldb/bindings/interface/SBThread.i
  lldb/bindings/interface/SBThreadPlan.i
  lldb/docs/python_api_enums.rst
  lldb/examples/python/performance.py
  lldb/include/lldb/API/SBThread.h
  lldb/include/lldb/API/SBThreadPlan.h
  lldb/include/lldb/Host/Debug.h
  lldb/include/lldb/Host/common/NativeProcessProtocol.h
  lldb/include/lldb/Target/Process.h
  lldb/include/lldb/Target/StopInfo.h
  lldb/include/lldb/lldb-enumerations.h
  lldb/packages/Python/lldbsuite/test/lldbutil.py
  lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
  lldb/source/API/SBThread.cpp
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
  lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
  lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
  lldb/source/Plugins/Process/Linux/NativeThreadLinux.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
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
  lldb/source/Target/Process.cpp
  lldb/source/Target/StackFrameList.cpp
  lldb/source/Target/StopInfo.cpp
  lldb/source/Target/Thread.cpp
  lldb/source/Utility/StringExtractorGDBRemote.cpp
  lldb/tools/lldb-vscode/JSONUtils.cpp
  lldb/tools/lldb-vscode/LLDBUtils.cpp

Index: lldb/tools/lldb-vscode/LLDBUtils.cpp
===================================================================
--- lldb/tools/lldb-vscode/LLDBUtils.cpp
+++ lldb/tools/lldb-vscode/LLDBUtils.cpp
@@ -55,6 +55,8 @@
   case lldb::eStopReasonSignal:
   case lldb::eStopReasonException:
   case lldb::eStopReasonExec:
+  case lldb::eStopReasonFork:
+  case lldb::eStopReasonVFork:
   case lldb::eStopReasonProcessorTrace:
     return true;
   case lldb::eStopReasonThreadExiting:
Index: lldb/tools/lldb-vscode/JSONUtils.cpp
===================================================================
--- lldb/tools/lldb-vscode/JSONUtils.cpp
+++ lldb/tools/lldb-vscode/JSONUtils.cpp
@@ -877,6 +877,12 @@
   case lldb::eStopReasonExec:
     body.try_emplace("reason", "entry");
     break;
+  case lldb::eStopReasonFork:
+    body.try_emplace("reason", "fork");
+    break;
+  case lldb::eStopReasonVFork:
+    body.try_emplace("reason", "vfork");
+    break;
   case lldb::eStopReasonThreadExiting:
   case lldb::eStopReasonInvalid:
   case lldb::eStopReasonNone:
Index: lldb/source/Utility/StringExtractorGDBRemote.cpp
===================================================================
--- lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -378,9 +378,7 @@
     return eServerPacketType_C;
 
   case 'D':
-    if (packet_size == 1)
-      return eServerPacketType_D;
-    break;
+    return eServerPacketType_D;
 
   case 'g':
     return eServerPacketType_g;
Index: lldb/source/Target/Thread.cpp
===================================================================
--- lldb/source/Target/Thread.cpp
+++ lldb/source/Target/Thread.cpp
@@ -1679,6 +1679,10 @@
     return "exception";
   case eStopReasonExec:
     return "exec";
+  case eStopReasonFork:
+    return "fork";
+  case eStopReasonVFork:
+    return "vfork";
   case eStopReasonPlanComplete:
     return "plan complete";
   case eStopReasonThreadExiting:
Index: lldb/source/Target/StopInfo.cpp
===================================================================
--- lldb/source/Target/StopInfo.cpp
+++ lldb/source/Target/StopInfo.cpp
@@ -1145,6 +1145,40 @@
   bool m_performed_action;
 };
 
+// StopInfoFork
+
+class StopInfoFork : public StopInfo {
+public:
+  StopInfoFork(Thread &thread, lldb::pid_t child_pid, lldb::tid_t child_tid)
+      : StopInfo(thread, LLDB_INVALID_UID), m_performed_action(false),
+        m_child_pid(child_pid), m_child_tid(child_tid) {}
+
+  ~StopInfoFork() override = default;
+
+  bool ShouldStop(Event *event_ptr) override { return false; }
+
+  StopReason GetStopReason() const override { return eStopReasonFork; }
+
+  const char *GetDescription() override { return "fork"; }
+
+protected:
+  void PerformAction(Event *event_ptr) override {
+    // Only perform the action once
+    if (m_performed_action)
+      return;
+    m_performed_action = true;
+    ThreadSP thread_sp(m_thread_wp.lock());
+    if (thread_sp)
+      thread_sp->GetProcess()->DidFork(m_child_pid, m_child_tid);
+  }
+
+  bool m_performed_action;
+
+private:
+  lldb::pid_t m_child_pid;
+  lldb::tid_t m_child_tid;
+};
+
 } // namespace lldb_private
 
 StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread,
@@ -1194,6 +1228,12 @@
   return StopInfoSP(new StopInfoExec(thread));
 }
 
+StopInfoSP StopInfo::CreateStopReasonWithFork(Thread &thread,
+                                              lldb::pid_t child_pid,
+                                              lldb::tid_t child_tid) {
+  return StopInfoSP(new StopInfoFork(thread, child_pid, child_tid));
+}
+
 ValueObjectSP StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp) {
   if (stop_info_sp &&
       stop_info_sp->GetStopReason() == eStopReasonPlanComplete) {
Index: lldb/source/Target/StackFrameList.cpp
===================================================================
--- lldb/source/Target/StackFrameList.cpp
+++ lldb/source/Target/StackFrameList.cpp
@@ -131,6 +131,8 @@
   case eStopReasonWatchpoint:
   case eStopReasonException:
   case eStopReasonExec:
+  case eStopReasonFork:
+  case eStopReasonVFork:
   case eStopReasonSignal:
     // In all these cases we want to stop in the deepest frame.
     m_current_inlined_pc = curr_pc;
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -800,6 +800,8 @@
             case eStopReasonWatchpoint:
             case eStopReasonException:
             case eStopReasonExec:
+            case eStopReasonFork:
+            case eStopReasonVFork:
             case eStopReasonThreadExiting:
             case eStopReasonInstrumentation:
             case eStopReasonProcessorTrace:
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -230,6 +230,8 @@
   std::string HarmonizeThreadIdsForProfileData(
       StringExtractorGDBRemote &inputStringExtractor);
 
+  void DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) override;
+
 protected:
   friend class ThreadGDBRemote;
   friend class GDBRemoteCommunicationClient;
Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1901,6 +1901,26 @@
             } else if (reason == "processor trace") {
               thread_sp->SetStopInfo(StopInfo::CreateStopReasonProcessorTrace(
                   *thread_sp, description.c_str()));
+            } else if (reason == "fork") {
+              StringExtractor desc_extractor(description.c_str());
+              lldb::pid_t child_pid = desc_extractor.GetU64(
+                  LLDB_INVALID_PROCESS_ID);
+              lldb::tid_t child_tid = desc_extractor.GetU64(
+                  LLDB_INVALID_THREAD_ID);
+              thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithFork(
+                  *thread_sp, child_pid, child_tid));
+              handled = true;
+#if 0
+            } else if (reason == "vfork") {
+              StringExtractor desc_extractor(description.c_str());
+              lldb::pid_t child_pid = desc_extractor.GetU64(
+                  LLDB_INVALID_PROCESS_ID);
+              lldb::tid_t child_tid = desc_extractor.GetU64(
+                  LLDB_INVALID_THREAD_ID);
+              thread_sp->SetStopInfo(StopInfo::CreateStopReasonWithVFork(
+                  *thread_sp, child_pid, child_tid));
+              handled = true;
+#endif
             }
           } else if (!signo) {
             addr_t pc = thread_sp->GetRegisterContext()->GetPC();
@@ -2307,6 +2327,21 @@
               ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
           LLDB_LOG_ERROR(log, std::move(error), "Failed to load modules: {0}");
         }
+      } else if (key.compare("fork") == 0 || key.compare("vfork") == 0) {
+        // fork includes child pid/tid in thread-id format
+        StringExtractorGDBRemote thread_id{value};
+        auto pid_tid = thread_id.GetPidTid(LLDB_INVALID_PROCESS_ID);
+        if (!pid_tid) {
+          Log *log(
+              ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+          LLDB_LOG(log, "Invalid PID/TID to fork: {0}", value);
+          pid_tid = {{LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID}};
+        }
+
+        reason = key.str();
+        StreamString ostr;
+        ostr.Printf("%" PRIu64 " %" PRIu64, pid_tid->first, pid_tid->second);
+        description = std::string(ostr.GetString());
       } else if (key.size() == 2 && ::isxdigit(key[0]) && ::isxdigit(key[1])) {
         uint32_t reg = UINT32_MAX;
         if (!key.getAsInteger(16, reg))
@@ -5399,3 +5434,39 @@
         GetTarget().GetDebugger().GetCommandInterpreter());
   return m_command_sp.get();
 }
+
+void ProcessGDBRemote::DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) {
+  Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
+
+  if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) {
+    lldb::pid_t orig_pid = m_gdb_comm.GetCurrentProcessID();
+
+    if (!m_gdb_comm.SetCurrentThread(child_tid, child_pid)) {
+        LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to set pid/tid");
+        return;
+    }
+
+    // Disable all software breakpoints in the forked process
+    GetBreakpointSiteList().ForEach([this](BreakpointSite *bp_site) {
+      if (bp_site->IsEnabled() && (bp_site->GetType() == BreakpointSite::eSoftware || bp_site->GetType() == BreakpointSite::eExternal)) {
+        m_gdb_comm.SendGDBStoppointTypePacket(eBreakpointSoftware, false, bp_site->GetLoadAddress(), bp_site->GetTrapOpcodeMaxByteSize());
+      }
+    });
+
+    // Reset gdb-remote to the original process.  Thread ID does not really
+    // matter, the next command will set it anyway.
+    if (!m_gdb_comm.SetCurrentThread(m_thread_ids.front(), orig_pid)) {
+        LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to reset pid/tid");
+        return;
+    }
+  }
+
+  LLDB_LOG(log, "Detaching forked child {0}", child_pid);
+  Status error = m_gdb_comm.Detach(false, child_pid);
+  if (error.Fail()) {
+      LLDB_LOG(log,
+               "ProcessGDBRemote::DidFork() detach packet send failed: {0}",
+                error.AsCString() ? error.AsCString() : "<unknown error>");
+      return;
+  }
+}
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
@@ -86,6 +86,7 @@
   const NativeProcessProtocol::Factory &m_process_factory;
   lldb::tid_t m_current_tid = LLDB_INVALID_THREAD_ID;
   lldb::tid_t m_continue_tid = LLDB_INVALID_THREAD_ID;
+  lldb::pid_t m_subprocess_pid = LLDB_INVALID_PROCESS_ID;
   std::recursive_mutex m_debugged_process_mutex;
   std::unique_ptr<NativeProcessProtocol> m_debugged_process_up;
 
@@ -201,6 +202,8 @@
 
   PacketResult Handle_g(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_qSupported_LLGS(StringExtractorGDBRemote &packet);
+
   void SetCurrentThreadID(lldb::tid_t tid);
 
   lldb::tid_t GetCurrentThreadID() const;
@@ -209,6 +212,8 @@
 
   lldb::tid_t GetContinueThreadID() const { return m_continue_tid; }
 
+  void SetCurrentSubprocess(lldb::pid_t pid, lldb::tid_t tid);
+
   Status SetSTDIOFileDescriptor(int fd);
 
   FileSpec FindModuleFile(const std::string &module_path,
@@ -219,6 +224,15 @@
 
   static std::string XMLEncodeAttributeValue(llvm::StringRef value);
 
+  llvm::SmallVector<std::string, 16>
+  HandleFeatures(const llvm::ArrayRef<llvm::StringRef> client_features) override;
+
+  // Get NativeProcessProtocol instance corresponding to the currently selected
+  // process.  If allow_multiprocess is true, this can be a forked subprocess.
+  // Returns nullptr if no process is being debugged, invalid process was
+  // selected or allow_subprocess is false and a subprocess is selected.
+  NativeProcessProtocol *GetSelectedProcess(bool allow_subprocess = false);
+
 private:
   llvm::Expected<std::unique_ptr<llvm::MemoryBuffer>> BuildTargetXml();
 
@@ -253,6 +267,9 @@
   llvm::Expected<lldb::tid_t> ReadTid(StringExtractorGDBRemote &packet,
                                       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
@@ -251,6 +251,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
@@ -318,6 +320,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();
@@ -647,6 +650,10 @@
     return "exec";
   case eStopReasonProcessorTrace:
     return "processor trace";
+  case eStopReasonFork:
+    return "fork";
+  case eStopReasonVFork:
+    return "vfork";
   case eStopReasonInstrumentation:
   case eStopReasonInvalid:
   case eStopReasonPlanComplete:
@@ -923,6 +930,14 @@
     }
   }
 
+  // Include child process PID/TID for forks.
+  if (tid_stop_info.reason == eStopReasonFork ||
+      tid_stop_info.reason == eStopReasonVFork) {
+    response.Printf("%s:p%" PRIx64 ".%" PRIx64 ";", reason_str,
+                    tid_stop_info.details.fork.child_pid,
+                    tid_stop_info.details.fork.child_tid);
+  }
+
   return SendPacketNoLock(response.GetString());
 }
 
@@ -1607,6 +1622,8 @@
   m_current_tid = tid;
   if (m_debugged_process_up)
     m_debugged_process_up->SetCurrentThreadID(m_current_tid);
+  // reset subprocess support
+  m_subprocess_pid = LLDB_INVALID_PROCESS_ID;
 }
 
 void GDBRemoteCommunicationServerLLGS::SetContinueThreadID(lldb::tid_t tid) {
@@ -1614,6 +1631,17 @@
   LLDB_LOG(log, "setting continue thread id to {0}", tid);
 
   m_continue_tid = tid;
+  // reset subprocess support
+  m_subprocess_pid = LLDB_INVALID_PROCESS_ID;
+}
+
+void GDBRemoteCommunicationServerLLGS::SetCurrentSubprocess(lldb::pid_t pid,
+                                                            lldb::tid_t tid) {
+  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+  LLDB_LOG(log, "setting current subprocess to {0}, thread id {1}", pid, tid);
+
+  m_subprocess_pid = pid;
+  m_current_tid = tid;
 }
 
 GDBRemoteCommunication::PacketResult
@@ -2067,11 +2095,33 @@
   }
 
   // Parse out the thread number.
-  llvm::Expected<lldb::tid_t> tid_ret = ReadTid(packet, /*allow_all=*/true);
-  if (!tid_ret)
-    return SendErrorResponse(tid_ret.takeError());
+  auto pid_tid = packet.GetPidTid(m_debugged_process_up->GetID());
+  if (!pid_tid)
+    return SendErrorResponse(llvm::make_error<StringError>(
+        inconvertibleErrorCode(), "Malformed thread-id"));
+
+  lldb::pid_t pid = pid_tid->first;
+  lldb::tid_t tid = pid_tid->second;
+
+  if (pid == StringExtractorGDBRemote::AllProcesses)
+    return SendUnimplementedResponse("Selecting all processes not supported");
+  if (pid != m_debugged_process_up->GetID()) {
+    // Is it a subprocess?
+    NativeProcessProtocol *subprocess =
+        m_debugged_process_up->GetSubprocess(pid);
+
+    if (!subprocess)
+      return SendErrorResponse(llvm::make_error<StringError>(
+          inconvertibleErrorCode(),
+          llvm::formatv("PID {0} not debugged", pid)));
+    if (h_variant != 'g')
+      return SendUnimplementedResponse(
+          "Hc packet for subprocess not supported");
+
+    SetCurrentSubprocess(pid, tid);
+    return SendOKResponse();
+  }
 
-  lldb::tid_t tid = tid_ret.get();
   // Ensure we have the given thread when not specifying -1 (all threads) or 0
   // (any thread).
   if (tid != LLDB_INVALID_THREAD_ID && tid != 0) {
@@ -2615,13 +2665,9 @@
 
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::Handle_z(StringExtractorGDBRemote &packet) {
-  // Ensure we have a process.
-  if (!m_debugged_process_up ||
-      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
-    Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
-    LLDB_LOG(log, "failed, no process available");
+  NativeProcessProtocol *process = GetSelectedProcess(true);
+  if (!process)
     return SendErrorResponse(0x15);
-  }
 
   // Parse out software or hardware breakpoint or watchpoint requested.
   packet.SetFilePos(strlen("z"));
@@ -2680,22 +2726,21 @@
 
   if (want_breakpoint) {
     // Try to clear the breakpoint.
-    const Status error =
-        m_debugged_process_up->RemoveBreakpoint(addr, want_hardware);
+    const Status error = process->RemoveBreakpoint(addr, want_hardware);
     if (error.Success())
       return SendOKResponse();
     Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_BREAKPOINTS));
-    LLDB_LOG(log, "pid {0} failed to remove breakpoint: {1}",
-             m_debugged_process_up->GetID(), error);
+    LLDB_LOG(log, "pid {0} failed to remove breakpoint: {1}", process->GetID(),
+             error);
     return SendErrorResponse(0x09);
   } else {
     // Try to clear the watchpoint.
-    const Status error = m_debugged_process_up->RemoveWatchpoint(addr);
+    const Status error = process->RemoveWatchpoint(addr);
     if (error.Success())
       return SendOKResponse();
     Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_WATCHPOINTS));
-    LLDB_LOG(log, "pid {0} failed to remove watchpoint: {1}",
-             m_debugged_process_up->GetID(), error);
+    LLDB_LOG(log, "pid {0} failed to remove watchpoint: {1}", process->GetID(),
+             error);
     return SendErrorResponse(0x09);
   }
 }
@@ -3210,19 +3255,27 @@
       return SendIllFormedResponse(packet, "D failed to parse the process id");
   }
 
-  if (pid != LLDB_INVALID_PROCESS_ID && m_debugged_process_up->GetID() != pid) {
-    return SendIllFormedResponse(packet, "Invalid pid");
+  NativeProcessProtocol* process;
+  if (pid == LLDB_INVALID_PROCESS_ID || m_debugged_process_up->GetID() == pid)
+    process = m_debugged_process_up.get();
+  else {
+    process = m_debugged_process_up->GetSubprocess(pid);
+    if (!process)
+      return SendIllFormedResponse(packet, "PID unknown to the debugger");
   }
 
-  const Status error = m_debugged_process_up->Detach();
+  const Status error = process->Detach();
   if (error.Fail()) {
     LLDB_LOGF(log,
               "GDBRemoteCommunicationServerLLGS::%s failed to detach from "
               "pid %" PRIu64 ": %s\n",
-              __FUNCTION__, m_debugged_process_up->GetID(), error.AsCString());
+              __FUNCTION__, process->GetID(), error.AsCString());
     return SendErrorResponse(0x01);
   }
 
+  if (pid != LLDB_INVALID_PROCESS_ID && m_debugged_process_up->GetID() != pid)
+    m_debugged_process_up->EraseSubprocess(pid);
+
   return SendOKResponse();
 }
 
@@ -3534,3 +3587,53 @@
 
   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}");
+}
+
+NativeProcessProtocol *GDBRemoteCommunicationServerLLGS::GetSelectedProcess(
+    bool allow_subprocess) {
+  Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+  // Ensure we have a process.
+  if (!m_debugged_process_up ||
+      (m_debugged_process_up->GetID() == LLDB_INVALID_PROCESS_ID)) {
+    LLDB_LOG(log, "failed, no process available");
+    return nullptr;
+  }
+
+  // If no subprocess is selected, proceed with the main process.
+  if (m_subprocess_pid == LLDB_INVALID_PROCESS_ID)
+    return m_debugged_process_up.get();
+
+  if (!allow_subprocess) {
+    LLDB_LOG(log, "failed, subprocess selected while not allowed");
+    return nullptr;
+  }
+
+  // Otherwise, try to find a subprocess matching requested PID.
+  return m_debugged_process_up->GetSubprocess(m_subprocess_pid);
+}
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
@@ -229,7 +229,7 @@
 
   bool DeallocateMemory(lldb::addr_t addr);
 
-  Status Detach(bool keep_stopped);
+  Status Detach(bool keep_stopped, lldb::pid_t pid = LLDB_INVALID_PROCESS_ID);
 
   Status GetMemoryRegionInfo(lldb::addr_t addr, MemoryRegionInfo &range_info);
 
@@ -334,7 +334,8 @@
   // and response times.
   bool SendSpeedTestPacket(uint32_t send_size, uint32_t recv_size);
 
-  bool SetCurrentThread(uint64_t tid);
+  bool SetCurrentThread(uint64_t tid,
+                        lldb::pid_t pid = LLDB_INVALID_PROCESS_ID);
 
   bool SetCurrentThreadForRun(uint64_t tid);
 
@@ -556,6 +557,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,
@@ -567,6 +570,7 @@
       m_supports_jModulesInfo : 1;
 
   lldb::pid_t m_curr_pid;
+  lldb::pid_t m_override_pid; // Used when handling forks
   lldb::tid_t m_curr_tid; // Current gdb remote protocol thread index for all
                           // other operations
   lldb::tid_t m_curr_tid_run; // Current gdb remote protocol thread index for
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),
@@ -98,7 +100,9 @@
       m_supports_QEnvironmentHexEncoded(true), m_supports_qSymbol(true),
       m_qSymbol_requests_done(false), m_supports_qModuleInfo(true),
       m_supports_jThreadsInfo(true), m_supports_jModulesInfo(true),
-      m_curr_pid(LLDB_INVALID_PROCESS_ID), m_curr_tid(LLDB_INVALID_THREAD_ID),
+      m_curr_pid(LLDB_INVALID_PROCESS_ID),
+      m_override_pid(LLDB_INVALID_PROCESS_ID),
+      m_curr_tid(LLDB_INVALID_THREAD_ID),
       m_curr_tid_run(LLDB_INVALID_THREAD_ID),
       m_num_supported_hardware_watchpoints(0), m_host_arch(), m_process_arch(),
       m_os_build(), m_os_kernel(), m_hostname(), m_gdb_server_name(),
@@ -294,6 +298,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 +351,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 +452,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 +
@@ -1454,9 +1474,12 @@
   return false;
 }
 
-Status GDBRemoteCommunicationClient::Detach(bool keep_stopped) {
+Status GDBRemoteCommunicationClient::Detach(bool keep_stopped,
+                                            lldb::pid_t pid) {
   Status error;
+  lldb_private::StreamString packet;
 
+  packet.PutChar('D');
   if (keep_stopped) {
     if (m_supports_detach_stay_stopped == eLazyBoolCalculate) {
       char packet[64];
@@ -1478,19 +1501,25 @@
       error.SetErrorString("Stays stopped not supported by this target.");
       return error;
     } else {
-      StringExtractorGDBRemote response;
-      PacketResult packet_result =
-          SendPacketAndWaitForResponse("D1", response, false);
-      if (packet_result != PacketResult::Success)
-        error.SetErrorString("Sending extended disconnect packet failed.");
+      packet.PutChar('1');
     }
-  } else {
-    StringExtractorGDBRemote response;
-    PacketResult packet_result =
-        SendPacketAndWaitForResponse("D", response, false);
-    if (packet_result != PacketResult::Success)
-      error.SetErrorString("Sending disconnect packet failed.");
   }
+
+  if (pid != LLDB_INVALID_PROCESS_ID) {
+    if (!m_supports_multiprocess) {
+      error.SetErrorString(
+          "Multiprocess extension not supported by the server.");
+      return error;
+    }
+    packet.PutChar(';');
+    packet.PutHex64(pid);
+  }
+
+  StringExtractorGDBRemote response;
+  PacketResult packet_result =
+      SendPacketAndWaitForResponse(packet.GetString(), response, false);
+  if (packet_result != PacketResult::Success)
+    error.SetErrorString("Sending isconnect packet failed.");
   return error;
 }
 
@@ -2630,22 +2659,32 @@
   return false;
 }
 
-bool GDBRemoteCommunicationClient::SetCurrentThread(uint64_t tid) {
-  if (m_curr_tid == tid)
+bool GDBRemoteCommunicationClient::SetCurrentThread(uint64_t tid,
+                                                    lldb::pid_t pid) {
+  if (m_curr_tid == tid && m_override_pid == pid)
     return true;
 
-  char packet[32];
-  int packet_len;
+  lldb_private::StreamString packet;
+  packet.PutCString("Hg");
+  if (m_override_pid != pid) {
+    if (pid == LLDB_INVALID_PROCESS_ID) {
+      assert(m_curr_pid != LLDB_INVALID_PROCESS_ID);
+      pid = m_curr_pid;
+    }
+    packet.PutChar('p');
+    packet.PutHex64(pid);
+    packet.PutChar('.');
+  }
   if (tid == UINT64_MAX)
-    packet_len = ::snprintf(packet, sizeof(packet), "Hg-1");
+    packet.PutCString("-1");
   else
-    packet_len = ::snprintf(packet, sizeof(packet), "Hg%" PRIx64, tid);
-  assert(packet_len + 1 < (int)sizeof(packet));
-  UNUSED_IF_ASSERT_DISABLED(packet_len);
+    packet.PutHex64(tid);
+
   StringExtractorGDBRemote response;
-  if (SendPacketAndWaitForResponse(packet, response, false) ==
+  if (SendPacketAndWaitForResponse(packet.GetString(), response, false) ==
       PacketResult::Success) {
     if (response.IsOKResponse()) {
+      m_override_pid = pid;
       m_curr_tid = tid;
       return true;
     }
Index: lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeThreadLinux.h
@@ -85,6 +85,8 @@
 
   void SetStoppedByTrace();
 
+  void SetStoppedByFork(lldb::pid_t child_pid);
+
   void SetStoppedWithNoReason();
 
   void SetStoppedByProcessorTrace(llvm::StringRef description);
Index: lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
+++ lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
@@ -394,6 +394,14 @@
   m_stop_info.details.signal.signo = SIGTRAP;
 }
 
+void NativeThreadLinux::SetStoppedByFork(lldb::pid_t child_pid) {
+  SetStopped();
+
+  m_stop_info.reason = StopReason::eStopReasonFork;
+  m_stop_info.details.fork.child_pid = child_pid;
+  m_stop_info.details.fork.child_tid = child_pid;
+}
+
 void NativeThreadLinux::SetStoppedWithNoReason() {
   SetStopped();
 
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -249,6 +249,12 @@
   // is pending second notification.
   bool MonitorClone(lldb::pid_t child_pid,
                     llvm::Optional<CloneInfo> clone_info);
+
+  MainLoop m_subprocess_loop;
+  std::vector<std::unique_ptr<NativeProcessLinux>> m_subprocesses;
+
+  NativeProcessProtocol *GetSubprocess(lldb::pid_t pid) override;
+  void EraseSubprocess(lldb::pid_t pid) override;
 };
 
 } // 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
@@ -914,16 +914,19 @@
     LLVM_FALLTHROUGH;
   case PTRACE_EVENT_FORK:
   case PTRACE_EVENT_VFORK: {
-    MainLoop unused_loop;
-    NativeProcessLinux child_process{static_cast<::pid_t>(child_pid),
-                                     m_terminal_fd,
-                                     *m_delegates[0],
-                                     m_arch,
-                                     unused_loop,
-                                     {static_cast<::pid_t>(child_pid)}};
-    child_process.Detach();
-    ResumeThread(*parent_thread, parent_thread->GetState(),
-                 LLDB_INVALID_SIGNAL_NUMBER);
+    std::unique_ptr<NativeProcessLinux> child_process{new NativeProcessLinux(
+        static_cast<::pid_t>(child_pid), m_terminal_fd, *m_delegates[0], m_arch,
+        m_subprocess_loop, {static_cast<::pid_t>(child_pid)})};
+    child_process->m_software_breakpoints = m_software_breakpoints;
+    if ((m_enabled_extensions & Extension::fork) == Extension::fork) {
+      m_subprocesses.push_back(std::move(child_process));
+      parent_thread->SetStoppedByFork(child_pid);
+      StopRunningThreads(parent_thread->GetID());
+    } else {
+      child_process->Detach();
+      ResumeThread(*parent_thread, parent_thread->GetState(),
+                   LLDB_INVALID_SIGNAL_NUMBER);
+    }
     break;
   }
   default:
@@ -1893,3 +1896,21 @@
     return m_intel_pt_manager.GetBinaryData(request);
   return NativeProcessProtocol::TraceGetBinaryData(request);
 }
+
+NativeProcessProtocol *NativeProcessLinux::GetSubprocess(lldb::pid_t pid) {
+  for (auto& p : m_subprocesses) {
+    if (p->GetID() == pid)
+      return p.get();
+  }
+
+  return nullptr;
+}
+
+void NativeProcessLinux::EraseSubprocess(lldb::pid_t pid) {
+  for (auto it = m_subprocesses.begin(); it != m_subprocesses.end();) {
+    if ((*it)->GetID() == pid)
+      it = m_subprocesses.erase(it);
+    else
+      ++it;
+  }
+}
Index: lldb/source/API/SBThread.cpp
===================================================================
--- lldb/source/API/SBThread.cpp
+++ lldb/source/API/SBThread.cpp
@@ -195,6 +195,12 @@
 
         case eStopReasonException:
           return 1;
+
+        case eStopReasonFork:
+          return 1;
+
+        case eStopReasonVFork:
+          return 1;
         }
       }
     }
@@ -258,6 +264,12 @@
 
         case eStopReasonException:
           return stop_info_sp->GetValue();
+
+        case eStopReasonFork:
+          return stop_info_sp->GetValue();
+
+        case eStopReasonVFork:
+          return stop_info_sp->GetValue();
         }
       }
     }
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/packages/Python/lldbsuite/test/lldbutil.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/lldbutil.py
+++ lldb/packages/Python/lldbsuite/test/lldbutil.py
@@ -247,6 +247,10 @@
         return "watchpoint"
     elif enum == lldb.eStopReasonExec:
         return "exec"
+    elif enum == lldb.eStopReasonFork:
+        return "fork"
+    elif enum == lldb.eStopReasonVFork:
+        return "vfork"
     elif enum == lldb.eStopReasonSignal:
         return "signal"
     elif enum == lldb.eStopReasonException:
Index: lldb/include/lldb/lldb-enumerations.h
===================================================================
--- lldb/include/lldb/lldb-enumerations.h
+++ lldb/include/lldb/lldb-enumerations.h
@@ -249,6 +249,8 @@
   eStopReasonThreadExiting,
   eStopReasonInstrumentation,
   eStopReasonProcessorTrace,
+  eStopReasonFork,
+  eStopReasonVFork,
 };
 
 /// Command Return Status Types.
Index: lldb/include/lldb/Target/StopInfo.h
===================================================================
--- lldb/include/lldb/Target/StopInfo.h
+++ lldb/include/lldb/Target/StopInfo.h
@@ -132,6 +132,14 @@
   static lldb::StopInfoSP
   CreateStopReasonProcessorTrace(Thread &thread, const char *description);
 
+  static lldb::StopInfoSP CreateStopReasonWithFork(Thread &thread,
+                                                   lldb::pid_t child_pid,
+                                                   lldb::tid_t child_tid);
+
+  static lldb::StopInfoSP CreateStopReasonWithVFork(Thread &thread,
+                                                    lldb::pid_t child_pid,
+                                                    lldb::tid_t child_tid);
+
   static lldb::ValueObjectSP
   GetReturnValueObject(lldb::StopInfoSP &stop_info_sp);
 
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -978,6 +978,9 @@
   /// anything after a process exec's itself.
   virtual void DoDidExec() {}
 
+  /// Called after a reported fork.
+  virtual void DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) {}
+
   /// Called before launching to a process.
   ///
   /// Allow Process plug-ins to execute some code before launching a process.
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;
 
@@ -358,6 +360,44 @@
     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) {
+    m_enabled_extensions = flags;
+    return llvm::Error::success();
+  }
+
+  /// Get subprocess (i.e. forked process) corresponding to PID.
+  ///
+  /// \param[in] pid
+  ///     The PID of requested process.
+  ///
+  /// \return A reference to NativeProcessProtocol instance, nullptr
+  /// if no process with specified PID is traced.
+  virtual NativeProcessProtocol *GetSubprocess(lldb::pid_t pid) {
+    return nullptr;
+  }
+
+  /// Erase subprocess corresponding to PID.
+  ///
+  /// \param[in] pid
+  ///     The PID of requested process.
+  virtual void EraseSubprocess(lldb::pid_t pid) {}
+
 protected:
   struct SoftwareBreakpoint {
     uint32_t ref_count;
@@ -388,6 +428,9 @@
   // stopping it.
   llvm::DenseSet<int> m_signals_to_ignore;
 
+  // Extensions enabled per the last SetEnabledExtensions() call.
+  Extension m_enabled_extensions;
+
   // lldb_private::Host calls should be used to launch a process for debugging,
   // and then the process should be attached to. When attaching to a process
   // lldb_private::Host calls should be used to locate the process to attach
Index: lldb/include/lldb/Host/Debug.h
===================================================================
--- lldb/include/lldb/Host/Debug.h
+++ lldb/include/lldb/Host/Debug.h
@@ -144,6 +144,12 @@
       uint32_t data_count;
       lldb::addr_t data[8];
     } exception;
+
+    // eStopReasonFork / eStopReasonVFork
+    struct {
+      lldb::pid_t child_pid;
+      lldb::tid_t child_tid;
+    } fork;
   } details;
 };
 }
Index: lldb/include/lldb/API/SBThreadPlan.h
===================================================================
--- lldb/include/lldb/API/SBThreadPlan.h
+++ lldb/include/lldb/API/SBThreadPlan.h
@@ -58,6 +58,8 @@
   /// eStopReasonSignal        1     unix signal number
   /// eStopReasonException     N     exception data
   /// eStopReasonExec          0
+  /// eStopReasonFork          1     pid of the child process
+  /// eStopReasonVFork         1     pid of the child process
   /// eStopReasonPlanComplete  0
   uint64_t GetStopReasonDataAtIndex(uint32_t idx);
 
Index: lldb/include/lldb/API/SBThread.h
===================================================================
--- lldb/include/lldb/API/SBThread.h
+++ lldb/include/lldb/API/SBThread.h
@@ -66,6 +66,8 @@
   /// eStopReasonSignal        1     unix signal number
   /// eStopReasonException     N     exception data
   /// eStopReasonExec          0
+  /// eStopReasonFork          1     pid of the child process
+  /// eStopReasonVFork         1     pid of the child process
   /// eStopReasonPlanComplete  0
   uint64_t GetStopReasonDataAtIndex(uint32_t idx);
 
Index: lldb/examples/python/performance.py
===================================================================
--- lldb/examples/python/performance.py
+++ lldb/examples/python/performance.py
@@ -255,6 +255,12 @@
                                 select_thread = True
                                 if self.verbose:
                                     print("signal %d" % (thread.GetStopReasonDataAtIndex(0)))
+                            elif stop_reason == lldb.eStopReasonFork:
+                                if self.verbose:
+                                    print("fork pid = %d" % (thread.GetStopReasonDataAtIndex(0)))
+                            elif stop_reason == lldb.eStopReasonVFork:
+                                if self.verbose:
+                                    print("vfork pid = %d" % (thread.GetStopReasonDataAtIndex(0)))
 
                             if select_thread and not selected_thread:
                                 self.thread = thread
Index: lldb/docs/python_api_enums.rst
===================================================================
--- lldb/docs/python_api_enums.rst
+++ lldb/docs/python_api_enums.rst
@@ -342,6 +342,8 @@
 .. py:data:: eStopReasonSignal
 .. py:data:: eStopReasonException
 .. py:data:: eStopReasonExec
+.. py:data:: eStopReasonFork
+.. py:data:: eStopReasonVFork
 .. py:data:: eStopReasonPlanComplete
 .. py:data:: eStopReasonThreadExiting
 .. py:data:: eStopReasonInstrumentation
Index: lldb/bindings/interface/SBThreadPlan.i
===================================================================
--- lldb/bindings/interface/SBThreadPlan.i
+++ lldb/bindings/interface/SBThreadPlan.i
@@ -73,6 +73,8 @@
     eStopReasonSignal        1     unix signal number
     eStopReasonException     N     exception data
     eStopReasonExec          0
+    eStopReasonFork          1     pid of the child process
+    eStopReasonVFork         1     pid of the child process
     eStopReasonPlanComplete  0") GetStopReasonDataAtIndex;
     uint64_t
     GetStopReasonDataAtIndex(uint32_t idx);
Index: lldb/bindings/interface/SBThread.i
===================================================================
--- lldb/bindings/interface/SBThread.i
+++ lldb/bindings/interface/SBThread.i
@@ -104,6 +104,8 @@
     eStopReasonSignal        1     unix signal number
     eStopReasonException     N     exception data
     eStopReasonExec          0
+    eStopReasonFork          1     pid of the child process
+    eStopReasonVFork         1     pid of the child process
     eStopReasonPlanComplete  0") GetStopReasonDataAtIndex;
     uint64_t
     GetStopReasonDataAtIndex(uint32_t idx);
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to