[Lldb-commits] [lldb] r261636 - Work around a stepping bug in arm64 android M

Pavel Labath via lldb-commits lldb-commits at lists.llvm.org
Tue Feb 23 05:56:30 PST 2016


Author: labath
Date: Tue Feb 23 07:56:30 2016
New Revision: 261636

URL: http://llvm.org/viewvc/llvm-project?rev=261636&view=rev
Log:
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

Added:
    lldb/trunk/source/Plugins/Process/Linux/SingleStepCheck.cpp
    lldb/trunk/source/Plugins/Process/Linux/SingleStepCheck.h
Modified:
    lldb/trunk/source/Plugins/Process/Linux/CMakeLists.txt
    lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp
    lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.h
    lldb/trunk/source/Plugins/Process/Linux/NativeThreadLinux.cpp
    lldb/trunk/source/Plugins/Process/Linux/NativeThreadLinux.h

Modified: lldb/trunk/source/Plugins/Process/Linux/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/CMakeLists.txt?rev=261636&r1=261635&r2=261636&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/CMakeLists.txt (original)
+++ lldb/trunk/source/Plugins/Process/Linux/CMakeLists.txt Tue Feb 23 07:56:30 2016
@@ -11,4 +11,5 @@ add_lldb_library(lldbPluginProcessLinux
   NativeRegisterContextLinux_mips64.cpp
   NativeThreadLinux.cpp
   ProcFileReader.cpp
+  SingleStepCheck.cpp
   )

Modified: lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp?rev=261636&r1=261635&r2=261636&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.cpp Tue Feb 23 07:56:30 2016
@@ -2655,43 +2655,6 @@ NativeProcessLinux::WriteMemory(lldb::ad
 }
 
 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);
@@ -2979,16 +2942,14 @@ NativeProcessLinux::ResumeThread(NativeT
     {
     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;

Modified: lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.h?rev=261636&r1=261635&r2=261636&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.h (original)
+++ lldb/trunk/source/Plugins/Process/Linux/NativeProcessLinux.h Tue Feb 23 07:56:30 2016
@@ -127,6 +127,9 @@ namespace process_linux {
                       size_t data_size = 0,
                       long *result = nullptr);
 
+        bool
+        SupportHardwareSingleStepping() const;
+
     protected:
         // ---------------------------------------------------------------------
         // NativeProcessProtocol protected interface
@@ -239,9 +242,6 @@ namespace process_linux {
         void
         MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread, bool exited);
 
-        bool
-        SupportHardwareSingleStepping() const;
-
         Error
         SetupSoftwareSingleStepping(NativeThreadLinux &thread);
 
@@ -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);
 

Modified: lldb/trunk/source/Plugins/Process/Linux/NativeThreadLinux.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/NativeThreadLinux.cpp?rev=261636&r1=261635&r2=261636&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/NativeThreadLinux.cpp (original)
+++ lldb/trunk/source/Plugins/Process/Linux/NativeThreadLinux.cpp Tue Feb 23 07:56:30 2016
@@ -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 (lld
     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(ui
     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::SetStoppedByBreakpoin
 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 (
     // 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;
+}

Modified: lldb/trunk/source/Plugins/Process/Linux/NativeThreadLinux.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/NativeThreadLinux.h?rev=261636&r1=261635&r2=261636&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/NativeThreadLinux.h (original)
+++ lldb/trunk/source/Plugins/Process/Linux/NativeThreadLinux.h Tue Feb 23 07:56:30 2016
@@ -13,6 +13,8 @@
 #include "lldb/lldb-private-forward.h"
 #include "lldb/Host/common/NativeThreadProtocol.h"
 
+#include <sched.h>
+
 #include <map>
 #include <memory>
 #include <string>
@@ -54,11 +56,16 @@ namespace process_linux {
         // ---------------------------------------------------------------------
         // 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 @@ namespace process_linux {
         void
         MaybeLogStateChange (lldb::StateType new_state);
 
+        NativeProcessLinux &
+        GetProcess();
+
+        void
+        SetStopped();
+
+        inline void
+        MaybePrepareSingleStepWorkaround();
+
+        inline void
+        MaybeCleanupSingleStepWorkaround();
+
         // ---------------------------------------------------------------------
         // Member Variables
         // ---------------------------------------------------------------------
@@ -111,6 +130,7 @@ namespace process_linux {
         std::string m_stop_description;
         using WatchpointIndexMap = std::map<lldb::addr_t, uint32_t>;
         WatchpointIndexMap m_watchpoint_index_map;
+        cpu_set_t m_original_cpu_set; // For single-step workaround.
     };
 
     typedef std::shared_ptr<NativeThreadLinux> NativeThreadLinuxSP;

Added: lldb/trunk/source/Plugins/Process/Linux/SingleStepCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/SingleStepCheck.cpp?rev=261636&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/SingleStepCheck.cpp (added)
+++ lldb/trunk/source/Plugins/Process/Linux/SingleStepCheck.cpp Tue Feb 23 07:56:30 2016
@@ -0,0 +1,177 @@
+//===-- 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 "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__)
+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.
+    }
+};
+
+} // 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;
+    }
+    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());
+        }
+        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;
+        }
+    }
+
+    // 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

Added: lldb/trunk/source/Plugins/Process/Linux/SingleStepCheck.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Process/Linux/SingleStepCheck.h?rev=261636&view=auto
==============================================================================
--- lldb/trunk/source/Plugins/Process/Linux/SingleStepCheck.h (added)
+++ lldb/trunk/source/Plugins/Process/Linux/SingleStepCheck.h Tue Feb 23 07:56:30 2016
@@ -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_SingleStepCheck_H_




More information about the lldb-commits mailing list