xref: /llvm-project/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp (revision 605b51b84e622ce64ee77b066599c7b16d9d2c9d)
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 
31 void LLVM_ATTRIBUTE_NORETURN
32 Child()
33 {
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 will fiddle with our cpu
38     // affinities and monitor the behaviour.
39     for (;;)
40     {
41         raise(SIGSTOP);
42 
43         // Generate a bunch of instructions here, so that a single-step does not land in the
44         // raise() accidentally. If single-stepping works, we will be spinning in this loop. If
45         // it doesn't, we'll land in the raise() call above.
46         for (volatile unsigned i = 0; i < CPU_SETSIZE; ++i)
47             ;
48     }
49 }
50 
51 struct ChildDeleter
52 {
53     ::pid_t pid;
54 
55     ~ChildDeleter()
56     {
57         int status;
58         kill(pid, SIGKILL);            // Kill the child.
59         waitpid(pid, &status, __WALL); // Pick up the remains.
60     }
61 };
62 
63 } // end anonymous namespace
64 
65 bool
66 impl::SingleStepWorkaroundNeeded()
67 {
68     // We shall spawn a child, and use it to verify the debug capabilities of the cpu. We shall
69     // iterate through the cpus, bind the child to each one in turn, and verify that
70     // single-stepping works on that cpu. A workaround is needed if we find at least one broken
71     // cpu.
72 
73     Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
74     Error error;
75     ::pid_t child_pid = fork();
76     if (child_pid == -1)
77     {
78         if (log)
79         {
80             error.SetErrorToErrno();
81             log->Printf("%s failed to fork(): %s", __FUNCTION__, error.AsCString());
82         }
83         return false;
84     }
85     if (child_pid == 0)
86         Child();
87 
88     ChildDeleter child_deleter{child_pid};
89     cpu_set_t available_cpus;
90     if (sched_getaffinity(child_pid, sizeof available_cpus, &available_cpus) == -1)
91     {
92         if (log)
93         {
94             error.SetErrorToErrno();
95             log->Printf("%s failed to get available cpus: %s", __FUNCTION__, error.AsCString());
96         }
97         return false;
98     }
99 
100     int status;
101     ::pid_t wpid = waitpid(child_pid, &status, __WALL);
102     if (wpid != child_pid || !WIFSTOPPED(status))
103     {
104         if (log)
105         {
106             error.SetErrorToErrno();
107             log->Printf("%s waitpid() failed (status = %x): %s", __FUNCTION__, status, error.AsCString());
108         }
109         return false;
110     }
111 
112     unsigned cpu;
113     for (cpu = 0; cpu < CPU_SETSIZE; ++cpu)
114     {
115         if (!CPU_ISSET(cpu, &available_cpus))
116             continue;
117 
118         cpu_set_t cpus;
119         CPU_ZERO(&cpus);
120         CPU_SET(cpu, &cpus);
121         if (sched_setaffinity(child_pid, sizeof cpus, &cpus) == -1)
122         {
123             if (log)
124             {
125                 error.SetErrorToErrno();
126                 log->Printf("%s failed to switch to cpu %u: %s", __FUNCTION__, cpu, error.AsCString());
127             }
128             continue;
129         }
130 
131         int status;
132         error = NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid);
133         if (error.Fail())
134         {
135             if (log)
136                 log->Printf("%s single step failed: %s", __FUNCTION__, error.AsCString());
137             break;
138         }
139 
140         wpid = waitpid(child_pid, &status, __WALL);
141         if (wpid != child_pid || !WIFSTOPPED(status))
142         {
143             if (log)
144             {
145                 error.SetErrorToErrno();
146                 log->Printf("%s waitpid() failed (status = %x): %s", __FUNCTION__, status, error.AsCString());
147             }
148             break;
149         }
150         if (WSTOPSIG(status) != SIGTRAP)
151         {
152             if (log)
153                 log->Printf("%s single stepping on cpu %d failed with status %x", __FUNCTION__, cpu, status);
154             break;
155         }
156     }
157 
158     // cpu is either the index of the first broken cpu, or CPU_SETSIZE.
159     if (cpu == 0)
160     {
161         if (log)
162             log->Printf("%s SINGLE STEPPING ON FIRST CPU IS NOT WORKING. DEBUGGING LIKELY TO BE UNRELIABLE.",
163                         __FUNCTION__);
164         // No point in trying to fiddle with the affinities, just give it our best shot and see how it goes.
165         return false;
166     }
167 
168     return cpu != CPU_SETSIZE;
169 }
170 
171 #else // !arm64
172 bool
173 impl::SingleStepWorkaroundNeeded()
174 {
175     return false;
176 }
177 #endif
178