brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.6 KiB · e7124ef Raw
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