187 lines · cpp
1//===-- SingleStepCheck.cpp -----------------------------------------------===//2//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "SingleStepCheck.h"10 11#include <csignal>12#include <sched.h>13#include <sys/wait.h>14#include <unistd.h>15 16#include "NativeProcessLinux.h"17 18#include "llvm/Support/Compiler.h"19#include "llvm/Support/Errno.h"20 21#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"22#include "lldb/Host/linux/Ptrace.h"23#include "lldb/Utility/Status.h"24 25using namespace lldb;26using namespace lldb_private;27using namespace lldb_private::process_linux;28 29#if defined(__arm64__) || defined(__aarch64__)30namespace {31 32[[noreturn]] void Child() {33 if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1)34 _exit(1);35 36 // We just do an endless loop SIGSTOPPING ourselves until killed. The tracer37 // will fiddle with our cpu affinities and monitor the behaviour.38 for (;;) {39 raise(SIGSTOP);40 41 // Generate a bunch of instructions here, so that a single-step does not42 // land in the raise() accidentally. If single-stepping works, we will be43 // spinning in this loop. If it doesn't, we'll land in the raise() call44 // above.45 for (volatile unsigned i = 0; i < CPU_SETSIZE; ++i)46 ;47 }48}49 50struct ChildDeleter {51 ::pid_t pid;52 53 ~ChildDeleter() {54 int status;55 // Kill the child.56 kill(pid, SIGKILL);57 // Pick up the remains.58 llvm::sys::RetryAfterSignal(-1, waitpid, pid, &status, __WALL);59 }60};61 62bool WorkaroundNeeded() {63 // We shall spawn a child, and use it to verify the debug capabilities of the64 // cpu. We shall iterate through the cpus, bind the child to each one in65 // turn, and verify that single-stepping works on that cpu. A workaround is66 // needed if we find at least one broken cpu.67 68 Log *log = GetLog(POSIXLog::Thread);69 ::pid_t child_pid = fork();70 if (child_pid == -1) {71 LLDB_LOG(log, "failed to fork(): {0}", Status(errno, eErrorTypePOSIX));72 return false;73 }74 if (child_pid == 0)75 Child();76 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) {81 LLDB_LOG(log, "failed to get available cpus: {0}",82 Status(errno, eErrorTypePOSIX));83 return false;84 }85 86 int status;87 ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, waitpid,88 child_pid, &status, __WALL);89 if (wpid != child_pid || !WIFSTOPPED(status)) {90 LLDB_LOG(log, "waitpid() failed (status = {0:x}): {1}", status,91 Status(errno, eErrorTypePOSIX));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) {104 LLDB_LOG(log, "failed to switch to cpu {0}: {1}", cpu,105 Status(errno, eErrorTypePOSIX));106 continue;107 }108 109 int status;110 Status error =111 NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid);112 if (error.Fail()) {113 LLDB_LOG(log, "single step failed: {0}", error);114 break;115 }116 117 wpid = llvm::sys::RetryAfterSignal(-1, waitpid,118 child_pid, &status, __WALL);119 if (wpid != child_pid || !WIFSTOPPED(status)) {120 LLDB_LOG(log, "waitpid() failed (status = {0:x}): {1}", status,121 Status(errno, eErrorTypePOSIX));122 break;123 }124 if (WSTOPSIG(status) != SIGTRAP) {125 LLDB_LOG(log, "single stepping on cpu {0} failed with status {1:x}", cpu,126 status);127 break;128 }129 }130 131 // cpu is either the index of the first broken cpu, or CPU_SETSIZE.132 if (cpu == 0) {133 LLDB_LOG(log,134 "SINGLE STEPPING ON FIRST CPU IS NOT WORKING. DEBUGGING "135 "LIKELY TO BE UNRELIABLE.");136 // No point in trying to fiddle with the affinities, just give it our best137 // shot and see how it goes.138 return false;139 }140 141 return cpu != CPU_SETSIZE;142}143 144} // end anonymous namespace145 146std::unique_ptr<SingleStepWorkaround> SingleStepWorkaround::Get(::pid_t tid) {147 Log *log = GetLog(POSIXLog::Thread);148 149 static bool workaround_needed = WorkaroundNeeded();150 if (!workaround_needed) {151 LLDB_LOG(log, "workaround for thread {0} not needed", tid);152 return nullptr;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,159 Status(errno, eErrorTypePOSIX));160 return nullptr;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 allowed168 // to run on cpu 0. If that happens, only thing we can do is it log it and169 // continue...170 LLDB_LOG(log, "Unable to set cpu affinity for thread {0}: {1}", tid,171 Status(errno, eErrorTypePOSIX));172 }173 174 LLDB_LOG(log, "workaround for thread {0} prepared", tid);175 return std::make_unique<SingleStepWorkaround>(tid, original_set);176}177 178SingleStepWorkaround::~SingleStepWorkaround() {179 Log *log = GetLog(POSIXLog::Thread);180 LLDB_LOG(log, "Removing workaround");181 if (sched_setaffinity(m_tid, sizeof m_original_set, &m_original_set) != 0) {182 LLDB_LOG(log, "Unable to reset cpu affinity for thread {0}: {1}", m_tid,183 Status(errno, eErrorTypePOSIX));184 }185}186#endif187