Skip to content

Commit

Permalink
Work around a stepping bug in arm64 android M
Browse files Browse the repository at this point in the history
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
  • Loading branch information
labath committed Feb 23, 2016
1 parent 0231f16 commit 605b51b
Show file tree
Hide file tree
Showing 7 changed files with 354 additions and 95 deletions.
1 change: 1 addition & 0 deletions lldb/source/Plugins/Process/Linux/CMakeLists.txt
Expand Up @@ -11,4 +11,5 @@ add_lldb_library(lldbPluginProcessLinux
NativeRegisterContextLinux_mips64.cpp
NativeThreadLinux.cpp
ProcFileReader.cpp
SingleStepCheck.cpp
)
43 changes: 2 additions & 41 deletions lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
Expand Up @@ -2654,43 +2654,6 @@ NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
return error;
}

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)
{
Expand Down Expand Up @@ -2979,16 +2942,14 @@ NativeProcessLinux::ResumeThread(NativeThreadLinux &thread, lldb::StateType stat
{
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;
Expand Down
16 changes: 3 additions & 13 deletions lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
Expand Up @@ -127,6 +127,9 @@ namespace process_linux {
size_t data_size = 0,
long *result = nullptr);

bool
SupportHardwareSingleStepping() const;

protected:
// ---------------------------------------------------------------------
// NativeProcessProtocol protected interface
Expand Down Expand Up @@ -239,9 +242,6 @@ namespace process_linux {
void
MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited);

bool
SupportHardwareSingleStepping() const;

Error
SetupSoftwareSingleStepping(NativeThreadLinux &thread);

Expand Down Expand Up @@ -285,16 +285,6 @@ namespace process_linux {
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);

Expand Down
143 changes: 106 additions & 37 deletions lldb/source/Plugins/Process/Linux/NativeThreadLinux.cpp
Expand Up @@ -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"

Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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 ()
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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");
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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;
}

0 comments on commit 605b51b

Please sign in to comment.