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