Index: lldb/bindings/interface/SBThread.i =================================================================== --- lldb/bindings/interface/SBThread.i +++ lldb/bindings/interface/SBThread.i @@ -104,6 +104,9 @@ 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 + eStopReasonVForkDone 0 eStopReasonPlanComplete 0") GetStopReasonDataAtIndex; uint64_t GetStopReasonDataAtIndex(uint32_t idx); Index: lldb/bindings/interface/SBThreadPlan.i =================================================================== --- lldb/bindings/interface/SBThreadPlan.i +++ lldb/bindings/interface/SBThreadPlan.i @@ -73,6 +73,9 @@ 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 + eStopReasonVForkDone 0 eStopReasonPlanComplete 0") GetStopReasonDataAtIndex; uint64_t GetStopReasonDataAtIndex(uint32_t idx); Index: lldb/docs/python_api_enums.rst =================================================================== --- lldb/docs/python_api_enums.rst +++ lldb/docs/python_api_enums.rst @@ -342,6 +342,9 @@ .. py:data:: eStopReasonSignal .. py:data:: eStopReasonException .. py:data:: eStopReasonExec +.. py:data:: eStopReasonFork +.. py:data:: eStopReasonVFork +.. py:data:: eStopReasonVForkDone .. py:data:: eStopReasonPlanComplete .. py:data:: eStopReasonThreadExiting .. py:data:: eStopReasonInstrumentation Index: lldb/examples/python/performance.py =================================================================== --- lldb/examples/python/performance.py +++ lldb/examples/python/performance.py @@ -255,6 +255,15 @@ 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))) + elif stop_reason == lldb.eStopReasonVForkDone: + if self.verbose: + print("vfork done") if select_thread and not selected_thread: self.thread = thread Index: lldb/include/lldb/API/SBThread.h =================================================================== --- lldb/include/lldb/API/SBThread.h +++ lldb/include/lldb/API/SBThread.h @@ -66,6 +66,9 @@ /// 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 + /// eStopReasonVForkDone 0 /// eStopReasonPlanComplete 0 uint64_t GetStopReasonDataAtIndex(uint32_t idx); Index: lldb/include/lldb/API/SBThreadPlan.h =================================================================== --- lldb/include/lldb/API/SBThreadPlan.h +++ lldb/include/lldb/API/SBThreadPlan.h @@ -58,6 +58,9 @@ /// 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 + /// eStopReasonVForkDone 0 /// eStopReasonPlanComplete 0 uint64_t GetStopReasonDataAtIndex(uint32_t idx); 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/Target/Process.h =================================================================== --- lldb/include/lldb/Target/Process.h +++ lldb/include/lldb/Target/Process.h @@ -980,6 +980,15 @@ /// 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 after a reported vfork. + virtual void DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) {} + + /// Called after reported vfork completion. + virtual void DidVForkDone() {} + /// Called before launching to a process. /// /// Allow Process plug-ins to execute some code before launching a process. Index: lldb/include/lldb/Target/StopInfo.h =================================================================== --- lldb/include/lldb/Target/StopInfo.h +++ lldb/include/lldb/Target/StopInfo.h @@ -132,6 +132,16 @@ static lldb::StopInfoSP CreateStopReasonProcessorTrace(Thread &thread, const char *description); + static lldb::StopInfoSP CreateStopReasonFork(Thread &thread, + lldb::pid_t child_pid, + lldb::tid_t child_tid); + + static lldb::StopInfoSP CreateStopReasonVFork(Thread &thread, + lldb::pid_t child_pid, + lldb::tid_t child_tid); + + static lldb::StopInfoSP CreateStopReasonVForkDone(Thread &thread); + static lldb::ValueObjectSP GetReturnValueObject(lldb::StopInfoSP &stop_info_sp); Index: lldb/include/lldb/lldb-enumerations.h =================================================================== --- lldb/include/lldb/lldb-enumerations.h +++ lldb/include/lldb/lldb-enumerations.h @@ -249,6 +249,9 @@ eStopReasonThreadExiting, eStopReasonInstrumentation, eStopReasonProcessorTrace, + eStopReasonFork, + eStopReasonVFork, + eStopReasonVForkDone, }; /// Command Return Status Types. 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,12 @@ return "watchpoint" elif enum == lldb.eStopReasonExec: return "exec" + elif enum == lldb.eStopReasonFork: + return "fork" + elif enum == lldb.eStopReasonVFork: + return "vfork" + elif enum == lldb.eStopReasonVForkDone: + return "vforkdone" elif enum == lldb.eStopReasonSignal: return "signal" elif enum == lldb.eStopReasonException: Index: lldb/source/API/SBThread.cpp =================================================================== --- lldb/source/API/SBThread.cpp +++ lldb/source/API/SBThread.cpp @@ -173,6 +173,7 @@ case eStopReasonThreadExiting: case eStopReasonInstrumentation: case eStopReasonProcessorTrace: + case eStopReasonVForkDone: // There is no data for these stop reasons. return 0; @@ -195,6 +196,12 @@ case eStopReasonException: return 1; + + case eStopReasonFork: + return 1; + + case eStopReasonVFork: + return 1; } } } @@ -225,6 +232,7 @@ case eStopReasonThreadExiting: case eStopReasonInstrumentation: case eStopReasonProcessorTrace: + case eStopReasonVForkDone: // There is no data for these stop reasons. return 0; @@ -258,6 +266,12 @@ case eStopReasonException: return stop_info_sp->GetValue(); + + case eStopReasonFork: + return stop_info_sp->GetValue(); + + case eStopReasonVFork: + return stop_info_sp->GetValue(); } } } 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 @@ -650,6 +650,12 @@ return "exec"; case eStopReasonProcessorTrace: return "processor trace"; + case eStopReasonFork: + return "fork"; + case eStopReasonVFork: + return "vfork"; + case eStopReasonVForkDone: + return "vforkdone"; case eStopReasonInstrumentation: case eStopReasonInvalid: case eStopReasonPlanComplete: @@ -926,6 +932,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()); } 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,28 @@ } 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::CreateStopReasonFork( + *thread_sp, child_pid, child_tid)); + handled = true; + } 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::CreateStopReasonVFork( + *thread_sp, child_pid, child_tid)); + handled = true; + } else if (reason == "vforkdone") { + thread_sp->SetStopInfo( + StopInfo::CreateStopReasonVForkDone(*thread_sp)); + handled = true; } } else if (!signo) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); @@ -2307,6 +2329,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)) Index: lldb/source/Target/Process.cpp =================================================================== --- lldb/source/Target/Process.cpp +++ lldb/source/Target/Process.cpp @@ -812,6 +812,9 @@ case eStopReasonWatchpoint: case eStopReasonException: case eStopReasonExec: + case eStopReasonFork: + case eStopReasonVFork: + case eStopReasonVForkDone: case eStopReasonThreadExiting: case eStopReasonInstrumentation: case eStopReasonProcessorTrace: Index: lldb/source/Target/StackFrameList.cpp =================================================================== --- lldb/source/Target/StackFrameList.cpp +++ lldb/source/Target/StackFrameList.cpp @@ -131,6 +131,9 @@ case eStopReasonWatchpoint: case eStopReasonException: case eStopReasonExec: + case eStopReasonFork: + case eStopReasonVFork: + case eStopReasonVForkDone: case eStopReasonSignal: // In all these cases we want to stop in the deepest frame. m_current_inlined_pc = curr_pc; Index: lldb/source/Target/StopInfo.cpp =================================================================== --- lldb/source/Target/StopInfo.cpp +++ lldb/source/Target/StopInfo.cpp @@ -1145,6 +1145,103 @@ 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, child_pid), 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; +}; + +// StopInfoVFork + +class StopInfoVFork : public StopInfo { +public: + StopInfoVFork(Thread &thread, lldb::pid_t child_pid, lldb::tid_t child_tid) + : StopInfo(thread, child_pid), m_performed_action(false), + m_child_pid(child_pid), m_child_tid(child_tid) {} + + ~StopInfoVFork() override = default; + + bool ShouldStop(Event *event_ptr) override { return false; } + + StopReason GetStopReason() const override { return eStopReasonVFork; } + + const char *GetDescription() override { return "vfork"; } + +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()->DidVFork(m_child_pid, m_child_tid); + } + + bool m_performed_action; + +private: + lldb::pid_t m_child_pid; + lldb::tid_t m_child_tid; +}; + +// StopInfoVForkDone + +class StopInfoVForkDone : public StopInfo { +public: + StopInfoVForkDone(Thread &thread) + : StopInfo(thread, 0), m_performed_action(false) {} + + ~StopInfoVForkDone() override = default; + + bool ShouldStop(Event *event_ptr) override { return false; } + + StopReason GetStopReason() const override { return eStopReasonVForkDone; } + + const char *GetDescription() override { return "vforkdone"; } + +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()->DidVForkDone(); + } + + bool m_performed_action; +}; + } // namespace lldb_private StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread, @@ -1194,6 +1291,23 @@ return StopInfoSP(new StopInfoExec(thread)); } +StopInfoSP StopInfo::CreateStopReasonFork(Thread &thread, + lldb::pid_t child_pid, + lldb::tid_t child_tid) { + return StopInfoSP(new StopInfoFork(thread, child_pid, child_tid)); +} + + +StopInfoSP StopInfo::CreateStopReasonVFork(Thread &thread, + lldb::pid_t child_pid, + lldb::tid_t child_tid) { + return StopInfoSP(new StopInfoVFork(thread, child_pid, child_tid)); +} + +StopInfoSP StopInfo::CreateStopReasonVForkDone(Thread &thread) { + return StopInfoSP(new StopInfoVForkDone(thread)); +} + ValueObjectSP StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp) { if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonPlanComplete) { Index: lldb/source/Target/Thread.cpp =================================================================== --- lldb/source/Target/Thread.cpp +++ lldb/source/Target/Thread.cpp @@ -1679,6 +1679,12 @@ return "exception"; case eStopReasonExec: return "exec"; + case eStopReasonFork: + return "fork"; + case eStopReasonVFork: + return "vfork"; + case eStopReasonVForkDone: + return "vfork done"; case eStopReasonPlanComplete: return "plan complete"; case eStopReasonThreadExiting: Index: lldb/tools/lldb-vscode/JSONUtils.cpp =================================================================== --- lldb/tools/lldb-vscode/JSONUtils.cpp +++ lldb/tools/lldb-vscode/JSONUtils.cpp @@ -877,6 +877,15 @@ 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::eStopReasonVForkDone: + body.try_emplace("reason", "vforkdone"); + break; case lldb::eStopReasonThreadExiting: case lldb::eStopReasonInvalid: case lldb::eStopReasonNone: Index: lldb/tools/lldb-vscode/LLDBUtils.cpp =================================================================== --- lldb/tools/lldb-vscode/LLDBUtils.cpp +++ lldb/tools/lldb-vscode/LLDBUtils.cpp @@ -56,6 +56,9 @@ case lldb::eStopReasonException: case lldb::eStopReasonExec: case lldb::eStopReasonProcessorTrace: + case lldb::eStopReasonFork: + case lldb::eStopReasonVFork: + case lldb::eStopReasonVForkDone: return true; case lldb::eStopReasonThreadExiting: case lldb::eStopReasonInvalid: