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/NativeThreadLinux.cpp | |
| parent | 0231f1649bd0d74828db9b41481421a67f469a6a (diff) | |
| download | llvm-605b51b84e622ce64ee77b066599c7b16d9d2c9d.zip llvm-605b51b84e622ce64ee77b066599c7b16d9d2c9d.tar.gz llvm-605b51b84e622ce64ee77b066599c7b16d9d2c9d.tar.bz2 | |
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/NativeThreadLinux.cpp')
| -rw-r--r-- | lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp | 143 |
1 files changed, 106 insertions, 37 deletions
diff --git a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp b/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp index cbf8288..070b1bcda 100644 --- a/lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp +++ b/lldb/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 @@ NativeThreadLinux::RemoveWatchpoint (lldb::addr_t addr) 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,92 @@ NativeThreadLinux::SetRunning () // 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<void *>(data)); } void -NativeThreadLinux::SetStepping () +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) + { + Error error(errno, eErrorTypePOSIX); + log->Printf("NativeThreadLinux::%s Unable to get cpu affinity for thread %" PRIx64 ": %s", __FUNCTION__, + m_tid, error.AsCString()); + } + 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... + Error error(errno, eErrorTypePOSIX); + log->Printf("NativeThreadLinux::%s Unable to set cpu affinity for thread %" PRIx64 ": %s", __FUNCTION__, m_tid, + error.AsCString()); + } +} + +void +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<void *>(data)); } void @@ -245,9 +310,7 @@ NativeThreadLinux::SetStoppedBySignal(uint32_t signo, const siginfo_t *info) 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 +351,17 @@ NativeThreadLinux::IsStopped (int *signo) 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 +370,7 @@ NativeThreadLinux::SetStoppedByExec () 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 +379,7 @@ NativeThreadLinux::SetStoppedByExec () 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 +389,7 @@ NativeThreadLinux::SetStoppedByBreakpoint () 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 +430,7 @@ NativeThreadLinux::IsStoppedAtWatchpoint () 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 +439,7 @@ NativeThreadLinux::SetStoppedByTrace () 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 +460,9 @@ NativeThreadLinux::RequestStop () { 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 +499,11 @@ NativeThreadLinux::MaybeLogStateChange (lldb::StateType new_state) // 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<NativeProcessLinux>(NativeThreadProtocol::GetProcess()); + assert(process_sp); + return *process_sp; +} |
