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