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; @@ -228,6 +230,15 @@ virtual Status GetFileLoadAddress(const llvm::StringRef &file_name, lldb::addr_t &load_addr) = 0; + /// Extension flag constants, returned by Factory::GetSupportedExtensions() + /// and passed to SetEnabledExtension() + enum class Extension { + fork = (1u << 0), + vfork = (1u << 1), + + LLVM_MARK_AS_BITMASK_ENUM(vfork) + }; + class Factory { public: virtual ~Factory(); @@ -274,6 +285,12 @@ virtual llvm::Expected> Attach(lldb::pid_t pid, NativeDelegate &native_delegate, MainLoop &mainloop) const = 0; + + /// Get the bitmask of extensions supported by this process plugin. + /// + /// \return + /// A NativeProcessProtocol::Extension bitmask. + virtual Extension GetSupportedExtensions() const { return {}; } }; /// Start tracing a process or its threads. @@ -328,6 +345,15 @@ return llvm::make_error(); } + /// Method called in order to propagate the bitmap of protocol + /// extensions supported by the client. + /// + /// \param[in] flags + /// The bitmap of enabled extensions. + virtual void SetEnabledExtensions(Extension flags) { + m_enabled_extensions = flags; + } + protected: struct SoftwareBreakpoint { uint32_t ref_count; @@ -357,6 +383,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/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 @@ -854,6 +854,8 @@ "qEcho", "QPassSignals", "multiprocess", + "fork-events", + "vfork-events", ] def parse_qSupported_response(self, context): 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 @@ -103,6 +103,8 @@ bool m_thread_suffix_supported = false; bool m_list_threads_in_stop_reply = false; + NativeProcessProtocol::Extension m_extensions_supported = {}; + PacketResult SendONotification(const char *buffer, uint32_t len); PacketResult SendWResponse(NativeProcessProtocol *process); @@ -264,6 +266,9 @@ llvm::Expected ReadTid(StringExtractorGDBRemote &packet, bool allow_all, lldb::pid_t default_pid); + // 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 @@ -259,6 +259,8 @@ m_continue_process = m_current_process = m_debugged_process_up.get(); } + SetEnabledExtensions(*m_current_process); + // 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 @@ -327,6 +329,7 @@ } m_debugged_process_up = std::move(*process_or); m_continue_process = m_current_process = m_debugged_process_up.get(); + SetEnabledExtensions(*m_current_process); // Setup stdout/stderr mapping from inferior. auto terminal_fd = m_debugged_process_up->GetTerminalFileDescriptor(); @@ -3557,7 +3560,7 @@ std::vector GDBRemoteCommunicationServerLLGS::HandleFeatures( const llvm::ArrayRef client_features) { - auto ret = + std::vector ret = GDBRemoteCommunicationServerCommon::HandleFeatures(client_features); ret.insert(ret.end(), { "QThreadSuffixSupported+", "QListThreadsInStopReply+", @@ -3566,5 +3569,36 @@ "QPassSignals+", "qXfer:auxv:read+", "qXfer:libraries-svr4:read+", #endif }); + + // check for client features + using Extension = NativeProcessProtocol::Extension; + m_extensions_supported = {}; + for (llvm::StringRef x : client_features) + m_extensions_supported |= llvm::StringSwitch(x) + .Case("fork-events+", Extension::fork) + .Case("vfork-events+", Extension::vfork) + .Default({}); + m_extensions_supported &= m_process_factory.GetSupportedExtensions(); + + // report only if actually supported + if (bool(m_extensions_supported & NativeProcessProtocol::Extension::fork)) + ret.push_back("fork-events+"); + if (bool(m_extensions_supported & NativeProcessProtocol::Extension::vfork)) + ret.push_back("vfork-events+"); + + if (m_debugged_process_up) + SetEnabledExtensions(*m_debugged_process_up); return ret; } + +void GDBRemoteCommunicationServerLLGS::SetEnabledExtensions( + NativeProcessProtocol &process) { + NativeProcessProtocol::Extension flags = {}; + if (bool(m_extensions_supported & NativeProcessProtocol::Extension::fork)) + flags |= NativeProcessProtocol::Extension::fork; + if (bool(m_extensions_supported & NativeProcessProtocol::Extension::vfork)) + flags |= NativeProcessProtocol::Extension::vfork; + + assert(!bool(flags & ~m_process_factory.GetSupportedExtensions())); + process.SetEnabledExtensions(flags); +}