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