[Lldb-commits] [lldb] 0a1d80d - [lldb] [gdb-remote client] Support minimal fork/vfork handling
Michał Górny via lldb-commits
lldb-commits at lists.llvm.org
Mon Aug 30 01:44:18 PDT 2021
Author: Michał Górny
Date: 2021-08-30T10:24:38+02:00
New Revision: 0a1d80d56e6a0e062d493e262d3322d2ac3a04d4
URL: https://github.com/llvm/llvm-project/commit/0a1d80d56e6a0e062d493e262d3322d2ac3a04d4
DIFF: https://github.com/llvm/llvm-project/commit/0a1d80d56e6a0e062d493e262d3322d2ac3a04d4.diff
LOG: [lldb] [gdb-remote client] Support minimal fork/vfork handling
Add a support for handling fork/vfork stops in LLGS client. At this
point, it only sends a detach packet for the newly forked child
(and implicitly resumes the parent).
Differential Revision: https://reviews.llvm.org/D100206
Added:
lldb/test/API/functionalities/gdb_remote_client/TestFork.py
Modified:
lldb/include/lldb/Target/Process.h
lldb/include/lldb/Target/StopInfo.h
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
lldb/source/Target/StopInfo.cpp
lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py
Removed:
################################################################################
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index 8dcc15b1667b8..b1f1d908c3581 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -981,6 +981,15 @@ class Process : public std::enable_shared_from_this<Process>,
/// 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 after a reported vfork.
+ virtual void DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) {}
+
+ /// Called after reported vfork completion.
+ virtual void DidVForkDone() {}
+
/// Called before launching to a process.
///
/// Allow Process plug-ins to execute some code before launching a process.
diff --git a/lldb/include/lldb/Target/StopInfo.h b/lldb/include/lldb/Target/StopInfo.h
index 0e81e51608464..cdb906dcd7ede 100644
--- a/lldb/include/lldb/Target/StopInfo.h
+++ b/lldb/include/lldb/Target/StopInfo.h
@@ -132,6 +132,16 @@ class StopInfo {
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::StopInfoSP CreateStopReasonVForkDone(Thread &thread);
+
static lldb::ValueObjectSP
GetReturnValueObject(lldb::StopInfoSP &stop_info_sp);
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index e898a08641dc0..d3b9bc78ea102 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -318,7 +318,8 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
// 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) {
@@ -1457,9 +1458,12 @@ bool GDBRemoteCommunicationClient::DeallocateMemory(addr_t addr) {
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];
@@ -1481,17 +1485,25 @@ Status GDBRemoteCommunicationClient::Detach(bool keep_stopped) {
error.SetErrorString("Stays stopped not supported by this target.");
return error;
} else {
- StringExtractorGDBRemote response;
- PacketResult packet_result = SendPacketAndWaitForResponse("D1", response);
- if (packet_result != PacketResult::Success)
- error.SetErrorString("Sending extended disconnect packet failed.");
+ packet.PutChar('1');
}
- } else {
- StringExtractorGDBRemote response;
- PacketResult packet_result = SendPacketAndWaitForResponse("D", response);
- 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);
+ if (packet_result != PacketResult::Success)
+ error.SetErrorString("Sending isconnect packet failed.");
return error;
}
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 1e1797c10dfcf..06d4f4d0df087 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -235,7 +235,7 @@ class GDBRemoteCommunicationClient : public GDBRemoteClientBase {
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);
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index a2f0fd929fcec..f135f8975cec7 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1906,6 +1906,28 @@ ThreadSP ProcessGDBRemote::SetThreadStopInfo(
} 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 (reason == "vforkdone") {
+ thread_sp->SetStopInfo(
+ StopInfo::CreateStopReasonVForkDone(*thread_sp));
+ handled = true;
}
} else if (!signo) {
addr_t pc = thread_sp->GetRegisterContext()->GetPC();
@@ -2312,6 +2334,21 @@ StateType ProcessGDBRemote::SetThreadStopInfo(StringExtractor &stop_packet) {
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))
@@ -5447,3 +5484,29 @@ CommandObject *ProcessGDBRemote::GetPluginCommandObject() {
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() : "<unknown error>");
+ return;
+ }
+}
+
+void ProcessGDBRemote::DidVFork(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() : "<unknown error>");
+ return;
+ }
+}
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index fe04cdddd0f5b..e86f697439500 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -230,6 +230,9 @@ class ProcessGDBRemote : public Process,
std::string HarmonizeThreadIdsForProfileData(
StringExtractorGDBRemote &inputStringExtractor);
+ void DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) override;
+ void DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) override;
+
protected:
friend class ThreadGDBRemote;
friend class GDBRemoteCommunicationClient;
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index aeb97f1919eb4..8258705990eb0 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -1154,6 +1154,103 @@ class StopInfoExec : public StopInfo {
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_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;
+};
+
+// StopInfoVFork
+
+class StopInfoVFork : public StopInfo {
+public:
+ StopInfoVFork(Thread &thread, lldb::pid_t child_pid, lldb::tid_t child_tid)
+ : StopInfo(thread, child_pid), m_performed_action(false),
+ 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"; }
+
+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()->DidVFork(m_child_pid, m_child_tid);
+ }
+
+ bool m_performed_action;
+
+private:
+ lldb::pid_t m_child_pid;
+ lldb::tid_t m_child_tid;
+};
+
+// StopInfoVForkDone
+
+class StopInfoVForkDone : public StopInfo {
+public:
+ StopInfoVForkDone(Thread &thread)
+ : StopInfo(thread, 0), m_performed_action(false) {}
+
+ ~StopInfoVForkDone() override = default;
+
+ bool ShouldStop(Event *event_ptr) override { return false; }
+
+ StopReason GetStopReason() const override { return eStopReasonVForkDone; }
+
+ const char *GetDescription() override { return "vforkdone"; }
+
+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()->DidVForkDone();
+ }
+
+ bool m_performed_action;
+};
+
} // namespace lldb_private
StopInfoSP StopInfo::CreateStopReasonWithBreakpointSiteID(Thread &thread,
@@ -1203,6 +1300,23 @@ StopInfoSP StopInfo::CreateStopReasonWithExec(Thread &thread) {
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));
+}
+
+StopInfoSP StopInfo::CreateStopReasonVForkDone(Thread &thread) {
+ return StopInfoSP(new StopInfoVForkDone(thread));
+}
+
ValueObjectSP StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp) {
if (stop_info_sp &&
stop_info_sp->GetStopReason() == eStopReasonPlanComplete) {
diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestFork.py b/lldb/test/API/functionalities/gdb_remote_client/TestFork.py
new file mode 100644
index 0000000000000..73bf829e85e09
--- /dev/null
+++ b/lldb/test/API/functionalities/gdb_remote_client/TestFork.py
@@ -0,0 +1,52 @@
+from __future__ import print_function
+import lldb
+import unittest
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from gdbclientutils import *
+
+
+class TestMultiprocess(GDBRemoteTestBase):
+ def base_test(self, variant):
+ class MyResponder(MockGDBServerResponder):
+ def __init__(self):
+ super().__init__()
+ self.first = True
+ self.detached = None
+ self.property = "{}-events+".format(variant)
+
+ def qSupported(self, client_supported):
+ assert "multiprocess+" in client_supported
+ assert self.property in client_supported
+ return "{};multiprocess+;{}".format(
+ super().qSupported(client_supported), self.property)
+
+ def qfThreadInfo(self):
+ return "mp400.10200"
+
+ def cont(self):
+ if self.first:
+ self.first = False
+ return ("T0fthread:p400.10200;reason:{0};{0}:p401.10400;"
+ .format(variant))
+ return "W00"
+
+ def D(self, packet):
+ self.detached = packet
+ return "OK"
+
+ self.server.responder = MyResponder()
+ target = self.dbg.CreateTarget('')
+ if self.TraceOn():
+ self.runCmd("log enable gdb-remote packets")
+ self.addTearDownHook(
+ lambda: self.runCmd("log disable gdb-remote packets"))
+ process = self.connect(target)
+ process.Continue()
+ self.assertRegex(self.server.responder.detached, r"D;0*401")
+
+ def test_fork(self):
+ self.base_test("fork")
+
+ def test_vfork(self):
+ self.base_test("vfork")
diff --git a/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py b/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py
index 29c1f126c2d2c..5bbee0b33de81 100644
--- a/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py
+++ b/lldb/test/API/functionalities/gdb_remote_client/gdbclientutils.py
@@ -109,6 +109,8 @@ def respond(self, packet):
return self.vCont(packet)
if packet[0] == "A":
return self.A(packet)
+ if packet[0] == "D":
+ return self.D(packet)
if packet[0] == "g":
return self.readRegisters()
if packet[0] == "G":
@@ -216,6 +218,9 @@ def vCont(self, packet):
def A(self, packet):
return ""
+ def D(self, packet):
+ return "OK"
+
def readRegisters(self):
return "00000000" * self.registerCount
More information about the lldb-commits
mailing list