Index: source/Plugins/Process/Linux/CMakeLists.txt =================================================================== --- source/Plugins/Process/Linux/CMakeLists.txt +++ source/Plugins/Process/Linux/CMakeLists.txt @@ -11,4 +11,5 @@ NativeRegisterContextLinux_mips64.cpp NativeThreadLinux.cpp ProcFileReader.cpp + SingleStepCheck.cpp ) Index: source/Plugins/Process/Linux/NativeProcessLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.h +++ source/Plugins/Process/Linux/NativeProcessLinux.h @@ -127,6 +127,9 @@ size_t data_size = 0, long *result = nullptr); + bool + SupportHardwareSingleStepping() const; + protected: // --------------------------------------------------------------------- // NativeProcessProtocol protected interface @@ -239,9 +242,6 @@ void MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited); - bool - SupportHardwareSingleStepping() const; - Error SetupSoftwareSingleStepping(NativeThreadLinux &thread); @@ -285,16 +285,6 @@ Error GetEventMessage(lldb::tid_t tid, unsigned long *message); - /// Resumes the given thread. If @p signo is anything but - /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. - Error - Resume(lldb::tid_t tid, uint32_t signo); - - /// Single steps the given thread. If @p signo is anything but - /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. - Error - SingleStep(lldb::tid_t tid, uint32_t signo); - void NotifyThreadDeath (lldb::tid_t tid); Index: source/Plugins/Process/Linux/NativeProcessLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeProcessLinux.cpp +++ source/Plugins/Process/Linux/NativeProcessLinux.cpp @@ -2656,43 +2656,6 @@ } Error -NativeProcessLinux::Resume (lldb::tid_t tid, uint32_t signo) -{ - Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); - - if (log) - log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " with signal %s", __FUNCTION__, tid, - Host::GetSignalAsCString(signo)); - - - - intptr_t data = 0; - - if (signo != LLDB_INVALID_SIGNAL_NUMBER) - data = signo; - - Error error = PtraceWrapper(PTRACE_CONT, tid, nullptr, (void*)data); - - if (log) - log->Printf ("NativeProcessLinux::%s() resuming thread = %" PRIu64 " result = %s", __FUNCTION__, tid, error.Success() ? "true" : "false"); - return error; -} - -Error -NativeProcessLinux::SingleStep(lldb::tid_t tid, uint32_t signo) -{ - intptr_t data = 0; - - if (signo != LLDB_INVALID_SIGNAL_NUMBER) - data = signo; - - // If hardware single-stepping is not supported, we just do a continue. The breakpoint on the - // next instruction has been setup in NativeProcessLinux::Resume. - return PtraceWrapper(SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP : PTRACE_CONT, - tid, nullptr, (void*)data); -} - -Error NativeProcessLinux::GetSignalInfo(lldb::tid_t tid, void *siginfo) { return PtraceWrapper(PTRACE_GETSIGINFO, tid, nullptr, siginfo); @@ -2980,16 +2943,14 @@ { case eStateRunning: { - thread.SetRunning(); - const auto resume_result = Resume(thread.GetID(), signo); + const auto resume_result = thread.Resume(signo); if (resume_result.Success()) SetState(eStateRunning, true); return resume_result; } case eStateStepping: { - thread.SetStepping(); - const auto step_result = SingleStep(thread.GetID(), signo); + const auto step_result = thread.SingleStep(signo); if (step_result.Success()) SetState(eStateRunning, true); return step_result; Index: source/Plugins/Process/Linux/NativeThreadLinux.h =================================================================== --- source/Plugins/Process/Linux/NativeThreadLinux.h +++ source/Plugins/Process/Linux/NativeThreadLinux.h @@ -13,6 +13,8 @@ #include "lldb/lldb-private-forward.h" #include "lldb/Host/common/NativeThreadProtocol.h" +#include + #include #include #include @@ -54,11 +56,16 @@ // --------------------------------------------------------------------- // Interface for friend classes // --------------------------------------------------------------------- - void - SetRunning (); - void - SetStepping (); + /// Resumes the thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Error + Resume(uint32_t signo); + + /// Single steps the thread. If @p signo is anything but + /// LLDB_INVALID_SIGNAL_NUMBER, deliver that signal to the thread. + Error + SingleStep(uint32_t signo); void SetStoppedBySignal(uint32_t signo, const siginfo_t *info = nullptr); @@ -102,6 +109,18 @@ void MaybeLogStateChange (lldb::StateType new_state); + NativeProcessLinux & + GetProcess(); + + void + SetStopped(); + + inline void + MaybePrepareSingleStepWorkaround(); + + inline void + MaybeCleanupSingleStepWorkaround(); + // --------------------------------------------------------------------- // Member Variables // --------------------------------------------------------------------- @@ -111,6 +130,7 @@ std::string m_stop_description; using WatchpointIndexMap = std::map; WatchpointIndexMap m_watchpoint_index_map; + cpu_set_t m_original_cpu_set; // For single-step workaround. }; typedef std::shared_ptr NativeThreadLinuxSP; Index: source/Plugins/Process/Linux/NativeThreadLinux.cpp =================================================================== --- source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ source/Plugins/Process/Linux/NativeThreadLinux.cpp @@ -14,10 +14,12 @@ #include "NativeProcessLinux.h" #include "NativeRegisterContextLinux.h" +#include "SingleStepCheck.h" #include "lldb/Core/Log.h" #include "lldb/Core/State.h" #include "lldb/Host/HostNativeThread.h" +#include "lldb/Host/linux/Ptrace.h" #include "lldb/Utility/LLDBAssert.h" #include "lldb/lldb-enumerations.h" @@ -199,8 +201,8 @@ return Error ("Clearing hardware watchpoint failed."); } -void -NativeThreadLinux::SetRunning () +Error +NativeThreadLinux::Resume(uint32_t signo) { const StateType new_state = StateType::eStateRunning; MaybeLogStateChange (new_state); @@ -213,29 +215,88 @@ // then this is a new thread. So set all existing watchpoints. if (m_watchpoint_index_map.empty()) { - const auto process_sp = GetProcess(); - if (process_sp) + NativeProcessLinux &process = GetProcess(); + + const auto &watchpoint_map = process.GetWatchpointMap(); + GetRegisterContext()->ClearAllHardwareWatchpoints(); + for (const auto &pair : watchpoint_map) { - const auto &watchpoint_map = process_sp->GetWatchpointMap(); - if (watchpoint_map.empty()) return; - GetRegisterContext()->ClearAllHardwareWatchpoints(); - for (const auto &pair : watchpoint_map) - { - const auto& wp = pair.second; - SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); - } + const auto &wp = pair.second; + SetWatchpoint(wp.m_addr, wp.m_size, wp.m_watch_flags, wp.m_hardware); } } + + intptr_t data = 0; + + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + return NativeProcessLinux::PtraceWrapper(PTRACE_CONT, GetID(), nullptr, reinterpret_cast(data)); +} + +void +NativeThreadLinux::MaybePrepareSingleStepWorkaround() +{ + if (!SingleStepWorkaroundNeeded()) + return; + + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + + if (sched_getaffinity(static_cast<::pid_t>(m_tid), sizeof m_original_cpu_set, &m_original_cpu_set) != 0) + { + // This should really not fail. But, just in case... + if (log) + log->Printf("NativeThreadLinux::%s Unable to get cpu affinity for thread %" PRIx64 ": %s", __FUNCTION__, + m_tid, strerror(errno)); + return; + } + + cpu_set_t set; + CPU_ZERO(&set); + CPU_SET(0, &set); + if (sched_setaffinity(static_cast<::pid_t>(m_tid), sizeof set, &set) != 0 && log) + { + // 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... + log->Printf("NativeThreadLinux::%s Unable to set cpu affinity for thread %" PRIx64 ": %s", __FUNCTION__, m_tid, + strerror(errno)); + } } void -NativeThreadLinux::SetStepping () +NativeThreadLinux::MaybeCleanupSingleStepWorkaround() +{ + if (!SingleStepWorkaroundNeeded()) + return; + + if (sched_setaffinity(static_cast<::pid_t>(m_tid), sizeof m_original_cpu_set, &m_original_cpu_set) != 0) + { + Error error(errno, eErrorTypePOSIX); + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD)); + log->Printf("NativeThreadLinux::%s Unable to reset cpu affinity for thread %" PRIx64 ": %s", __FUNCTION__, + m_tid, error.AsCString()); + } +} + +Error +NativeThreadLinux::SingleStep(uint32_t signo) { const StateType new_state = StateType::eStateStepping; MaybeLogStateChange (new_state); m_state = new_state; - m_stop_info.reason = StopReason::eStopReasonNone; + + MaybePrepareSingleStepWorkaround(); + + intptr_t data = 0; + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + data = signo; + + // If hardware single-stepping is not supported, we just do a continue. The breakpoint on the + // next instruction has been setup in NativeProcessLinux::Resume. + return NativeProcessLinux::PtraceWrapper(GetProcess().SupportHardwareSingleStepping() ? PTRACE_SINGLESTEP + : PTRACE_CONT, + m_tid, nullptr, reinterpret_cast(data)); } void @@ -245,9 +306,7 @@ if (log) log->Printf ("NativeThreadLinux::%s called with signal 0x%02" PRIx32, __FUNCTION__, signo); - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; + SetStopped(); m_stop_info.reason = StopReason::eStopReasonSignal; m_stop_info.details.signal.signo = signo; @@ -288,6 +347,17 @@ return true; } +void +NativeThreadLinux::SetStopped() +{ + if (m_state == StateType::eStateStepping) + MaybeCleanupSingleStepWorkaround(); + + const StateType new_state = StateType::eStateStopped; + MaybeLogStateChange(new_state); + m_state = new_state; + m_stop_description.clear(); +} void NativeThreadLinux::SetStoppedByExec () @@ -296,9 +366,7 @@ if (log) log->Printf ("NativeThreadLinux::%s()", __FUNCTION__); - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; + SetStopped(); m_stop_info.reason = StopReason::eStopReasonExec; m_stop_info.details.signal.signo = SIGSTOP; @@ -307,9 +375,7 @@ void NativeThreadLinux::SetStoppedByBreakpoint () { - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; + SetStopped(); m_stop_info.reason = StopReason::eStopReasonBreakpoint; m_stop_info.details.signal.signo = SIGTRAP; @@ -319,10 +385,7 @@ void NativeThreadLinux::SetStoppedByWatchpoint (uint32_t wp_index) { - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; - m_stop_description.clear (); + SetStopped(); lldbassert(wp_index != LLDB_INVALID_INDEX32 && "wp_index cannot be invalid"); @@ -363,9 +426,7 @@ void NativeThreadLinux::SetStoppedByTrace () { - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; + SetStopped(); m_stop_info.reason = StopReason::eStopReasonTrace; m_stop_info.details.signal.signo = SIGTRAP; @@ -374,9 +435,7 @@ void NativeThreadLinux::SetStoppedWithNoReason () { - const StateType new_state = StateType::eStateStopped; - MaybeLogStateChange (new_state); - m_state = new_state; + SetStopped(); m_stop_info.reason = StopReason::eStopReasonNone; m_stop_info.details.signal.signo = 0; @@ -397,11 +456,9 @@ { Log* log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); - const auto process_sp = GetProcess(); - if (! process_sp) - return Error("Process is null."); + NativeProcessLinux &process = GetProcess(); - lldb::pid_t pid = process_sp->GetID(); + lldb::pid_t pid = process.GetID(); lldb::tid_t tid = GetID(); if (log) @@ -438,3 +495,11 @@ // Log it. log->Printf ("NativeThreadLinux: thread (pid=%" PRIu64 ", tid=%" PRIu64 ") changing from state %s to %s", pid, GetID (), StateAsCString (old_state), StateAsCString (new_state)); } + +NativeProcessLinux & +NativeThreadLinux::GetProcess() +{ + auto process_sp = std::static_pointer_cast(NativeThreadProtocol::GetProcess()); + assert(process_sp); + return *process_sp; +} Index: source/Plugins/Process/Linux/SingleStepCheck.h =================================================================== --- /dev/null +++ source/Plugins/Process/Linux/SingleStepCheck.h @@ -0,0 +1,41 @@ +//===-- SingleStepCheck.h ------------------------------------- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SingleStepCheck_H_ +#define liblldb_SingleStepCheck_H_ + +namespace lldb_private +{ +namespace process_linux +{ + +namespace impl +{ +extern bool +SingleStepWorkaroundNeeded(); +} + +// arm64 linux had a bug which prevented single-stepping and watchpoints from working on non-boot +// cpus, due to them being incorrectly initialized after coming out of suspend. This issue is +// particularly affecting android M, which uses suspend ("doze mode") quite aggressively. This +// code detects that situation and makes single-stepping work by doing all the step operations on +// the boot cpu. +// +// The underlying issue has been fixed in android N and linux 4.4. This code can be removed once +// these systems become obsolete. +inline bool +SingleStepWorkaroundNeeded() +{ + static bool value = impl::SingleStepWorkaroundNeeded(); + return value; +} +} // end namespace process_linux +} // end namespace lldb_private + +#endif // #ifndef liblldb_NativeThreadLinux_H_ Index: source/Plugins/Process/Linux/SingleStepCheck.cpp =================================================================== --- /dev/null +++ source/Plugins/Process/Linux/SingleStepCheck.cpp @@ -0,0 +1,163 @@ +//===-- 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 +#include +#include +#include + +#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__) +static 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 instruction 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) + ; + } +} + +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(); + + 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()); + } + kill(child_pid, SIGKILL); + 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; + } + } + + kill(child_pid, SIGKILL); // Kill the child. + waitpid(child_pid, &status, __WALL); // And pick up the remains. + + // 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