Index: lldb/packages/Python/lldbsuite/test/dotest.py
===================================================================
--- lldb/packages/Python/lldbsuite/test/dotest.py
+++ lldb/packages/Python/lldbsuite/test/dotest.py
@@ -863,7 +863,7 @@
     from lldbsuite.test import lldbplatformutil
 
     platform = lldbplatformutil.getPlatform()
-    if platform not in ["linux"]:
+    if platform not in ["freebsd", "linux"]:
         configuration.skip_categories.append("fork")
 
 
Index: lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
===================================================================
--- lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
+++ lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
@@ -39,6 +39,8 @@
     llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
     Attach(lldb::pid_t pid, NativeDelegate &native_delegate,
            MainLoop &mainloop) const override;
+
+    Extension GetSupportedExtensions() const override;
   };
 
   // NativeProcessProtocol Interface
@@ -96,6 +98,7 @@
 private:
   MainLoop::SignalHandleUP m_sigchld_handle;
   ArchSpec m_arch;
+  MainLoop& m_main_loop;
   LazyBool m_supports_mem_region = eLazyBoolCalculate;
   std::vector<std::pair<MemoryRegionInfo, FileSpec>> m_mem_region_cache;
 
@@ -113,7 +116,8 @@
   void MonitorSIGSTOP(lldb::pid_t pid);
   void MonitorSIGTRAP(lldb::pid_t pid);
   void MonitorSignal(lldb::pid_t pid, int signal);
-  void MonitorClone(::pid_t child_pid);
+  void MonitorClone(::pid_t child_pid, bool is_vfork,
+                    NativeThreadFreeBSD &parent_thread);
 
   Status PopulateMemoryRegionCache();
   void SigchldHandler();
Index: lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
===================================================================
--- lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
+++ lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
@@ -128,13 +128,19 @@
   return std::move(process_up);
 }
 
+NativeProcessFreeBSD::Extension
+NativeProcessFreeBSD::Factory::GetSupportedExtensions() const {
+  return Extension::multiprocess | Extension::fork | Extension::vfork;
+}
+
 // Public Instance Methods
 
 NativeProcessFreeBSD::NativeProcessFreeBSD(::pid_t pid, int terminal_fd,
                                            NativeDelegate &delegate,
                                            const ArchSpec &arch,
                                            MainLoop &mainloop)
-    : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch) {
+    : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch),
+      m_main_loop(mainloop) {
   if (m_terminal_fd != -1) {
     Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK);
     assert(status.Success());
@@ -247,19 +253,6 @@
     return;
   }
 
-  if (info.pl_flags & PL_FLAG_FORKED) {
-    MonitorClone(info.pl_child_pid);
-    return;
-  }
-
-  if (info.pl_flags & PL_FLAG_VFORK_DONE) {
-    Status error =
-        PtraceWrapper(PT_CONTINUE, pid, reinterpret_cast<void *>(1), 0);
-    if (error.Fail())
-      SetState(StateType::eStateInvalid);
-    return;
-  }
-
   if (info.pl_lwpid > 0) {
     for (const auto &t : m_threads) {
       if (t->GetID() == static_cast<lldb::tid_t>(info.pl_lwpid))
@@ -271,6 +264,26 @@
                info.pl_lwpid);
   }
 
