mgorny updated this revision to Diff 331609.
mgorny added a comment.
Add a helper function to get PID (TGID) from TID. Recognize fork notifications
from parent (SIGTRAP) and from child (SIGSTOP with a TGID != `GetID()`) and
call a helper for them. The helper doesn't do anything except for logging right
now.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D98822/new/
https://reviews.llvm.org/D98822
Files:
lldb/include/lldb/Host/common/NativeProcessProtocol.h
lldb/include/lldb/Host/linux/Host.h
lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
lldb/source/Host/linux/Host.cpp
lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
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}");
+}
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<llvm::StringRef, 4> 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/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<std::string> 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/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<llvm::ArrayRef<uint8_t>>
GetSoftwareBreakpointTrapOpcode(size_t size_hint) override;
@@ -146,6 +148,8 @@
/// Inferior memory (allocated by us) and its size.
llvm::DenseMap<lldb::addr_t, lldb::addr_t> 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<std::vector<::pid_t>> 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);
@@ -171,6 +175,11 @@
void MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread,
bool exited);
+ // Handle a fork-like event. parent indicates whether we're getting
+ // the signal from the parent (true) or from the child (false).
+ // In case of parent, event gives the PTRACE_EVENT_* code.
+ void MonitorFork(lldb::pid_t child_pid, bool parent, uint32_t event);
+
bool HasThreadNoLock(lldb::tid_t thread_id);
bool StopTrackingThread(lldb::tid_t thread_id);
Index: lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
===================================================================
--- lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -30,6 +30,7 @@
#include "lldb/Host/PseudoTerminal.h"
#include "lldb/Host/ThreadLauncher.h"
#include "lldb/Host/common/NativeRegisterContext.h"
+#include "lldb/Host/linux/Host.h"
#include "lldb/Host/linux/Ptrace.h"
#include "lldb/Host/linux/Uio.h"
#include "lldb/Host/posix/ProcessLauncherPosixFork.h"
@@ -247,7 +248,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 +287,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 +353,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 +377,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 +386,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);
}
@@ -444,6 +450,14 @@
LLDB_LOG(log, "tid {0}, si_code: {1}, si_pid: {2}", pid, info.si_code,
info.si_pid);
+ llvm::Optional<lldb::pid_t> tgid = getPIDForTID(pid);
+ if (tgid && tgid.getValue() != GetID()) {
+ LLDB_LOG(log, "tid {0} belongs to a different tgid {1}, assuming child",
+ pid, tgid.getValue());
+ MonitorFork(pid, false, 0);
+ return;
+ }
+
NativeThreadLinux &thread = AddThread(pid);
// Resume the newly created thread.
@@ -649,6 +663,14 @@
break;
}
+ case (SIGTRAP | (PTRACE_EVENT_FORK << 8)): {
+ unsigned long data = 0;
+ if (GetEventMessage(thread.GetID(), &data).Fail())
+ data = 0;
+
+ MonitorFork(data, true, PTRACE_EVENT_FORK);
+ break;
+ }
case 0:
case TRAP_TRACE: // We receive this on single stepping.
case TRAP_HWBKPT: // We receive this on watchpoint hit
@@ -858,6 +880,13 @@
StopRunningThreads(thread.GetID());
}
+void NativeProcessLinux::MonitorFork(lldb::pid_t child_pid, bool parent,
+ uint32_t event) {
+ Log *log(ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_PROCESS));
+ LLDB_LOG(log, "fork, child_pid={0}, parent={1}, event={2}", child_pid, parent,
+ event);
+}
+
bool NativeProcessLinux::SupportHardwareSingleStepping() const {
if (m_arch.GetMachine() == llvm::Triple::arm || m_arch.IsMIPS())
return false;
@@ -2021,3 +2050,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/Host/linux/Host.cpp
===================================================================
--- lldb/source/Host/linux/Host.cpp
+++ lldb/source/Host/linux/Host.cpp
@@ -53,7 +53,8 @@
}
static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,
- ProcessState &State, ::pid_t &TracerPid) {
+ ProcessState &State, ::pid_t &TracerPid,
+ ::pid_t &Tgid) {
Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
auto BufferOrError = getProcFile(Pid, "status");
@@ -107,6 +108,9 @@
} else if (Line.consume_front("TracerPid:")) {
Line = Line.ltrim();
Line.consumeInteger(10, TracerPid);
+ } else if (Line.consume_front("Tgid:")) {
+ Line = Line.ltrim();
+ Line.consumeInteger(10, Tgid);
}
}
return true;
@@ -204,6 +208,7 @@
static bool GetProcessAndStatInfo(::pid_t pid,
ProcessInstanceInfo &process_info,
ProcessState &State, ::pid_t &tracerpid) {
+ ::pid_t tgid;
tracerpid = 0;
process_info.Clear();
@@ -214,7 +219,7 @@
GetProcessEnviron(pid, process_info);
// Get User and Group IDs and get tracer pid.
- if (!GetStatusInfo(pid, process_info, State, tracerpid))
+ if (!GetStatusInfo(pid, process_info, State, tracerpid, tgid))
return false;
return true;
@@ -308,3 +313,15 @@
Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
return Status("unimplemented");
}
+
+namespace lldb_private {
+llvm::Optional<::pid_t> getPIDForTID(::pid_t tid) {
+ ::pid_t tracerpid, tgid = LLDB_INVALID_PROCESS_ID;
+ ProcessInstanceInfo process_info;
+ ProcessState state;
+
+ if (!GetStatusInfo(tid, process_info, state, tracerpid, tgid) || tgid == 0)
+ return llvm::None;
+ return tgid;
+}
+}
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/include/lldb/Host/linux/Host.h
===================================================================
--- /dev/null
+++ lldb/include/lldb/Host/linux/Host.h
@@ -0,0 +1,21 @@
+//===-- Host.h --------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_HOST_LINUX_HOST_H
+#define LLDB_HOST_LINUX_HOST_H
+
+#include "llvm/ADT/Optional.h"
+
+namespace lldb_private {
+
+// Get PID (i.e. the primary thread ID) corresponding to the specified TID.
+llvm::Optional<lldb::pid_t> getPIDForTID(::pid_t tid);
+
+} // namespace lldb_private
+
+#endif // #ifndef LLDB_HOST_LINUX_HOST_H
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<UnimplementedError>();
}
+ /// 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;
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits