diff options
Diffstat (limited to 'source/Plugins/Process/Linux/SingleStepCheck.cpp')
-rw-r--r-- | source/Plugins/Process/Linux/SingleStepCheck.cpp | 182 |
1 files changed, 0 insertions, 182 deletions
diff --git a/source/Plugins/Process/Linux/SingleStepCheck.cpp b/source/Plugins/Process/Linux/SingleStepCheck.cpp deleted file mode 100644 index c57a2daf2275..000000000000 --- a/source/Plugins/Process/Linux/SingleStepCheck.cpp +++ /dev/null @@ -1,182 +0,0 @@ -//===-- SingleStepCheck.cpp ----------------------------------- -*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "SingleStepCheck.h" - -#include <sched.h> -#include <signal.h> -#include <sys/wait.h> -#include <unistd.h> - -#include "NativeProcessLinux.h" - -#include "llvm/Support/Compiler.h" - -#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" -#include "lldb/Host/linux/Ptrace.h" -#include "lldb/Utility/Status.h" - -using namespace lldb; -using namespace lldb_private; -using namespace lldb_private::process_linux; - -#if defined(__arm64__) || defined(__aarch64__) -namespace { - -void LLVM_ATTRIBUTE_NORETURN Child() { - if (ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == -1) - _exit(1); - - // We just do an endless loop SIGSTOPPING ourselves until killed. The tracer - // will fiddle with our cpu affinities and monitor the behaviour. - for (;;) { - raise(SIGSTOP); - - // Generate a bunch of instructions here, so that a single-step does not - // land in the raise() accidentally. If single-stepping works, we will be - // spinning in this loop. If it doesn't, we'll land in the raise() call - // above. - for (volatile unsigned i = 0; i < CPU_SETSIZE; ++i) - ; - } -} - -struct ChildDeleter { - ::pid_t pid; - - ~ChildDeleter() { - int status; - kill(pid, SIGKILL); // Kill the child. - waitpid(pid, &status, __WALL); // Pick up the remains. - } -}; - -bool WorkaroundNeeded() { - // We shall spawn a child, and use it to verify the debug capabilities of the - // cpu. We shall iterate through the cpus, bind the child to each one in - // turn, and verify that single-stepping works on that cpu. A workaround is - // needed if we find at least one broken cpu. - - Log *log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); - ::pid_t child_pid = fork(); - if (child_pid == -1) { - LLDB_LOG(log, "failed to fork(): {0}", Status(errno, eErrorTypePOSIX)); - return false; - } - if (child_pid == 0) - Child(); - - ChildDeleter child_deleter{child_pid}; - cpu_set_t available_cpus; - if (sched_getaffinity(child_pid, sizeof available_cpus, &available_cpus) == - -1) { - LLDB_LOG(log, "failed to get available cpus: {0}", - Status(errno, eErrorTypePOSIX)); - return false; - } - - int status; - ::pid_t wpid = waitpid(child_pid, &status, __WALL); - if (wpid != child_pid || !WIFSTOPPED(status)) { - LLDB_LOG(log, "waitpid() failed (status = {0:x}): {1}", status, - Status(errno, eErrorTypePOSIX)); - return false; - } - - unsigned cpu; - for (cpu = 0; cpu < CPU_SETSIZE; ++cpu) { - if (!CPU_ISSET(cpu, &available_cpus)) - continue; - - cpu_set_t cpus; - CPU_ZERO(&cpus); - CPU_SET(cpu, &cpus); - if (sched_setaffinity(child_pid, sizeof cpus, &cpus) == -1) { - LLDB_LOG(log, "failed to switch to cpu {0}: {1}", cpu, - Status(errno, eErrorTypePOSIX)); - continue; - } - - int status; - Status error = - NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid); - if (error.Fail()) { - LLDB_LOG(log, "single step failed: {0}", error); - break; - } - - wpid = waitpid(child_pid, &status, __WALL); - if (wpid != child_pid || !WIFSTOPPED(status)) { - LLDB_LOG(log, "waitpid() failed (status = {0:x}): {1}", status, - Status(errno, eErrorTypePOSIX)); - break; - } - if (WSTOPSIG(status) != SIGTRAP) { - LLDB_LOG(log, "single stepping on cpu {0} failed with status {1:x}", cpu, - status); - break; - } - } - - // cpu is either the index of the first broken cpu, or CPU_SETSIZE. - if (cpu == 0) { - LLDB_LOG(log, - "SINGLE STEPPING ON FIRST CPU IS NOT WORKING. DEBUGGING " - "LIKELY TO BE UNRELIABLE."); - // No point in trying to fiddle with the affinities, just give it our best - // shot and see how it goes. - return false; - } - - return cpu != CPU_SETSIZE; -} - -} // end anonymous namespace - -std::unique_ptr<SingleStepWorkaround> SingleStepWorkaround::Get(::pid_t tid) { - Log *log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); - - static bool workaround_needed = WorkaroundNeeded(); - if (!workaround_needed) { - LLDB_LOG(log, "workaround for thread {0} not needed", tid); - return nullptr; - } - - cpu_set_t original_set; - if (sched_getaffinity(tid, sizeof original_set, &original_set) != 0) { - // This should really not fail. But, just in case... - LLDB_LOG(log, "Unable to get cpu affinity for thread {0}: {1}", tid, - Status(errno, eErrorTypePOSIX)); - return nullptr; - } - - cpu_set_t set; - CPU_ZERO(&set); - CPU_SET(0, &set); - if (sched_setaffinity(tid, sizeof set, &set) != 0) { - // This may fail in very locked down systems, if the thread is not allowed - // to run on cpu 0. If that happens, only thing we can do is it log it and - // continue... - LLDB_LOG(log, "Unable to set cpu affinity for thread {0}: {1}", tid, - Status(errno, eErrorTypePOSIX)); - } - - LLDB_LOG(log, "workaround for thread {0} prepared", tid); - return llvm::make_unique<SingleStepWorkaround>(tid, original_set); -} - -SingleStepWorkaround::~SingleStepWorkaround() { - Log *log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD); - LLDB_LOG(log, "Removing workaround"); - if (sched_setaffinity(m_tid, sizeof m_original_set, &m_original_set) != 0) { - LLDB_LOG(log, "Unable to reset cpu affinity for thread {0}: {1}", m_tid, - Status(errno, eErrorTypePOSIX)); - } -} -#endif |