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); 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/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/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/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/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/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/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 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::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,8 @@ eStopReasonThreadExiting, eStopReasonInstrumentation, eStopReasonProcessorTrace, + eStopReasonFork, + eStopReasonVFork, }; /// 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,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/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/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,10 @@ return "exec"; case eStopReasonProcessorTrace: return "processor trace"; + case eStopReasonFork: + return "fork"; + case eStopReasonVFork: + return "vfork"; case eStopReasonInstrumentation: case eStopReasonInvalid: case eStopReasonPlanComplete: @@ -926,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()); } 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,24 @@ } 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 (!signo) { addr_t pc = thread_sp->GetRegisterContext()->GetPC(); @@ -2307,6 +2325,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 @@ -800,6 +800,8 @@ case eStopReasonWatchpoint: case eStopReasonException: case eStopReasonExec: + case eStopReasonFork: + case eStopReasonVFork: 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,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/StopInfo.cpp =================================================================== --- lldb/source/Target/StopInfo.cpp +++ lldb/source/Target/StopInfo.cpp @@ -1145,6 +1145,48 @@ 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_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"; } + +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_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"; } + +private: + lldb::pid_t m_child_pid; + lldb::tid_t m_child_tid; +}; + } // namespace lldb_private StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread, @@ -1194,6 +1236,19 @@ 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)); +} + 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,10 @@ return "exception"; case eStopReasonExec: return "exec"; + case eStopReasonFork: + return "fork"; + case eStopReasonVFork: + return "vfork"; 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,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/tools/lldb-vscode/LLDBUtils.cpp =================================================================== --- lldb/tools/lldb-vscode/LLDBUtils.cpp +++ lldb/tools/lldb-vscode/LLDBUtils.cpp @@ -56,6 +56,8 @@ case lldb::eStopReasonException: case lldb::eStopReasonExec: case lldb::eStopReasonProcessorTrace: + case lldb::eStopReasonFork: + case lldb::eStopReasonVFork: return true; case lldb::eStopReasonThreadExiting: case lldb::eStopReasonInvalid: