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/Host/common/NativeProcessProtocol.h =================================================================== --- lldb/include/lldb/Host/common/NativeProcessProtocol.h +++ lldb/include/lldb/Host/common/NativeProcessProtocol.h @@ -30,6 +30,8 @@ #include namespace lldb_private { +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + class MemoryRegionInfo; class ResumeActionList; @@ -358,6 +360,44 @@ return llvm::make_error(); } + /// 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 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/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/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/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/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/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/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 clone_info); + + MainLoop m_subprocess_loop; + std::vector> 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 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/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/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); @@ -556,6 +556,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 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 + @@ -1454,9 +1472,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 +1499,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; } 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 + HandleFeatures(const llvm::ArrayRef 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 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 +GDBRemoteCommunicationServerCommon::HandleFeatures( + const llvm::ArrayRef 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 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/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 + HandleFeatures(const llvm::ArrayRef client_features) override; + private: llvm::Expected> BuildTargetXml(); @@ -253,6 +258,9 @@ llvm::Expected 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()); } @@ -3210,19 +3225,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 +3557,29 @@ return tid; } + +llvm::SmallVector +GDBRemoteCommunicationServerLLGS::HandleFeatures( + const llvm::ArrayRef 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/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,16 @@ 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)); + + 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() : ""); + return; + } +} 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,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/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/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/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 @@ -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: