LLDB mainline
SingleStepCheck.cpp
Go to the documentation of this file.
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
23#include "lldb/Utility/Status.h"
24
25using namespace lldb;
26using namespace lldb_private;
27using namespace lldb_private::process_linux;
28
29#if defined(__arm64__) || defined(__aarch64__)
30namespace {
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
50struct 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
62bool 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
146std::unique_ptr<SingleStepWorkaround> SingleStepWorkaround::Get(::pid_t tid) {
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
178SingleStepWorkaround::~SingleStepWorkaround() {
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
static llvm::raw_ostream & error(Stream &strm)
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition: Log.h:359
An error handling class.
Definition: Status.h:44
static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr=nullptr, void *data=nullptr, size_t data_size=0, long *result=nullptr)
}
static std::unique_ptr< SingleStepWorkaround > Get(::pid_t tid)
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition: Log.h:331
Definition: SBAddress.h:15
@ eErrorTypePOSIX
POSIX error codes.
uint64_t pid_t
Definition: lldb-types.h:83
#define SIGSTOP
#define SIGTRAP
pid_t fork(void)
#define SIGKILL