Index: lldb/include/lldb/Host/common/NativeProcessProtocol.h =================================================================== --- lldb/include/lldb/Host/common/NativeProcessProtocol.h +++ lldb/include/lldb/Host/common/NativeProcessProtocol.h @@ -397,6 +397,24 @@ return llvm::make_error(); } + /// Extension flag constants, passed to SetEnabledExtension(). + struct Extension { + static constexpr uint32_t fork = 1; + static constexpr uint32_t vfork = 2; + }; + + /// 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(uint32_t flags) { + return llvm::Error::success(); + } + protected: struct SoftwareBreakpoint { uint32_t ref_count; 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/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- lldb/source/Plugins/Process/Linux/NativeProcessLinux.h +++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.h @@ -128,6 +128,8 @@ bool SupportHardwareSingleStepping() const; + llvm::Error SetEnabledExtensions(uint32_t flags) override; + protected: llvm::Expected> GetSoftwareBreakpointTrapOpcode(size_t size_hint) override; @@ -146,6 +148,8 @@ /// Inferior memory (allocated by us) and its size. llvm::DenseMap m_allocated_memory; + uint32_t m_enabled_extensions; + // Private Instance Methods NativeProcessLinux(::pid_t pid, int terminal_fd, NativeDelegate &delegate, const ArchSpec &arch, MainLoop &mainloop, @@ -154,7 +158,7 @@ // Returns a list of process threads that we have attached to. static llvm::Expected> Attach(::pid_t pid); - static Status SetDefaultPtraceOpts(const lldb::pid_t); + static Status SetPtraceOpts(const lldb::pid_t, uint32_t extensions = 0); void MonitorCallback(lldb::pid_t pid, bool exited, WaitStatus status); Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -247,7 +247,7 @@ LLDB_LOG(log, "pid = {0:x}, detected architecture {1}", pid, Info.GetArchitecture().GetArchitectureName()); - status = SetDefaultPtraceOpts(pid); + status = SetPtraceOpts(pid); if (status.Fail()) { LLDB_LOG(log, "failed to set default ptrace options: {0}", status); return status.ToError(); @@ -286,7 +286,8 @@ NativeDelegate &delegate, const ArchSpec &arch, MainLoop &mainloop, llvm::ArrayRef<::pid_t> tids) - : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch) { + : NativeProcessELF(pid, terminal_fd, delegate), m_arch(arch), + m_enabled_extensions(0) { if (m_terminal_fd != -1) { Status status = EnsureFDFlags(m_terminal_fd, O_NONBLOCK); assert(status.Success()); @@ -351,7 +352,7 @@ std::error_code(errno, std::generic_category())); } - if ((status = SetDefaultPtraceOpts(tid)).Fail()) + if ((status = SetPtraceOpts(tid)).Fail()) return status.ToError(); LLDB_LOG(log, "adding tid = {0}", tid); @@ -375,7 +376,8 @@ return std::move(tids); } -Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) { +Status NativeProcessLinux::SetPtraceOpts(lldb::pid_t pid, + uint32_t extensions) { long ptrace_opts = 0; // Have the child raise an event on exit. This is used to keep the child in @@ -383,14 +385,17 @@ ptrace_opts |= PTRACE_O_TRACEEXIT; // Have the tracer trace threads which spawn in the inferior process. - // TODO: if we want to support tracing the inferiors' child, add the - // appropriate ptrace flags here (PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK) ptrace_opts |= PTRACE_O_TRACECLONE; // Have the tracer notify us before execve returns (needed to disable legacy // SIGTRAP generation) ptrace_opts |= PTRACE_O_TRACEEXEC; + // Have the tracer trace forked children if the fork-events extension + // is supported. + if (extensions & Extension::fork) + ptrace_opts |= PTRACE_O_TRACEFORK; + return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts); } @@ -2021,3 +2026,16 @@ return error; } + +llvm::Error NativeProcessLinux::SetEnabledExtensions(uint32_t flags) { + m_enabled_extensions = flags; + for (const auto &thread : m_threads) { + assert(thread && "thread list should not contain NULL threads"); + + Status status = SetPtraceOpts(thread->GetID(), flags); + if (status.Fail()) + return status.ToError(); + } + + return llvm::Error::success(); +} 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 @@ -559,6 +559,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 + 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); 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( @@ -849,6 +850,19 @@ response.PutCString(";qXfer:libraries-svr4:read+"); #endif response.PutCString(";multiprocess+"); + response.PutCString(";fork-events+"); + response.PutCString(";vfork-events+"); + + // Parse client-indicated features. + llvm::StringRef view = packet.GetStringRef(); + llvm::SmallVector client_features; + view.split(client_features, ';'); + 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 SendPacketNoLock(response.GetString()); } 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; @@ -255,6 +257,9 @@ bool allow_any = false, 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 @@ -184,6 +184,9 @@ RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_QPassSignals, &GDBRemoteCommunicationServerLLGS::Handle_QPassSignals); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_qSupported, + &GDBRemoteCommunicationServerLLGS::Handle_qSupported_LLGS); RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_jTraceStart, @@ -254,6 +257,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 @@ -321,6 +326,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(); @@ -3703,3 +3709,28 @@ return tid; } + +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerLLGS::Handle_qSupported_LLGS( + StringExtractorGDBRemote &packet) { + auto ret = Handle_qSupported(packet); + if (m_debugged_process_up) + SetEnabledExtensions(*m_debugged_process_up); + return ret; +} + +void GDBRemoteCommunicationServerLLGS::SetEnabledExtensions( + NativeProcessProtocol &process) { + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PROCESS)); + + uint32_t flags = 0; + 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}"); +}