wallace created this revision.
wallace added reviewers: clayborg, jingham.
wallace requested review of this revision.
Herald added a project: LLDB.
Herald added a subscriber: lldb-commits.
On Linux, unlike Darwin, after a process performs exec, the thread id doesn't
change. This causes any living thread plans to fail because the original thread
is now invalid. This crash only happens if thread plans are still executing
when the original thread dies.
I've been testing this on my Linux machine and it happens mostly when I attach
to a process that then execs. If LLDB launches that process, this problem
almost never happens. As with most race conditions, it's hard to predict and
reproduce. In any case, I added a test with an attach scenario that works
correctly on my machine.
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D93874
Files:
lldb/include/lldb/Target/Process.h
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
lldb/source/Target/Process.cpp
lldb/test/API/functionalities/exec/TestExec.py
lldb/test/API/functionalities/exec/main.cpp
Index: lldb/test/API/functionalities/exec/main.cpp
===================================================================
--- lldb/test/API/functionalities/exec/main.cpp
+++ lldb/test/API/functionalities/exec/main.cpp
@@ -1,5 +1,6 @@
#define _POSIX_C_SOURCE 200809L
+#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
@@ -8,6 +9,9 @@
#include <unistd.h>
int main(int argc, char const **argv) {
+ if (getenv("LLDB_TEST_SHOULD_ATTACH") != nullptr)
+ raise(SIGSTOP);
+
char *buf = strdup(argv[0]); // Set breakpoint 1 here
std::string directory_name(::dirname(buf));
Index: lldb/test/API/functionalities/exec/TestExec.py
===================================================================
--- lldb/test/API/functionalities/exec/TestExec.py
+++ lldb/test/API/functionalities/exec/TestExec.py
@@ -24,7 +24,8 @@
@skipIfAsan # rdar://problem/43756823
@skipIfWindows
def test_hitting_exec (self):
- self.do_test(False)
+ self.do_test(False, False)
+ self.do_test(False, True)
@expectedFailureAll(archs=['i386'],
oslist=no_match(["freebsd"]),
@@ -34,9 +35,10 @@
@skipIfAsan # rdar://problem/43756823
@skipIfWindows
def test_skipping_exec (self):
- self.do_test(True)
+ self.do_test(True, False)
+ self.do_test(True, True)
- def do_test(self, skip_exec):
+ def do_test(self, skip_exec, should_attach):
self.build()
exe = self.getBuildArtifact("a.out")
secondprog = self.getBuildArtifact("secondprog")
@@ -52,10 +54,31 @@
'Set breakpoint 2 here', lldb.SBFileSpec("secondprog.cpp", False))
self.assertTrue(breakpoint2, VALID_BREAKPOINT)
- # Launch the process
- process = target.LaunchSimple(
- None, None, self.get_process_working_directory())
- self.assertTrue(process, PROCESS_IS_VALID)
+ if not should_attach:
+ # Launch the process
+ process = target.LaunchSimple(
+ None, None, self.get_process_working_directory())
+ self.assertTrue(process, PROCESS_IS_VALID)
+ else:
+ # Spawn the process and attach to it
+ popen = subprocess.Popen(exe, env={"LLDB_TEST_SHOULD_ATTACH": "1"})
+ if self.TraceOn():
+ print("Will attach to PID" + str(popen.pid))
+
+ error = lldb.SBError()
+ attach_info = lldb.SBAttachInfo(popen.pid)
+ process = target.Attach(attach_info, error)
+ self.assertTrue(error.Success() and process)
+
+ # In some systems lldb will stop for a second time at the SIGSTOP attach
+ # point. That might be need to be fixed in a different patch.
+ # To avoid making guesses, we continue until we hit breakpoint1.
+ while True:
+ error = process.Continue()
+ self.assertTrue(error.Success())
+ threads = lldbutil.get_threads_stopped_at_breakpoint(process, breakpoint1)
+ if len(threads) > 0:
+ break
if self.TraceOn():
self.runCmd("settings show target.process.stop-on-exec", check=False)
Index: lldb/source/Target/Process.cpp
===================================================================
--- lldb/source/Target/Process.cpp
+++ lldb/source/Target/Process.cpp
@@ -1248,6 +1248,12 @@
return false;
}
+void Process::ProcessDidExec() {
+ m_thread_list_real.Clear();
+ m_thread_list.Clear();
+ m_thread_plans.Clear();
+}
+
void Process::UpdateThreadListIfNeeded() {
const uint32_t stop_id = GetStopID();
if (m_thread_list.GetSize(false) == 0 ||
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
@@ -2611,8 +2611,7 @@
Log *log(ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS));
LLDB_LOGF(log, "ProcessGDBRemote::SetLastStopPacket () - detected exec");
- m_thread_list_real.Clear();
- m_thread_list.Clear();
+ ProcessDidExec();
BuildDynamicRegisterInfo(true);
m_gdb_comm.ResetDiscoverableSettings(did_exec);
}
Index: lldb/include/lldb/Target/Process.h
===================================================================
--- lldb/include/lldb/Target/Process.h
+++ lldb/include/lldb/Target/Process.h
@@ -2075,6 +2075,10 @@
void UpdateThreadListIfNeeded();
+ /// Method to be invoked to signal that the underlying process has exec'ed
+ /// and some clean up should be done to prepare for the new process state.
+ void ProcessDidExec();
+
ThreadList &GetThreadList() { return m_thread_list; }
// When ExtendedBacktraces are requested, the HistoryThreads that are created
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits