NPL: Compartmentalize arm64 single step workaround better

The main motivation for me doing this is being able to build an arm
android lldb-server against api level 9 headers, but it seems like a
good cleanup nonetheless.

The entirety of the cpu_set_t dance now resides in SingleStepCheck.cpp,
which is only built on arm64.

llvm-svn: 293046
diff --git a/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp b/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp
index b23c1bd..f3a07e0 100644
--- a/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp
+++ b/lldb/source/Plugins/Process/Linux/SingleStepCheck.cpp
@@ -18,10 +18,12 @@
 
 #include "llvm/Support/Compiler.h"
 
+#include "Plugins/Process/POSIX/ProcessPOSIXLog.h"
 #include "lldb/Core/Error.h"
-#include "lldb/Core/Log.h"
 #include "lldb/Host/linux/Ptrace.h"
 
+using namespace lldb;
+using namespace lldb_private;
 using namespace lldb_private::process_linux;
 
 #if defined(__arm64__) || defined(__aarch64__)
@@ -32,16 +34,14 @@
     _exit(1);
 
   // We just do an endless loop SIGSTOPPING ourselves until killed. The tracer
-  // will fiddle with our cpu
-  // affinities and monitor the behaviour.
+  // 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.
+    // 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)
       ;
   }
@@ -57,25 +57,16 @@
   }
 };
 
-} // end anonymous namespace
-
-bool impl::SingleStepWorkaroundNeeded() {
+bool WorkaroundNeeded() {
   // 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.
+  // 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;
+  Log *log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD);
   ::pid_t child_pid = fork();
   if (child_pid == -1) {
-    if (log) {
-      error.SetErrorToErrno();
-      log->Printf("%s failed to fork(): %s", __FUNCTION__, error.AsCString());
-    }
+    LLDB_LOG(log, "failed to fork(): {0}", Error(errno, eErrorTypePOSIX));
     return false;
   }
   if (child_pid == 0)
@@ -85,22 +76,16 @@
   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());
-    }
+    LLDB_LOG(log, "failed to get available cpus: {0}",
+             Error(errno, eErrorTypePOSIX));
     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());
-    }
+    LLDB_LOG(log, "waitpid() failed (status = {0:x}): {1}", status,
+             Error(errno, eErrorTypePOSIX));
     return false;
   }
 
@@ -113,46 +98,37 @@
     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());
-      }
+      LLDB_LOG(log, "failed to switch to cpu {0}: {1}", cpu,
+               Error(errno, eErrorTypePOSIX));
       continue;
     }
 
     int status;
-    error = NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid);
+    Error error =
+        NativeProcessLinux::PtraceWrapper(PTRACE_SINGLESTEP, child_pid);
     if (error.Fail()) {
-      if (log)
-        log->Printf("%s single step failed: %s", __FUNCTION__,
-                    error.AsCString());
+      LLDB_LOG(log, "single step failed: {0}", error);
       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());
-      }
+      LLDB_LOG(log, "waitpid() failed (status = {0:x}): {1}", status,
+               Error(errno, eErrorTypePOSIX));
       break;
     }
     if (WSTOPSIG(status) != SIGTRAP) {
-      if (log)
-        log->Printf("%s single stepping on cpu %d failed with status %x",
-                    __FUNCTION__, cpu, status);
+      LLDB_LOG(log, "single stepping on cpu {0} failed with status {1:x}", 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__);
+    LLDB_LOG(log,
+             "SINGLE STEPPING ON FIRST CPU IS NOT WORKING. DEBUGGING "
+             "LIKELY TO BE UNRELIABLE.");
     // No point in trying to fiddle with the affinities, just give it our best
     // shot and see how it goes.
     return false;
@@ -161,6 +137,45 @@
   return cpu != CPU_SETSIZE;
 }
 
-#else // !arm64
-bool impl::SingleStepWorkaroundNeeded() { return false; }
+} // end anonymous namespace
+
+llvm::Optional<SingleStepWorkaround> SingleStepWorkaround::Get(::pid_t tid) {
+  Log *log = ProcessPOSIXLog::GetLogIfAllCategoriesSet(POSIX_LOG_THREAD);
+
+  static bool workaround_needed = WorkaroundNeeded();
+  if (!workaround_needed) {
+    LLDB_LOG(log, "workaround for thread {0} not needed", tid);
+    return llvm::None;
+  }
+
+  cpu_set_t original_set;
+  if (sched_getaffinity(tid, sizeof original_set, &original_set) != 0) {
+    // This should really not fail. But, just in case...
+    LLDB_LOG(log, "Unable to get cpu affinity for thread {0}: {1}", tid,
+             Error(errno, eErrorTypePOSIX));
+    return llvm::None;
+  }
+
+  cpu_set_t set;
+  CPU_ZERO(&set);
+  CPU_SET(0, &set);
+  if (sched_setaffinity(tid, sizeof set, &set) != 0) {
+    // 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...
+    LLDB_LOG(log, "Unable to set cpu affinity for thread {0}: {1}", tid,
+             Error(errno, eErrorTypePOSIX));
+  }
+
+  LLDB_LOG(log, "workaround for thread {0} prepared", tid);
+  return SingleStepWorkaround(tid, original_set);
+}
+
+SingleStepWorkaround::~SingleStepWorkaround() {
+  if (sched_setaffinity(m_tid, sizeof m_original_set, &m_original_set) != 0) {
+    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
+    LLDB_LOG(log, "Unable to reset cpu affinity for thread {0}: {1}", m_tid,
+             Error(errno, eErrorTypePOSIX));
+  }
+}
 #endif