diff options
Diffstat (limited to 'source/Plugins/Process/Linux/SingleStepCheck.cpp')
| -rw-r--r-- | source/Plugins/Process/Linux/SingleStepCheck.cpp | 130 |
1 files changed, 73 insertions, 57 deletions
diff --git a/source/Plugins/Process/Linux/SingleStepCheck.cpp b/source/Plugins/Process/Linux/SingleStepCheck.cpp index b23c1bd245d5..4e979bd45532 100644 --- a/source/Plugins/Process/Linux/SingleStepCheck.cpp +++ b/source/Plugins/Process/Linux/SingleStepCheck.cpp @@ -18,10 +18,12 @@ #include "llvm/Support/Compiler.h" -#include "lldb/Core/Error.h" -#include "lldb/Core/Log.h" +#include "Plugins/Process/POSIX/ProcessPOSIXLog.h" #include "lldb/Host/linux/Ptrace.h" +#include "lldb/Utility/Error.h" +using namespace lldb; +using namespace lldb_private; using namespace lldb_private::process_linux; #if defined(__arm64__) || defined(__aarch64__) @@ -32,16 +34,14 @@ void LLVM_ATTRIBUTE_NORETURN Child() { _exit(1); // We just do an endless loop SIGSTOPPING ourselves until killed. The tracer - // will fiddle with our cpu - // affinities and monitor the behaviour. + // 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. + // 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) ; } @@ -57,25 +57,16 @@ struct ChildDeleter { } }; -} // end anonymous namespace - -bool impl::SingleStepWorkaroundNeeded() { +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(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); - Error error; + // 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) { - if (log) { - error.SetErrorToErrno(); - log->Printf("%s failed to fork(): %s", __FUNCTION__, error.AsCString()); - } + LLDB_LOG(log, "failed to fork(): {0}", Error(errno, eErrorTypePOSIX)); return false; } if (child_pid == 0) @@ -85,22 +76,16 @@ bool impl::SingleStepWorkaroundNeeded() { cpu_set_t available_cpus; if (sched_getaffinity(child_pid, sizeof available_cpus, &available_cpus) == -1) { - if (log) { - error.SetErrorToErrno(); - log->Printf("%s failed to get available cpus: %s", __FUNCTION__, - error.AsCString()); - } + LLDB_LOG(log, "failed to get available cpus: {0}", + Error(errno, eErrorTypePOSIX)); return false; } int status; ::pid_t wpid = waitpid(child_pid, &status, __WALL); if (wpid != child_pid || !WIFSTOPPED(status)) { - if (log) { - error.SetErrorToErrno(); - log->Printf("%s waitpid() failed (status = %x): %s", __FUNCTION__, status, - error.AsCString()); - } + LLDB_LOG(log, "waitpid() failed (status = {0:x}): {1}", status, + Error(errno, eErrorTypePOSIX)); return false; } @@ -113,46 +98,37 @@ bool impl::SingleStepWorkaroundNeeded() { CPU_ZERO(&cpus); CPU_SET(cpu, &cpus); if (sched_setaffinity(child_pid, sizeof cpus, &cpus) == -1) { - if (log) { - error.SetErrorToErrno(); - log->Printf("%s failed to switch to cpu %u: %s", __FUNCTION__, cpu, - error.AsCString()); - } + LLDB_LOG(log, "failed to switch to cpu {0}: {1}", cpu, + Error(errno, eErrorTypePOSIX)); continue; } int status; - error = NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid); + Error error = + NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid); if (error.Fail()) { - if (log) - log->Printf("%s single step failed: %s", __FUNCTION__, - error.AsCString()); + LLDB_LOG(log, "single step failed: {0}", error); break; } wpid = waitpid(child_pid, &status, __WALL); if (wpid != child_pid || !WIFSTOPPED(status)) { - if (log) { - error.SetErrorToErrno(); - log->Printf("%s waitpid() failed (status = %x): %s", __FUNCTION__, - status, error.AsCString()); - } + LLDB_LOG(log, "waitpid() failed (status = {0:x}): {1}", status, + Error(errno, eErrorTypePOSIX)); break; } if (WSTOPSIG(status) != SIGTRAP) { - if (log) - log->Printf("%s single stepping on cpu %d failed with status %x", - __FUNCTION__, cpu, status); + 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) { - if (log) - log->Printf("%s SINGLE STEPPING ON FIRST CPU IS NOT WORKING. DEBUGGING " - "LIKELY TO BE UNRELIABLE.", - __FUNCTION__); + 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; @@ -161,6 +137,46 @@ bool impl::SingleStepWorkaroundNeeded() { return cpu != CPU_SETSIZE; } -#else // !arm64 -bool impl::SingleStepWorkaroundNeeded() { return false; } +} // 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, + Error(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, + Error(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, + Error(errno, eErrorTypePOSIX)); + } +} #endif |
