
*** to conform to clang-format’s LLVM style. This kind of mass change has *** two obvious implications: Firstly, merging this particular commit into a downstream fork may be a huge effort. Alternatively, it may be worth merging all changes up to this commit, performing the same reformatting operation locally, and then discarding the merge for this particular commit. The commands used to accomplish this reformatting were as follows (with current working directory as the root of the repository): find . \( -iname "*.c" -or -iname "*.cpp" -or -iname "*.h" -or -iname "*.mm" \) -exec clang-format -i {} + find . -iname "*.py" -exec autopep8 --in-place --aggressive --aggressive {} + ; The version of clang-format used was 3.9.0, and autopep8 was 1.2.4. Secondly, “blame” style tools will generally point to this commit instead of a meaningful prior commit. There are alternatives available that will attempt to look through this change and find the appropriate prior commit. YMMV. llvm-svn: 280751
167 lines
4.5 KiB
C++
167 lines
4.5 KiB
C++
//===-- 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
|