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