diff options
Diffstat (limited to 'lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp')
-rw-r--r-- | lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp | 249 |
1 files changed, 119 insertions, 130 deletions
diff --git a/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp b/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp index 8c557d4b6ff..b23c1bd245d 100644 --- a/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp +++ b/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp @@ -25,153 +25,142 @@ 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) - ; - } +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; +struct ChildDeleter { + ::pid_t pid; - ~ChildDeleter() - { - int status; - kill(pid, SIGKILL); // Kill the child. - waitpid(pid, &status, __WALL); // Pick up the remains. - } + ~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; +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()); } - 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; + 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; - ::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; + error = NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid); + if (error.Fail()) { + if (log) + log->Printf("%s single step failed: %s", __FUNCTION__, + error.AsCString()); + break; } - 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; - } + 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; } - - // 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; + 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; + return cpu != CPU_SETSIZE; } #else // !arm64 -bool -impl::SingleStepWorkaroundNeeded() -{ - return false; -} +bool impl::SingleStepWorkaroundNeeded() { return false; } #endif |