diff options
author | Pavel Labath <labath@google.com> | 2016-02-23 13:56:30 +0000 |
---|---|---|
committer | Pavel Labath <labath@google.com> | 2016-02-23 13:56:30 +0000 |
commit | 605b51b84e622ce64ee77b066599c7b16d9d2c9d (patch) | |
tree | 0ca0c82ebce807499af82ea3c4cd2cd23fb6ff9c /lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp | |
parent | 0231f1649bd0d74828db9b41481421a67f469a6a (diff) | |
download | bcm5719-llvm-605b51b84e622ce64ee77b066599c7b16d9d2c9d.tar.gz bcm5719-llvm-605b51b84e622ce64ee77b066599c7b16d9d2c9d.zip |
Work around a stepping bug in arm64 android M
Summary:
On arm64, linux<=4.4 and Android<=M there is a bug, which prevents single-stepping from working when
the system comes back from suspend, because of incorrectly initialized CPUs. This did not really
affect Android<M, because it did not use software suspend, but it is a problem for M, which uses
suspend (doze) quite extensively. Fortunately, it seems that the first CPU is not affected by
this bug, so this commit implements a workaround by forcing the inferior to execute on the first
cpu whenever we are doing single stepping.
While inside, I have moved the implementations of Resume() and SingleStep() to the thread class
(instead of process).
Reviewers: tberghammer, ovyalov
Subscribers: aemerson, rengolin, tberghammer, danalbert, srhines, lldb-commits
Differential Revision: http://reviews.llvm.org/D17509
llvm-svn: 261636
Diffstat (limited to 'lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp')
-rw-r--r-- | lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp b/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp new file mode 100644 index 00000000000..8c557d4b6ff --- /dev/null +++ b/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp @@ -0,0 +1,177 @@ +//===-- 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 "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/linux/Ptrace.h" + +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. + } +}; + +} // end anonymous namespace + +bool +impl::SingleStepWorkaroundNeeded() +{ + // 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; + ::pid_t child_pid = fork(); + if (child_pid == -1) + { + if (log) + { + error.SetErrorToErrno(); + log->Printf("%s failed to fork(): %s", __FUNCTION__, error.AsCString()); + } + 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) + { + if (log) + { + error.SetErrorToErrno(); + log->Printf("%s failed to get available cpus: %s", __FUNCTION__, error.AsCString()); + } + 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()); + } + 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) + { + if (log) + { + error.SetErrorToErrno(); + log->Printf("%s failed to switch to cpu %u: %s", __FUNCTION__, cpu, error.AsCString()); + } + continue; + } + + int status; + error = NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid); + if (error.Fail()) + { + if (log) + log->Printf("%s single step failed: %s", __FUNCTION__, error.AsCString()); + 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()); + } + break; + } + if (WSTOPSIG(status) != SIGTRAP) + { + if (log) + log->Printf("%s single stepping on cpu %d failed with status %x", __FUNCTION__, 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__); + // 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; +} + +#else // !arm64 +bool +impl::SingleStepWorkaroundNeeded() +{ + return false; +} +#endif |