This renames the LLDB error class to Status, as discussed on the lldb-dev mailing list. A change of this magnitude cannot easily be done without find and replace, but that has potential to catch unwanted occurrences of common strings such as "Error". Every effort was made to find all the obvious things such as the word "Error" appearing in a string, etc, but it's possible there are still some lingering occurences left around. Hopefully nothing too serious. llvm-svn: 302872
183 lines
5.5 KiB
C++
183 lines
5.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 "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}", Error(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
|