mgorny updated this revision to Diff 360049.
mgorny added a comment.
Rebased. Switched to watchpoint iterator API.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D100503/new/
https://reviews.llvm.org/D100503
Files:
lldb/include/lldb/Target/Process.h
lldb/include/lldb/lldb-private-enumerations.h
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
lldb/source/Target/Process.cpp
lldb/source/Target/TargetProperties.td
lldb/test/Shell/Subprocess/clone-follow-child-softbp.test
lldb/test/Shell/Subprocess/clone-follow-child-wp.test
lldb/test/Shell/Subprocess/clone-follow-child.test
lldb/test/Shell/Subprocess/fork-follow-child-softbp.test
lldb/test/Shell/Subprocess/fork-follow-child-wp.test
lldb/test/Shell/Subprocess/fork-follow-child.test
lldb/test/Shell/Subprocess/fork-follow-parent-softbp.test
lldb/test/Shell/Subprocess/vfork-follow-child-softbp.test
lldb/test/Shell/Subprocess/vfork-follow-child-wp.test
lldb/test/Shell/Subprocess/vfork-follow-child.test
Index: lldb/test/Shell/Subprocess/vfork-follow-child.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/vfork-follow-child.test
@@ -0,0 +1,9 @@
+# REQUIRES: native
+# UNSUPPORTED: system-windows
+# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_FORK=vfork -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+b parent_func
+process launch
+# CHECK: function run in parent
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/vfork-follow-child-wp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/vfork-follow-child-wp.test
@@ -0,0 +1,11 @@
+# REQUIRES: native && dbregs-set
+# UNSUPPORTED: system-windows
+# UNSUPPORTED: system-darwin
+# RUN: %clangxx_host -g %p/Inputs/fork.cpp -DTEST_FORK=vfork -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+process launch -s
+watchpoint set variable -w write g_val
+# CHECK: Watchpoint created:
+continue
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/vfork-follow-child-softbp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/vfork-follow-child-softbp.test
@@ -0,0 +1,10 @@
+# REQUIRES: native
+# UNSUPPORTED: system-windows
+# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_FORK=vfork -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+b child_func
+b parent_func
+process launch
+# CHECK: function run in parent
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/fork-follow-parent-softbp.test
===================================================================
--- lldb/test/Shell/Subprocess/fork-follow-parent-softbp.test
+++ lldb/test/Shell/Subprocess/fork-follow-parent-softbp.test
@@ -7,6 +7,7 @@
process launch
# CHECK-NOT: function run in parent
# CHECK: stop reason = breakpoint
+# CHECK-NEXT: parent_func
continue
# CHECK: function run in parent
# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/fork-follow-child.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/fork-follow-child.test
@@ -0,0 +1,9 @@
+# REQUIRES: native
+# UNSUPPORTED: system-windows
+# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_FORK=fork -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+b parent_func
+process launch
+# CHECK: function run in parent
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/fork-follow-child-wp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/fork-follow-child-wp.test
@@ -0,0 +1,14 @@
+# REQUIRES: native && dbregs-set
+# UNSUPPORTED: system-windows
+# RUN: %clangxx_host -g %p/Inputs/fork.cpp -DTEST_FORK=fork -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+process launch -s
+watchpoint set variable -w write g_val
+# CHECK: Watchpoint created:
+continue
+# CHECK: stop reason = watchpoint
+continue
+# CHECK: stop reason = watchpoint
+continue
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/fork-follow-child-softbp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/fork-follow-child-softbp.test
@@ -0,0 +1,12 @@
+# REQUIRES: native
+# UNSUPPORTED: system-windows
+# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_FORK=fork -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+b child_func
+b parent_func
+process launch
+# CHECK: stop reason = breakpoint
+# CHECK-NEXT: child_func
+continue
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/clone-follow-child.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/clone-follow-child.test
@@ -0,0 +1,10 @@
+# REQUIRES: native && system-linux
+# clone() tests fails on arm64 Linux, PR #49899
+# UNSUPPORTED: system-linux && target-aarch64
+# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_CLONE -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+b parent_func
+process launch
+# CHECK: function run in parent
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/clone-follow-child-wp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/clone-follow-child-wp.test
@@ -0,0 +1,15 @@
+# REQUIRES: native && system-linux && dbregs-set
+# clone() tests fails on arm64 Linux, PR #49899
+# UNSUPPORTED: system-linux && target-aarch64
+# RUN: %clangxx_host -g %p/Inputs/fork.cpp -DTEST_CLONE -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+process launch -s
+watchpoint set variable -w write g_val
+# CHECK: Watchpoint created:
+continue
+# CHECK: stop reason = watchpoint
+continue
+# CHECK: stop reason = watchpoint
+continue
+# CHECK: child exited: 0
Index: lldb/test/Shell/Subprocess/clone-follow-child-softbp.test
===================================================================
--- /dev/null
+++ lldb/test/Shell/Subprocess/clone-follow-child-softbp.test
@@ -0,0 +1,13 @@
+# REQUIRES: native && system-linux
+# clone() tests fails on arm64 Linux, PR #49899
+# UNSUPPORTED: system-linux && target-aarch64
+# RUN: %clangxx_host %p/Inputs/fork.cpp -DTEST_CLONE -o %t
+# RUN: %lldb -b -s %s %t | FileCheck %s
+settings set target.process.follow-fork-mode child
+b child_func
+b parent_func
+process launch
+# CHECK: stop reason = breakpoint
+# CHECK-NEXT: child_func
+continue
+# CHECK: child exited: 0
Index: lldb/source/Target/TargetProperties.td
===================================================================
--- lldb/source/Target/TargetProperties.td
+++ lldb/source/Target/TargetProperties.td
@@ -239,6 +239,10 @@
def VirtualAddressableBits: Property<"virtual-addressable-bits", "UInt64">,
DefaultUnsignedValue<0>,
Desc<"The number of bits used for addressing. If the value is 39, then bits 0..38 are used for addressing. The default value of 0 means unspecified.">;
+ def FollowForkMode: Property<"follow-fork-mode", "Enum">,
+ DefaultEnumValue<"eFollowParent">,
+ EnumValues<"OptionEnumValues(g_follow_fork_mode_values)">,
+ Desc<"Debugger's behavior upon fork or vfork.">;
}
let Definition = "platform" in {
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -110,6 +110,19 @@
}
};
+static constexpr OptionEnumValueElement g_follow_fork_mode_values[] = {
+ {
+ eFollowParent,
+ "parent",
+ "Continue tracing the parent process and detach the child.",
+ },
+ {
+ eFollowChild,
+ "child",
+ "Trace the child process and detach the parent.",
+ },
+};
+
#define LLDB_PROPERTIES_process
#include "TargetProperties.inc"
@@ -334,6 +347,12 @@
nullptr, ePropertyOSPluginReportsAllThreads, does_report);
}
+FollowForkMode ProcessProperties::GetFollowForkMode() const {
+ const uint32_t idx = ePropertyFollowForkMode;
+ return (FollowForkMode)m_collection_sp->GetPropertyAtIndexAsEnumeration(
+ nullptr, idx, g_process_properties[idx].default_uint_value);
+}
+
ProcessSP Process::FindPlugin(lldb::TargetSP target_sp,
llvm::StringRef plugin_name,
ListenerSP listener_sp,
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
@@ -233,6 +233,7 @@
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;
void DidVForkDone() override;
+ void DidExec() override;
protected:
friend class ThreadGDBRemote;
@@ -465,6 +466,7 @@
// fork helpers
void DidForkSwitchSoftwareBreakpoints(bool enable);
+ void DidForkSwitchHardwareTraps(bool enable);
};
} // namespace process_gdb_remote
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
@@ -5490,6 +5490,31 @@
});
}
+void ProcessGDBRemote::DidForkSwitchHardwareTraps(bool enable) {
+ if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointHardware)) {
+ GetBreakpointSiteList().ForEach([this, enable](BreakpointSite *bp_site) {
+ if (bp_site->IsEnabled() &&
+ bp_site->GetType() == BreakpointSite::eHardware) {
+ m_gdb_comm.SendGDBStoppointTypePacket(
+ eBreakpointHardware, enable, bp_site->GetLoadAddress(),
+ bp_site->GetTrapOpcodeMaxByteSize(), GetInterruptTimeout());
+ }
+ });
+ }
+
+ WatchpointList &wps = GetTarget().GetWatchpointList();
+ size_t wp_count = wps.GetSize();
+ for (size_t i = 0; i < wp_count; ++i) {
+ WatchpointSP wp = wps.GetByIndex(i);
+ if (wp->IsEnabled()) {
+ GDBStoppointType type = GetGDBStoppointType(wp.get());
+ m_gdb_comm.SendGDBStoppointTypePacket(type, enable, wp->GetLoadAddress(),
+ wp->GetByteSize(),
+ GetInterruptTimeout());
+ }
+ }
+}
+
void ProcessGDBRemote::DidFork(lldb::pid_t child_pid, lldb::tid_t child_tid) {
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
@@ -5498,30 +5523,58 @@
// anyway.
lldb::tid_t parent_tid = m_thread_ids.front();
- if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware)) {
- // Switch to the new process to clear breakpoints there.
- if (!m_gdb_comm.SetCurrentThread(child_tid, child_pid)) {
- LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to set pid/tid");
- return;
- }
+ lldb::pid_t follow_pid, detach_pid;
+ lldb::tid_t follow_tid, detach_tid;
+
+ switch (GetFollowForkMode()) {
+ case eFollowParent:
+ follow_pid = parent_pid;
+ follow_tid = parent_tid;
+ detach_pid = child_pid;
+ detach_tid = child_tid;
+ break;
+ case eFollowChild:
+ follow_pid = child_pid;
+ follow_tid = child_tid;
+ detach_pid = parent_pid;
+ detach_tid = parent_tid;
+ break;
+ }
- // Disable all software breakpoints in the forked process.
+ // Switch to the process that is going to be detached.
+ if (!m_gdb_comm.SetCurrentThread(detach_tid, detach_pid)) {
+ LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to set pid/tid");
+ return;
+ }
+
+ // Disable all software breakpoints in the forked process.
+ if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware))
DidForkSwitchSoftwareBreakpoints(false);
- // Reset gdb-remote to the original process.
- if (!m_gdb_comm.SetCurrentThread(parent_tid, parent_pid)) {
- LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to reset pid/tid");
- return;
- }
+ // Remove hardware breakpoints / watchpoints from parent process if we're
+ // following child.
+ if (GetFollowForkMode() == eFollowChild)
+ DidForkSwitchHardwareTraps(false);
+
+ // Switch to the process that is going to be followed
+ if (!m_gdb_comm.SetCurrentThread(follow_tid, follow_pid) ||
+ !m_gdb_comm.SetCurrentThreadForRun(follow_tid, follow_pid)) {
+ LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to reset pid/tid");
+ return;
}
- LLDB_LOG(log, "Detaching forked child {0}", child_pid);
- Status error = m_gdb_comm.Detach(false, child_pid);
+ LLDB_LOG(log, "Detaching process {0}", detach_pid);
+ Status error = m_gdb_comm.Detach(false, detach_pid);
if (error.Fail()) {
LLDB_LOG(log, "ProcessGDBRemote::DidFork() detach packet send failed: {0}",
error.AsCString() ? error.AsCString() : "<unknown error>");
return;
}
+
+ // Hardware breakpoints/watchpoints are not inherited implicitly,
+ // so we need to readd them if we're following child.
+ if (GetFollowForkMode() == eFollowChild)
+ DidForkSwitchHardwareTraps(true);
}
void ProcessGDBRemote::DidVFork(lldb::pid_t child_pid, lldb::tid_t child_tid) {
@@ -5534,8 +5587,40 @@
if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware))
DidForkSwitchSoftwareBreakpoints(false);
- LLDB_LOG(log, "Detaching forked child {0}", child_pid);
- Status error = m_gdb_comm.Detach(false, child_pid);
+ lldb::pid_t detach_pid;
+ lldb::tid_t detach_tid;
+
+ switch (GetFollowForkMode()) {
+ case eFollowParent:
+ detach_pid = child_pid;
+ detach_tid = child_tid;
+ break;
+ case eFollowChild:
+ detach_pid = m_gdb_comm.GetCurrentProcessID();
+ // Any valid TID will suffice, thread-relevant actions will set a proper TID
+ // anyway.
+ detach_tid = m_thread_ids.front();
+
+ // Switch to the parent process before detaching it.
+ if (!m_gdb_comm.SetCurrentThread(detach_tid, detach_pid)) {
+ LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to set pid/tid");
+ return;
+ }
+
+ // Remove hardware breakpoints / watchpoints from the parent process.
+ DidForkSwitchHardwareTraps(false);
+
+ // Switch to the child process.
+ if (!m_gdb_comm.SetCurrentThread(child_tid, child_pid) ||
+ !m_gdb_comm.SetCurrentThreadForRun(child_tid, child_pid)) {
+ LLDB_LOG(log, "ProcessGDBRemote::DidFork() unable to reset pid/tid");
+ return;
+ }
+ break;
+ }
+
+ LLDB_LOG(log, "Detaching process {0}", detach_pid);
+ Status error = m_gdb_comm.Detach(false, detach_pid);
if (error.Fail()) {
LLDB_LOG(log,
"ProcessGDBRemote::DidFork() detach packet send failed: {0}",
@@ -5552,3 +5637,11 @@
if (m_gdb_comm.SupportsGDBStoppointPacket(eBreakpointSoftware))
DidForkSwitchSoftwareBreakpoints(true);
}
+
+void ProcessGDBRemote::DidExec() {
+ // If we are following children, vfork is finished by exec (rather than
+ // vforkdone that is submitted for parent).
+ if (GetFollowForkMode() == eFollowChild)
+ m_vfork_in_progress = false;
+ Process::DidExec();
+}
Index: lldb/include/lldb/lldb-private-enumerations.h
===================================================================
--- lldb/include/lldb/lldb-private-enumerations.h
+++ lldb/include/lldb/lldb-private-enumerations.h
@@ -172,6 +172,12 @@
eMemoryModuleLoadLevelComplete, // Load sections and all symbols
};
+// Behavior on fork/vfork
+enum FollowForkMode {
+ eFollowParent, // Follow parent process
+ eFollowChild, // Follow child process
+};
+
// Result enums for when reading multiple lines from IOHandlers
enum class LineStatus {
Success, // The line that was just edited if good and should be added to the
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -99,6 +99,7 @@
bool GetOSPluginReportsAllThreads() const;
void SetOSPluginReportsAllThreads(bool does_report);
bool GetSteppingRunsAllThreads() const;
+ FollowForkMode GetFollowForkMode() const;
protected:
Process *m_process; // Can be nullptr for global ProcessProperties
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits