xref: /llvm-project/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp (revision b9c1b51e45b845debb76d8658edabca70ca56079)
1 //===-- SingleStepCheck.cpp ----------------------------------- -*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "SingleStepCheck.h"
11 
12 #include <sched.h>
13 #include <signal.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16 
17 #include "NativeProcessLinux.h"
18 
19 #include "llvm/Support/Compiler.h"
20 
21 #include "lldb/Core/Error.h"
22 #include "lldb/Core/Log.h"
23 #include "lldb/Host/linux/Ptrace.h"
24 
25 using namespace lldb_private::process_linux;
26 
27 #if defined(__arm64__) || defined(__aarch64__)
28 namespace {
29 
30 void LLVM_ATTRIBUTE_NORETURN Child() {
31   if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1)
32     _exit(1);
33 
34   // We just do an endless loop SIGSTOPPING ourselves until killed. The tracer
35   // will fiddle with our cpu
36   // affinities and monitor the behaviour.
37   for (;;) {
38     raise(SIGSTOP);
39 
40     // Generate a bunch of instructions here, so that a single-step does not
41     // land in the
42     // raise() accidentally. If single-stepping works, we will be spinning in
43     // this loop. If
44     // it doesn't, we'll land in the raise() call 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(pid, SIGKILL);            // Kill the child.
56     waitpid(pid, &status, __WALL); // Pick up the remains.
57   }
58 };
59 
60 } // end anonymous namespace
61 
62 bool impl::SingleStepWorkaroundNeeded() {
63   // We shall spawn a child, and use it to verify the debug capabilities of the
64   // cpu. We shall
65   // iterate through the cpus, bind the child to each one in turn, and verify
66   // that
67   // single-stepping works on that cpu. A workaround is needed if we find at
68   // least one broken
69   // cpu.
70 
71   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
72   Error error;
73   ::pid_t child_pid = fork();
74   if (child_pid == -1) {
75     if (log) {
76       error.SetErrorToErrno();
77       log->Printf("%s failed to fork(): %s", __FUNCTION__, error.AsCString());
78     }
79     return false;
80   }
81   if (child_pid == 0)
82     Child();
83 
84   ChildDeleter child_deleter{child_pid};
85   cpu_set_t available_cpus;
86   if (sched_getaffinity(child_pid, sizeof available_cpus, &available_cpus) ==
87       -1) {
88     if (log) {
89       error.SetErrorToErrno();
90       log->Printf("%s failed to get available cpus: %s", __FUNCTION__,
91                   error.AsCString());
92     }
93     return false;
94   }
95 
96   int status;
97   ::pid_t wpid = waitpid(child_pid, &status, __WALL);
98   if (wpid != child_pid || !WIFSTOPPED(status)) {
99     if (log) {
100       error.SetErrorToErrno();
101       log->Printf("%s waitpid() failed (status = %x): %s", __FUNCTION__, status,
102                   error.AsCString());
103     }
104     return false;
105   }
106 
107   unsigned cpu;
108   for (cpu = 0; cpu < CPU_SETSIZE; ++cpu) {
109     if (!CPU_ISSET(cpu, &available_cpus))
110       continue;
111 
112     cpu_set_t cpus;
113     CPU_ZERO(&cpus);
114     CPU_SET(cpu, &cpus);
115     if (sched_setaffinity(child_pid, sizeof cpus, &cpus) == -1) {
116       if (log) {
117         error.SetErrorToErrno();
118         log->Printf("%s failed to switch to cpu %u: %s", __FUNCTION__, cpu,
119                     error.AsCString());
120       }
121       continue;
122     }
123 
124     int status;
125     error = NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid);
126     if (error.Fail()) {
127       if (log)
128         log->Printf("%s single step failed: %s", __FUNCTION__,
129                     error.AsCString());
130       break;
131     }
132 
133     wpid = waitpid(child_pid, &status, __WALL);
134     if (wpid != child_pid || !WIFSTOPPED(status)) {
135       if (log) {
136         error.SetErrorToErrno();
137         log->Printf("%s waitpid() failed (status = %x): %s", __FUNCTION__,
138                     status, error.AsCString());
139       }
140       break;
141     }
142     if (WSTOPSIG(status) != SIGTRAP) {
143       if (log)
144         log->Printf("%s single stepping on cpu %d failed with status %x",
145                     __FUNCTION__, cpu, status);
146       break;
147     }
148   }
149 
150   // cpu is either the index of the first broken cpu, or CPU_SETSIZE.
151   if (cpu == 0) {
152     if (log)
153       log->Printf("%s SINGLE STEPPING ON FIRST CPU IS NOT WORKING. DEBUGGING "
154                   "LIKELY TO BE UNRELIABLE.",
155                   __FUNCTION__);
156     // No point in trying to fiddle with the affinities, just give it our best
157     // shot and see how it goes.
158     return false;
159   }
160 
161   return cpu != CPU_SETSIZE;
162 }
163 
164 #else // !arm64
165 bool impl::SingleStepWorkaroundNeeded() { return false; }
166 #endif
167