+  if (info.pl_flags & PL_FLAG_FORKED) {
+    assert(thread);
+    MonitorClone(info.pl_child_pid, info.pl_flags & PL_FLAG_VFORKED, *thread);
+    return;
+  }
+
+  if (info.pl_flags & PL_FLAG_VFORK_DONE) {
+    assert(thread);
+    if ((m_enabled_extensions & Extension::vfork) == Extension::vfork) {
+      thread->SetStoppedByVForkDone();
+      SetState(StateType::eStateStopped, true);
+    } else {
+      Status error =
+          PtraceWrapper(PT_CONTINUE, pid, reinterpret_cast<void *>(1), 0);
+      if (error.Fail())
+        SetState(StateType::eStateInvalid);
+    }
+    return;
+  }
+
   if (info.pl_flags & PL_FLAG_SI) {
     assert(info.pl_siginfo.si_signo == SIGTRAP);
     LLDB_LOG(log, "SIGTRAP siginfo: si_code = {0}, pid = {1}",
@@ -933,7 +946,8 @@
   return !m_arch.IsMIPS();
 }
 
-void NativeProcessFreeBSD::MonitorClone(::pid_t child_pid) {
+void NativeProcessFreeBSD::MonitorClone(::pid_t child_pid, bool is_vfork,
+                                        NativeThreadFreeBSD &parent_thread) {
   Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
   LLDB_LOG(log, "fork, child_pid={0}", child_pid);
 
@@ -955,16 +969,42 @@
     return;
   }
 
-  MainLoop unused_loop;
-  NativeProcessFreeBSD child_process{static_cast<::pid_t>(child_pid),
-                                     m_terminal_fd, m_delegate, m_arch,
-                                     unused_loop};
-  child_process.Detach();
-  Status pt_error =
-      PtraceWrapper(PT_CONTINUE, GetID(), reinterpret_cast<void *>(1), 0);
-  if (pt_error.Fail()) {
-    LLDB_LOG_ERROR(log, pt_error.ToError(),
-                   "unable to resume parent process {1}: {0}", GetID());
-    SetState(StateType::eStateInvalid);
+  struct ptrace_lwpinfo info;
+  const auto siginfo_err = PtraceWrapper(PT_LWPINFO, child_pid, &info, sizeof(info));
+  if (siginfo_err.Fail()) {
+    LLDB_LOG(log, "PT_LWPINFO failed {0}", siginfo_err);
+    return;
+  }
+  assert(info.pl_event == PL_EVENT_SIGNAL);
+  lldb::tid_t child_tid = info.pl_lwpid;
+
+  std::unique_ptr<NativeProcessFreeBSD> child_process{
+      new NativeProcessFreeBSD(static_cast<::pid_t>(child_pid), m_terminal_fd,
+                               m_delegate, m_arch, m_main_loop)};
+  if (!is_vfork)
+    child_process->m_software_breakpoints = m_software_breakpoints;
+
+  Extension expected_ext = is_vfork ? Extension::vfork : Extension::fork;
+  if ((m_enabled_extensions & expected_ext) == expected_ext) {
+    child_process->SetupTrace();
+    for (const auto &thread : child_process->m_threads)
+      static_cast<NativeThreadFreeBSD &>(*thread).SetStoppedBySignal(SIGSTOP);
+    child_process->SetState(StateType::eStateStopped, false);
+
+    m_delegate.NewSubprocess(this, std::move(child_process));
+    if (is_vfork)
+      parent_thread.SetStoppedByVFork(child_pid, child_tid);
+    else
+      parent_thread.SetStoppedByFork(child_pid, child_tid);
+    SetState(StateType::eStateStopped, true);
+  } else {
+    child_process->Detach();
+    Status pt_error =
+        PtraceWrapper(PT_CONTINUE, GetID(), reinterpret_cast<void *>(1), 0);
+    if (pt_error.Fail()) {
+      LLDB_LOG_ERROR(log, pt_error.ToError(),
+                     "unable to resume parent process {1}: {0}", GetID());
+      SetState(StateType::eStateInvalid);
+    }
   }
 }
Index: lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.h
===================================================================
--- lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.h
+++ lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.h
@@ -59,6 +59,9 @@
   void SetStoppedByTrace();
   void SetStoppedByExec();
   void SetStoppedByWatchpoint(uint32_t wp_index);
+  void SetStoppedByFork(lldb::pid_t child_pid, lldb::tid_t child_tid);
+  void SetStoppedByVFork(lldb::pid_t child_pid, lldb::tid_t child_tid);
+  void SetStoppedByVForkDone();
   void SetStoppedWithNoReason();
   void SetStopped();
   void SetRunning();
Index: lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.cpp
===================================================================
--- lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.cpp
+++ lldb/source/Plugins/Process/FreeBSD/NativeThreadFreeBSD.cpp
@@ -130,6 +130,30 @@
   m_stop_info.details.signal.signo = SIGTRAP;
 }
 
+void NativeThreadFreeBSD::SetStoppedByFork(lldb::pid_t child_pid,
+                                           lldb::tid_t child_tid) {
+  SetStopped();
+
+  m_stop_info.reason = StopReason::eStopReasonFork;
+  m_stop_info.details.fork.child_pid = child_pid;
+  m_stop_info.details.fork.child_tid = child_tid;
+}
+
+void NativeThreadFreeBSD::SetStoppedByVFork(lldb::pid_t child_pid,
+                                            lldb::tid_t child_tid) {
+  SetStopped();
+
+  m_stop_info.reason = StopReason::eStopReasonVFork;
+  m_stop_info.details.fork.child_pid = child_pid;
+  m_stop_info.details.fork.child_tid = child_tid;
+}
+
+void NativeThreadFreeBSD::SetStoppedByVForkDone() {
+  SetStopped();
+
+  m_stop_info.reason = StopReason::eStopReasonVForkDone;
+}
+
 void NativeThreadFreeBSD::SetStoppedWithNoReason() {
   SetStopped();