1 //===-- SingleStepCheck.cpp -----------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "SingleStepCheck.h"
10 
11 #include <csignal>
12 #include <sched.h>
13 #include <sys/wait.h>
14 #include <unistd.h>
15 
16 #include "NativeProcessLinux.h"
17 
18 #include "llvm/Support/Compiler.h"
19 #include "llvm/Support/Errno.h"
20 
21 #include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
22 #include "lldb/Host/linux/Ptrace.h"
23 #include "lldb/Utility/Status.h"
24 
25 using namespace lldb;
26 using namespace lldb_private;
27 using namespace lldb_private::process_linux;
28 
29 #if defined(__arm64__) || defined(__aarch64__)
30 namespace {
31 
32 [[noreturn]] void Child() {
33   if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1)
34     _exit(1);
35 
36   // We just do an endless loop SIGSTOPPING ourselves until killed. The tracer
37   // will fiddle with our cpu affinities and monitor the behaviour.
38   for (;;) {
39     raise(SIGSTOP);
40 
41     // Generate a bunch of instructions here, so that a single-step does not
42     // land in the raise() accidentally. If single-stepping works, we will be
43     // spinning in this loop. If it doesn't, we'll land in the raise() call
44     // above.
45     for (volatile unsigned i = 0; i < CPU_SETSIZE; ++i)
46       ;
47   }
48 }
49 
50 struct ChildDeleter {
51   ::pid_t pid;
52 
53   ~ChildDeleter() {
54     int status;
55     // Kill the child.
56     kill(pid, SIGKILL);
57     // Pick up the remains.
58     llvm::sys::RetryAfterSignal(-1, waitpid, pid, &status, __WALL);
59   }
60 };
61 
62 bool WorkaroundNeeded() {
63   // We shall spawn a child, and use it to verify the debug capabilities of the
64   // cpu. We shall iterate through the cpus, bind the child to each one in
65   // turn, and verify that single-stepping works on that cpu. A workaround is
66   // needed if we find at least one broken cpu.
67 
68   Log *log = GetLog(POSIXLog::Thread);
69   ::pid_t child_pid = fork();
70   if (child_pid == -1) {
71     LLDB_LOG(log, "failed to fork(): {0}", Status(errno, eErrorTypePOSIX));
72     return false;
73   }
74   if (child_pid == 0)
75     Child();
76 
77   ChildDeleter child_deleter{child_pid};
78   cpu_set_t available_cpus;
79   if (sched_getaffinity(child_pid, sizeof available_cpus, &available_cpus) ==
80       -1) {
81     LLDB_LOG(log, "failed to get available cpus: {0}",
82              Status(errno, eErrorTypePOSIX));
83     return false;
84   }
85 
86   int status;
87   ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, waitpid,
88       child_pid, &status, __WALL);
89   if (wpid != child_pid || !WIFSTOPPED(status)) {
90     LLDB_LOG(log, "waitpid() failed (status = {0:x}): {1}", status,
91              Status(errno, eErrorTypePOSIX));
92     return false;
93   }
94 
95   unsigned cpu;
96   for (cpu = 0; cpu < CPU_SETSIZE; ++cpu) {
97     if (!CPU_ISSET(cpu, &available_cpus))
98       continue;
99 
100     cpu_set_t cpus;
101     CPU_ZERO(&cpus);
102     CPU_SET(cpu, &cpus);
103     if (sched_setaffinity(child_pid, sizeof cpus, &cpus) == -1) {
104       LLDB_LOG(log, "failed to switch to cpu {0}: {1}", cpu,
105                Status(errno, eErrorTypePOSIX));
106       continue;
107     }
108 
109     int status;
110     Status error =
111         NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid);
112     if (error.Fail()) {
113       LLDB_LOG(log, "single step failed: {0}", error);
114       break;
115     }
116 
117     wpid = llvm::sys::RetryAfterSignal(-1, waitpid,
118         child_pid, &status, __WALL);
119     if (wpid != child_pid || !WIFSTOPPED(status)) {
120       LLDB_LOG(log, "waitpid() failed (status = {0:x}): {1}", status,
121                Status(errno, eErrorTypePOSIX));
122       break;
123     }
124     if (WSTOPSIG(status) != SIGTRAP) {
125       LLDB_LOG(log, "single stepping on cpu {0} failed with status {1:x}", cpu,
126                status);
127       break;
128     }
129   }
130 
131   // cpu is either the index of the first broken cpu, or CPU_SETSIZE.
132   if (cpu == 0) {
133     LLDB_LOG(log,
134              "SINGLE STEPPING ON FIRST CPU IS NOT WORKING. DEBUGGING "
135              "LIKELY TO BE UNRELIABLE.");
136     // No point in trying to fiddle with the affinities, just give it our best
137     // shot and see how it goes.
138     return false;
139   }
140 
141   return cpu != CPU_SETSIZE;
142 }
143 
144 } // end anonymous namespace
145 
146 std::unique_ptr<SingleStepWorkaround> SingleStepWorkaround::Get(::pid_t tid) {
147   Log *log = GetLog(POSIXLog::Thread);
148 
149   static bool workaround_needed = WorkaroundNeeded();
150   if (!workaround_needed) {
151     LLDB_LOG(log, "workaround for thread {0} not needed", tid);
152     return nullptr;
153   }
154 
155   cpu_set_t original_set;
156   if (sched_getaffinity(tid, sizeof original_set, &original_set) != 0) {
157     // This should really not fail. But, just in case...
158     LLDB_LOG(log, "Unable to get cpu affinity for thread {0}: {1}", tid,
159              Status(errno, eErrorTypePOSIX));
160     return nullptr;
161   }
162 
163   cpu_set_t set;
164   CPU_ZERO(&set);
165   CPU_SET(0, &set);
166   if (sched_setaffinity(tid, sizeof set, &set) != 0) {
167     // This may fail in very locked down systems, if the thread is not allowed
168     // to run on cpu 0. If that happens, only thing we can do is it log it and
169     // continue...
170     LLDB_LOG(log, "Unable to set cpu affinity for thread {0}: {1}", tid,
171              Status(errno, eErrorTypePOSIX));
172   }
173 
174   LLDB_LOG(log, "workaround for thread {0} prepared", tid);
175   return std::make_unique<SingleStepWorkaround>(tid, original_set);
176 }
177 
178 SingleStepWorkaround::~SingleStepWorkaround() {
179   Log *log = GetLog(POSIXLog::Thread);
180   LLDB_LOG(log, "Removing workaround");
181   if (sched_setaffinity(m_tid, sizeof m_original_set, &m_original_set) != 0) {
182     LLDB_LOG(log, "Unable to reset cpu affinity for thread {0}: {1}", m_tid,
183              Status(errno, eErrorTypePOSIX));
184   }
185 }
186 #endif
187