diff options
Diffstat (limited to 'lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp')
| -rw-r--r-- | lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp | 1535 | 
1 files changed, 1535 insertions, 0 deletions
diff --git a/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp b/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp new file mode 100644 index 000000000000..18dbdda9a33b --- /dev/null +++ b/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp @@ -0,0 +1,1535 @@ +//===-- NativeProcessDarwin.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "NativeProcessDarwin.h" + +// C includes +#include <mach/mach_init.h> +#include <mach/mach_traps.h> +#include <sys/ptrace.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/types.h> + +// C++ includes +// LLDB includes +#include "lldb/Host/PseudoTerminal.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/State.h" +#include "lldb/Utility/StreamString.h" + +#include "CFBundle.h" +#include "CFString.h" +#include "DarwinProcessLauncher.h" + +#include "MachException.h" + +#include "llvm/Support/FileSystem.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_darwin; +using namespace lldb_private::darwin_process_launcher; + +// Hidden Impl + +namespace { +struct hack_task_dyld_info { +  mach_vm_address_t all_image_info_addr; +  mach_vm_size_t all_image_info_size; +}; +} + +// Public Static Methods + +Status NativeProcessProtocol::Launch( +    ProcessLaunchInfo &launch_info, +    NativeProcessProtocol::NativeDelegate &native_delegate, MainLoop &mainloop, +    NativeProcessProtocolSP &native_process_sp) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  Status error; + +  // Verify the working directory is valid if one was specified. +  FileSpec working_dir(launch_info.GetWorkingDirectory()); +  if (working_dir) { +    FileInstance::Instance().Resolve(working_dir); +    if (!FileSystem::Instance().IsDirectory(working_dir)) { +      error.SetErrorStringWithFormat("No such file or directory: %s", +                                   working_dir.GetCString()); +      return error; +    } +  } + +  // Launch the inferior. +  int pty_master_fd = -1; +  LaunchFlavor launch_flavor = LaunchFlavor::Default; + +  error = LaunchInferior(launch_info, &pty_master_fd, &launch_flavor); + +  // Handle launch failure. +  if (!error.Success()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s() failed to launch process: " +              "%s", +              __FUNCTION__, error.AsCString()); +    return error; +  } + +  // Handle failure to return a pid. +  if (launch_info.GetProcessID() == LLDB_INVALID_PROCESS_ID) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s() launch succeeded but no " +              "pid was returned!  Aborting.", +              __FUNCTION__); +    return error; +  } + +  // Create the Darwin native process impl. +  std::shared_ptr<NativeProcessDarwin> np_darwin_sp( +      new NativeProcessDarwin(launch_info.GetProcessID(), pty_master_fd)); +  if (!np_darwin_sp->RegisterNativeDelegate(native_delegate)) { +    native_process_sp.reset(); +    error.SetErrorStringWithFormat("failed to register the native delegate"); +    return error; +  } + +  // Finalize the processing needed to debug the launched process with a +  // NativeProcessDarwin instance. +  error = np_darwin_sp->FinalizeLaunch(launch_flavor, mainloop); +  if (!error.Success()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s() aborting, failed to finalize" +              " the launching of the process: %s", +              __FUNCTION__, error.AsCString()); +    return error; +  } + +  // Return the process and process id to the caller through the launch args. +  native_process_sp = np_darwin_sp; +  return error; +} + +Status NativeProcessProtocol::Attach( +    lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate, +    MainLoop &mainloop, NativeProcessProtocolSP &native_process_sp) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); +  LLDB_LOGF(log, "NativeProcessDarwin::%s(pid = %" PRIi64 ")", __FUNCTION__, +            pid); + +  // Retrieve the architecture for the running process. +  ArchSpec process_arch; +  Status error = ResolveProcessArchitecture(pid, process_arch); +  if (!error.Success()) +    return error; + +  // TODO get attach to return this value. +  const int pty_master_fd = -1; +  std::shared_ptr<NativeProcessDarwin> native_process_darwin_sp( +      new NativeProcessDarwin(pid, pty_master_fd)); + +  if (!native_process_darwin_sp->RegisterNativeDelegate(native_delegate)) { +    error.SetErrorStringWithFormat("failed to register the native " +                                   "delegate"); +    return error; +  } + +  native_process_darwin_sp->AttachToInferior(mainloop, pid, error); +  if (!error.Success()) +    return error; + +  native_process_sp = native_process_darwin_sp; +  return error; +} + +// ctor/dtor + +NativeProcessDarwin::NativeProcessDarwin(lldb::pid_t pid, int pty_master_fd) +    : NativeProcessProtocol(pid), m_task(TASK_NULL), m_did_exec(false), +      m_cpu_type(0), m_exception_port(MACH_PORT_NULL), m_exc_port_info(), +      m_exception_thread(nullptr), m_exception_messages_mutex(), +      m_sent_interrupt_signo(0), m_auto_resume_signo(0), m_thread_list(), +      m_thread_actions(), m_waitpid_pipe(), m_waitpid_thread(nullptr), +      m_waitpid_reader_handle() { +  // TODO add this to the NativeProcessProtocol constructor. +  m_terminal_fd = pty_master_fd; +} + +NativeProcessDarwin::~NativeProcessDarwin() {} + +// Instance methods + +Status NativeProcessDarwin::FinalizeLaunch(LaunchFlavor launch_flavor, +                                           MainLoop &main_loop) { +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  error = StartExceptionThread(); +  if (!error.Success()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): failure starting the " +              "mach exception port monitor thread: %s", +              __FUNCTION__, error.AsCString()); + +    // Terminate the inferior process.  There's nothing meaningful we can do if +    // we can't receive signals and exceptions.  Since we launched the process, +    // it's fair game for us to kill it. +    ::ptrace(PT_KILL, m_pid, 0, 0); +    SetState(eStateExited); + +    return error; +  } + +  StartSTDIOThread(); + +  if (launch_flavor == LaunchFlavor::PosixSpawn) { +    SetState(eStateAttaching); +    errno = 0; +    int err = ::ptrace(PT_ATTACHEXC, m_pid, 0, 0); +    if (err == 0) { +      // m_flags |= eMachProcessFlagsAttached; +      LLDB_LOGF(log, +                "NativeProcessDarwin::%s(): successfully spawned " +                "process with pid %" PRIu64, +                __FUNCTION__, m_pid); +    } else { +      error.SetErrorToErrno(); +      SetState(eStateExited); +      LLDB_LOGF(log, +                "NativeProcessDarwin::%s(): error: failed to " +                "attach to spawned pid %" PRIu64 " (error=%d (%s))", +                __FUNCTION__, m_pid, (int)error.GetError(), error.AsCString()); +      return error; +    } +  } + +  LLDB_LOGF(log, "NativeProcessDarwin::%s(): new pid is %" PRIu64 "...", +            __FUNCTION__, m_pid); + +  // Spawn a thread to reap our child inferior process... +  error = StartWaitpidThread(main_loop); +  if (error.Fail()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): failed to start waitpid() " +              "thread: %s", +              __FUNCTION__, error.AsCString()); +    kill(SIGKILL, static_cast<::pid_t>(m_pid)); +    return error; +  } + +  if (TaskPortForProcessID(error) == TASK_NULL) { +    // We failed to get the task for our process ID which is bad. Kill our +    // process; otherwise, it will be stopped at the entry point and get +    // reparented to someone else and never go away. +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): could not get task port " +              "for process, sending SIGKILL and exiting: %s", +              __FUNCTION__, error.AsCString()); +    kill(SIGKILL, static_cast<::pid_t>(m_pid)); +    return error; +  } + +  // Indicate that we're stopped, as we always launch suspended. +  SetState(eStateStopped); + +  // Success. +  return error; +} + +Status NativeProcessDarwin::SaveExceptionPortInfo() { +  return m_exc_port_info.Save(m_task); +} + +bool NativeProcessDarwin::ProcessUsingSpringBoard() const { +  // TODO implement flags +  // return (m_flags & eMachProcessFlagsUsingSBS) != 0; +  return false; +} + +bool NativeProcessDarwin::ProcessUsingBackBoard() const { +  // TODO implement flags +  // return (m_flags & eMachProcessFlagsUsingBKS) != 0; +  return false; +} + +// Called by the exception thread when an exception has been received from our +// process. The exception message is completely filled and the exception data +// has already been copied. +void NativeProcessDarwin::ExceptionMessageReceived( +    const MachException::Message &message) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); + +  std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex); +  if (m_exception_messages.empty()) { +    // Suspend the task the moment we receive our first exception message. +    SuspendTask(); +  } + +  // Use a locker to automatically unlock our mutex in case of exceptions Add +  // the exception to our internal exception stack +  m_exception_messages.push_back(message); + +  LLDB_LOGF(log, "NativeProcessDarwin::%s(): new queued message count: %lu", +            __FUNCTION__, m_exception_messages.size()); +} + +void *NativeProcessDarwin::ExceptionThread(void *arg) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); +  if (!arg) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): cannot run mach exception " +              "thread, mandatory process arg was null", +              __FUNCTION__); +    return nullptr; +  } + +  return reinterpret_cast<NativeProcessDarwin *>(arg)->DoExceptionThread(); +} + +void *NativeProcessDarwin::DoExceptionThread() { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); + +  LLDB_LOGF(log, "NativeProcessDarwin::%s(arg=%p) starting thread...", +            __FUNCTION__, this); + +  pthread_setname_np("exception monitoring thread"); + +  // Ensure we don't get CPU starved. +  MaybeRaiseThreadPriority(); + +  // We keep a count of the number of consecutive exceptions received so we +  // know to grab all exceptions without a timeout. We do this to get a bunch +  // of related exceptions on our exception port so we can process then +  // together. When we have multiple threads, we can get an exception per +  // thread and they will come in consecutively. The main loop in this thread +  // can stop periodically if needed to service things related to this process. +  // +  // [did we lose some words here?] +  // +  // flag set in the options, so we will wait forever for an exception on +  // 0 our exception port. After we get one exception, we then will use the +  // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current +  // exceptions for our process. After we have received the last pending +  // exception, we will get a timeout which enables us to then notify our main +  // thread that we have an exception bundle available. We then wait for the +  // main thread to tell this exception thread to start trying to get +  // exceptions messages again and we start again with a mach_msg read with +  // infinite timeout. +  // +  // We choose to park a thread on this, rather than polling, because the +  // polling is expensive.  On devices, we need to minimize overhead caused by +  // the process monitor. +  uint32_t num_exceptions_received = 0; +  Status error; +  task_t task = m_task; +  mach_msg_timeout_t periodic_timeout = 0; + +#if defined(WITH_SPRINGBOARD) && !defined(WITH_BKS) +  mach_msg_timeout_t watchdog_elapsed = 0; +  mach_msg_timeout_t watchdog_timeout = 60 * 1000; +  ::pid_t pid = (::pid_t)process->GetID(); +  CFReleaser<SBSWatchdogAssertionRef> watchdog; + +  if (process->ProcessUsingSpringBoard()) { +    // Request a renewal for every 60 seconds if we attached using SpringBoard. +    watchdog.reset(::SBSWatchdogAssertionCreateForPID(nullptr, pid, 60)); +    LLDB_LOGF(log, +              "::SBSWatchdogAssertionCreateForPID(NULL, %4.4x, 60) " +              "=> %p", +              pid, watchdog.get()); + +    if (watchdog.get()) { +      ::SBSWatchdogAssertionRenew(watchdog.get()); + +      CFTimeInterval watchdogRenewalInterval = +          ::SBSWatchdogAssertionGetRenewalInterval(watchdog.get()); +      LLDB_LOGF(log, +                "::SBSWatchdogAssertionGetRenewalInterval(%p) => " +                "%g seconds", +                watchdog.get(), watchdogRenewalInterval); +      if (watchdogRenewalInterval > 0.0) { +        watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000; +        if (watchdog_timeout > 3000) { +          // Give us a second to renew our timeout. +          watchdog_timeout -= 1000; +        } else if (watchdog_timeout > 1000) { +          // Give us a quarter of a second to renew our timeout. +          watchdog_timeout -= 250; +        } +      } +    } +    if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout) +      periodic_timeout = watchdog_timeout; +  } +#endif // #if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS) + +#ifdef WITH_BKS +  CFReleaser<BKSWatchdogAssertionRef> watchdog; +  if (process->ProcessUsingBackBoard()) { +    ::pid_t pid = process->GetID(); +    CFAllocatorRef alloc = kCFAllocatorDefault; +    watchdog.reset(::BKSWatchdogAssertionCreateForPID(alloc, pid)); +  } +#endif // #ifdef WITH_BKS + +  // Do we want to use a weak pointer to the NativeProcessDarwin here, in which +  // case we can guarantee we don't whack the process monitor if we race +  // between this thread and the main one on shutdown? +  while (IsExceptionPortValid()) { +    ::pthread_testcancel(); + +    MachException::Message exception_message; + +    if (num_exceptions_received > 0) { +      // We don't want a timeout here, just receive as many exceptions as we +      // can since we already have one.  We want to get all currently available +      // exceptions for this task at once. +      error = exception_message.Receive( +          GetExceptionPort(), +          MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0); +    } else if (periodic_timeout > 0) { +      // We need to stop periodically in this loop, so try and get a mach +      // message with a valid timeout (ms). +      error = exception_message.Receive(GetExceptionPort(), +                                        MACH_RCV_MSG | MACH_RCV_INTERRUPT | +                                            MACH_RCV_TIMEOUT, +                                        periodic_timeout); +    } else { +      // We don't need to parse all current exceptions or stop periodically, +      // just wait for an exception forever. +      error = exception_message.Receive(GetExceptionPort(), +                                        MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0); +    } + +    if (error.Success()) { +      // We successfully received an exception. +      if (exception_message.CatchExceptionRaise(task)) { +        ++num_exceptions_received; +        ExceptionMessageReceived(exception_message); +      } +    } else { +      if (error.GetError() == MACH_RCV_INTERRUPTED) { +        // We were interrupted. + +        // If we have no task port we should exit this thread, as it implies +        // the inferior went down. +        if (!IsExceptionPortValid()) { +          LLDB_LOGF(log, +                    "NativeProcessDarwin::%s(): the inferior " +                    "exception port is no longer valid, " +                    "canceling exception thread...", +                    __FUNCTION__); +          // Should we be setting a process state here? +          break; +        } + +        // Make sure the inferior task is still valid. +        if (IsTaskValid()) { +          // Task is still ok. +          LLDB_LOGF(log, +                    "NativeProcessDarwin::%s(): interrupted, but " +                    "the inferior task iss till valid, " +                    "continuing...", +                    __FUNCTION__); +          continue; +        } else { +          // The inferior task is no longer valid.  Time to exit as the process +          // has gone away. +          LLDB_LOGF(log, +                    "NativeProcessDarwin::%s(): the inferior task " +                    "has exited, and so will we...", +                    __FUNCTION__); +          // Does this race at all with our waitpid()? +          SetState(eStateExited); +          break; +        } +      } else if (error.GetError() == MACH_RCV_TIMED_OUT) { +        // We timed out when waiting for exceptions. + +        if (num_exceptions_received > 0) { +          // We were receiving all current exceptions with a timeout of zero. +          // It is time to go back to our normal looping mode. +          num_exceptions_received = 0; + +          // Notify our main thread we have a complete exception message bundle +          // available.  Get the possibly updated task port back from the +          // process in case we exec'ed and our task port changed. +          task = ExceptionMessageBundleComplete(); + +          // In case we use a timeout value when getting exceptions, make sure +          // our task is still valid. +          if (IsTaskValid(task)) { +            // Task is still ok. +            LLDB_LOGF(log, +                      "NativeProcessDarwin::%s(): got a timeout, " +                      "continuing...", +                      __FUNCTION__); +            continue; +          } else { +            // The inferior task is no longer valid.  Time to exit as the +            // process has gone away. +            LLDB_LOGF(log, +                      "NativeProcessDarwin::%s(): the inferior " +                      "task has exited, and so will we...", +                      __FUNCTION__); +            // Does this race at all with our waitpid()? +            SetState(eStateExited); +            break; +          } +        } + +#if defined(WITH_SPRINGBOARD) && !defined(WITH_BKS) +        if (watchdog.get()) { +          watchdog_elapsed += periodic_timeout; +          if (watchdog_elapsed >= watchdog_timeout) { +            LLDB_LOGF(log, "SBSWatchdogAssertionRenew(%p)", watchdog.get()); +            ::SBSWatchdogAssertionRenew(watchdog.get()); +            watchdog_elapsed = 0; +          } +        } +#endif +      } else { +        LLDB_LOGF(log, +                  "NativeProcessDarwin::%s(): continuing after " +                  "receiving an unexpected error: %u (%s)", +                  __FUNCTION__, error.GetError(), error.AsCString()); +        // TODO: notify of error? +      } +    } +  } + +#if defined(WITH_SPRINGBOARD) && !defined(WITH_BKS) +  if (watchdog.get()) { +    // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel +    // when we +    // all are up and running on systems that support it. The SBS framework has +    // a #define that will forward SBSWatchdogAssertionRelease to +    // SBSWatchdogAssertionCancel for now so it should still build either way. +    DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", +                     watchdog.get()); +    ::SBSWatchdogAssertionRelease(watchdog.get()); +  } +#endif // #if defined (WITH_SPRINGBOARD) && !defined (WITH_BKS) + +  LLDB_LOGF(log, "NativeProcessDarwin::%s(%p): thread exiting...", __FUNCTION__, +            this); +  return nullptr; +} + +Status NativeProcessDarwin::StartExceptionThread() { +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); +  LLDB_LOGF(log, "NativeProcessDarwin::%s() called", __FUNCTION__); + +  // Make sure we've looked up the inferior port. +  TaskPortForProcessID(error); + +  // Ensure the inferior task is valid. +  if (!IsTaskValid()) { +    error.SetErrorStringWithFormat("cannot start exception thread: " +                                   "task 0x%4.4x is not valid", +                                   m_task); +    return error; +  } + +  // Get the mach port for the process monitor. +  mach_port_t task_self = mach_task_self(); + +  // Allocate an exception port that we will use to track our child process +  auto mach_err = ::mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE, +                                       &m_exception_port); +  error.SetError(mach_err, eErrorTypeMachKernel); +  if (error.Fail()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): mach_port_allocate(" +              "task_self=0x%4.4x, MACH_PORT_RIGHT_RECEIVE, " +              "&m_exception_port) failed: %u (%s)", +              __FUNCTION__, task_self, error.GetError(), error.AsCString()); +    return error; +  } + +  // Add the ability to send messages on the new exception port +  mach_err = ::mach_port_insert_right( +      task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND); +  error.SetError(mach_err, eErrorTypeMachKernel); +  if (error.Fail()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): mach_port_insert_right(" +              "task_self=0x%4.4x, m_exception_port=0x%4.4x, " +              "m_exception_port=0x%4.4x, MACH_MSG_TYPE_MAKE_SEND) " +              "failed: %u (%s)", +              __FUNCTION__, task_self, m_exception_port, m_exception_port, +              error.GetError(), error.AsCString()); +    return error; +  } + +  // Save the original state of the exception ports for our child process. +  error = SaveExceptionPortInfo(); +  if (error.Fail() || (m_exc_port_info.mask == 0)) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): SaveExceptionPortInfo() " +              "failed, cannot install exception handler: %s", +              __FUNCTION__, error.AsCString()); +    return error; +  } + +  // Set the ability to get all exceptions on this port. +  mach_err = ::task_set_exception_ports( +      m_task, m_exc_port_info.mask, m_exception_port, +      EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); +  error.SetError(mach_err, eErrorTypeMachKernel); +  if (error.Fail()) { +    LLDB_LOGF(log, +              "::task_set_exception_ports (task = 0x%4.4x, " +              "exception_mask = 0x%8.8x, new_port = 0x%4.4x, " +              "behavior = 0x%8.8x, new_flavor = 0x%8.8x) failed: " +              "%u (%s)", +              m_task, m_exc_port_info.mask, m_exception_port, +              (EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES), THREAD_STATE_NONE, +              error.GetError(), error.AsCString()); +    return error; +  } + +  // Create the exception thread. +  auto pthread_err = +      ::pthread_create(&m_exception_thread, nullptr, ExceptionThread, this); +  error.SetError(pthread_err, eErrorTypePOSIX); +  if (error.Fail()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): failed to create Mach " +              "exception-handling thread: %u (%s)", +              __FUNCTION__, error.GetError(), error.AsCString()); +  } + +  return error; +} + +lldb::addr_t +NativeProcessDarwin::GetDYLDAllImageInfosAddress(Status &error) const { +  error.Clear(); + +  struct hack_task_dyld_info dyld_info; +  mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; +  // Make sure that COUNT isn't bigger than our hacked up struct +  // hack_task_dyld_info.  If it is, then make COUNT smaller to match. +  if (count > (sizeof(struct hack_task_dyld_info) / sizeof(natural_t))) { +    count = (sizeof(struct hack_task_dyld_info) / sizeof(natural_t)); +  } + +  TaskPortForProcessID(error); +  if (error.Fail()) +    return LLDB_INVALID_ADDRESS; + +  auto mach_err = +      ::task_info(m_task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count); +  error.SetError(mach_err, eErrorTypeMachKernel); +  if (error.Success()) { +    // We now have the address of the all image infos structure. +    return dyld_info.all_image_info_addr; +  } + +  // We don't have it. +  return LLDB_INVALID_ADDRESS; +} + +uint32_t NativeProcessDarwin::GetCPUTypeForLocalProcess(::pid_t pid) { +  int mib[CTL_MAXNAME] = { +      0, +  }; +  size_t len = CTL_MAXNAME; + +  if (::sysctlnametomib("sysctl.proc_cputype", mib, &len)) +    return 0; + +  mib[len] = pid; +  len++; + +  cpu_type_t cpu; +  size_t cpu_len = sizeof(cpu); +  if (::sysctl(mib, static_cast<u_int>(len), &cpu, &cpu_len, 0, 0)) +    cpu = 0; +  return cpu; +} + +uint32_t NativeProcessDarwin::GetCPUType() const { +  if (m_cpu_type == 0 && m_pid != 0) +    m_cpu_type = GetCPUTypeForLocalProcess(m_pid); +  return m_cpu_type; +} + +task_t NativeProcessDarwin::ExceptionMessageBundleComplete() { +  // We have a complete bundle of exceptions for our child process. +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); + +  std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex); +  LLDB_LOGF(log, +            "NativeProcessDarwin::%s(): processing %lu exception " +            "messages.", +            __FUNCTION__, m_exception_messages.size()); + +  if (m_exception_messages.empty()) { +    // Not particularly useful... +    return m_task; +  } + +  bool auto_resume = false; +  m_did_exec = false; + +  // First check for any SIGTRAP and make sure we didn't exec +  const task_t task = m_task; +  size_t i; +  if (m_pid != 0) { +    bool received_interrupt = false; +    uint32_t num_task_exceptions = 0; +    for (i = 0; i < m_exception_messages.size(); ++i) { +      if (m_exception_messages[i].state.task_port != task) { +        // This is an exception that is not for our inferior, ignore. +        continue; +      } + +      // This is an exception for the inferior. +      ++num_task_exceptions; +      const int signo = m_exception_messages[i].state.SoftSignal(); +      if (signo == SIGTRAP) { +        // SIGTRAP could mean that we exec'ed. We need to check the +        // dyld all_image_infos.infoArray to see if it is NULL and if so, say +        // that we exec'ed. +        const addr_t aii_addr = GetDYLDAllImageInfosAddress(error); +        if (aii_addr == LLDB_INVALID_ADDRESS) +          break; + +        const addr_t info_array_count_addr = aii_addr + 4; +        uint32_t info_array_count = 0; +        size_t bytes_read = 0; +        Status read_error; +        read_error = ReadMemory(info_array_count_addr, // source addr +                                &info_array_count,     // dest addr +                                4,                     // byte count +                                bytes_read);           // #bytes read +        if (read_error.Success() && (bytes_read == 4)) { +          if (info_array_count == 0) { +            // We got the all infos address, and there are zero entries.  We +            // think we exec'd. +            m_did_exec = true; + +            // Force the task port to update itself in case the task port +            // changed after exec +            const task_t old_task = m_task; +            const bool force_update = true; +            const task_t new_task = TaskPortForProcessID(error, force_update); +            if (old_task != new_task) { +              LLDB_LOGF(log, +                        "exec: inferior task port changed " +                        "from 0x%4.4x to 0x%4.4x", +                        old_task, new_task); +            } +          } +        } else { +          LLDB_LOGF(log, +                    "NativeProcessDarwin::%s() warning: " +                    "failed to read all_image_infos." +                    "infoArrayCount from 0x%8.8llx", +                    __FUNCTION__, info_array_count_addr); +        } +      } else if ((m_sent_interrupt_signo != 0) && +                 (signo == m_sent_interrupt_signo)) { +        // We just received the interrupt that we sent to ourselves. +        received_interrupt = true; +      } +    } + +    if (m_did_exec) { +      cpu_type_t process_cpu_type = GetCPUTypeForLocalProcess(m_pid); +      if (m_cpu_type != process_cpu_type) { +        LLDB_LOGF(log, +                  "NativeProcessDarwin::%s(): arch changed from " +                  "0x%8.8x to 0x%8.8x", +                  __FUNCTION__, m_cpu_type, process_cpu_type); +        m_cpu_type = process_cpu_type; +        // TODO figure out if we need to do something here. +        // DNBArchProtocol::SetArchitecture (process_cpu_type); +      } +      m_thread_list.Clear(); + +      // TODO hook up breakpoints. +      // m_breakpoints.DisableAll(); +    } + +    if (m_sent_interrupt_signo != 0) { +      if (received_interrupt) { +        LLDB_LOGF(log, +                  "NativeProcessDarwin::%s(): process " +                  "successfully interrupted with signal %i", +                  __FUNCTION__, m_sent_interrupt_signo); + +        // Mark that we received the interrupt signal +        m_sent_interrupt_signo = 0; +        // Now check if we had a case where: +        // 1 - We called NativeProcessDarwin::Interrupt() but we stopped +        //     for another reason. +        // 2 - We called NativeProcessDarwin::Resume() (but still +        //     haven't gotten the interrupt signal). +        // 3 - We are now incorrectly stopped because we are handling +        //     the interrupt signal we missed. +        // 4 - We might need to resume if we stopped only with the +        //     interrupt signal that we never handled. +        if (m_auto_resume_signo != 0) { +          // Only auto_resume if we stopped with _only_ the interrupt signal. +          if (num_task_exceptions == 1) { +            auto_resume = true; +            LLDB_LOGF(log, +                      "NativeProcessDarwin::%s(): auto " +                      "resuming due to unhandled interrupt " +                      "signal %i", +                      __FUNCTION__, m_auto_resume_signo); +          } +          m_auto_resume_signo = 0; +        } +      } else { +        LLDB_LOGF(log, +                  "NativeProcessDarwin::%s(): didn't get signal " +                  "%i after MachProcess::Interrupt()", +                  __FUNCTION__, m_sent_interrupt_signo); +      } +    } +  } + +  // Let all threads recover from stopping and do any clean up based on the +  // previous thread state (if any). +  m_thread_list.ProcessDidStop(*this); + +  // Let each thread know of any exceptions +  for (i = 0; i < m_exception_messages.size(); ++i) { +    // Let the thread list forward all exceptions on down to each thread. +    if (m_exception_messages[i].state.task_port == task) { +      // This exception is for our inferior. +      m_thread_list.NotifyException(m_exception_messages[i].state); +    } + +    if (log) { +      StreamString stream; +      m_exception_messages[i].Dump(stream); +      stream.Flush(); +      log->PutCString(stream.GetString().c_str()); +    } +  } + +  if (log) { +    StreamString stream; +    m_thread_list.Dump(stream); +    stream.Flush(); +    log->PutCString(stream.GetString().c_str()); +  } + +  bool step_more = false; +  if (m_thread_list.ShouldStop(step_more) && (auto_resume == false)) { +// TODO - need to hook up event system here. !!!! +#if 0 +        // Wait for the eEventProcessRunningStateChanged event to be reset +        // before changing state to stopped to avoid race condition with very +        // fast start/stops. +        struct timespec timeout; + +        //DNBTimer::OffsetTimeOfDay(&timeout, 0, 250 * 1000);   // Wait for 250 ms +        DNBTimer::OffsetTimeOfDay(&timeout, 1, 0);  // Wait for 250 ms +        m_events.WaitForEventsToReset(eEventProcessRunningStateChanged, +                                      &timeout); +#endif +    SetState(eStateStopped); +  } else { +    // Resume without checking our current state. +    PrivateResume(); +  } + +  return m_task; +} + +void NativeProcessDarwin::StartSTDIOThread() { +  // TODO implement +} + +Status NativeProcessDarwin::StartWaitpidThread(MainLoop &main_loop) { +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  // Strategy: create a thread that sits on waitpid(), waiting for the inferior +  // process to die, reaping it in the process.  Arrange for the thread to have +  // a pipe file descriptor that it can send a byte over when the waitpid +  // completes.  Have the main loop have a read object for the other side of +  // the pipe, and have the callback for the read do the process termination +  // message sending. + +  // Create a single-direction communication channel. +  const bool child_inherits = false; +  error = m_waitpid_pipe.CreateNew(child_inherits); +  if (error.Fail()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): failed to create waitpid " +              "communication pipe: %s", +              __FUNCTION__, error.AsCString()); +    return error; +  } + +  // Hook up the waitpid reader callback. + +  // TODO make PipePOSIX derive from IOObject.  This is goofy here. +  const bool transfer_ownership = false; +  auto io_sp = IOObjectSP(new NativeFile(m_waitpid_pipe.GetReadFileDescriptor(), +                                         transfer_ownership)); +  m_waitpid_reader_handle = main_loop.RegisterReadObject( +      io_sp, [this](MainLoopBase &) { HandleWaitpidResult(); }, error); + +  // Create the thread. +  auto pthread_err = +      ::pthread_create(&m_waitpid_thread, nullptr, WaitpidThread, this); +  error.SetError(pthread_err, eErrorTypePOSIX); +  if (error.Fail()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): failed to create waitpid " +              "handling thread: %u (%s)", +              __FUNCTION__, error.GetError(), error.AsCString()); +    return error; +  } + +  return error; +} + +void *NativeProcessDarwin::WaitpidThread(void *arg) { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); +  if (!arg) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): cannot run waitpid " +              "thread, mandatory process arg was null", +              __FUNCTION__); +    return nullptr; +  } + +  return reinterpret_cast<NativeProcessDarwin *>(arg)->DoWaitpidThread(); +} + +void NativeProcessDarwin::MaybeRaiseThreadPriority() { +#if defined(__arm__) || defined(__arm64__) || defined(__aarch64__) +  struct sched_param thread_param; +  int thread_sched_policy; +  if (pthread_getschedparam(pthread_self(), &thread_sched_policy, +                            &thread_param) == 0) { +    thread_param.sched_priority = 47; +    pthread_setschedparam(pthread_self(), thread_sched_policy, &thread_param); +  } +#endif +} + +void *NativeProcessDarwin::DoWaitpidThread() { +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  if (m_pid == LLDB_INVALID_PROCESS_ID) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): inferior process ID is " +              "not set, cannot waitpid on it", +              __FUNCTION__); +    return nullptr; +  } + +  // Name the thread. +  pthread_setname_np("waitpid thread"); + +  // Ensure we don't get CPU starved. +  MaybeRaiseThreadPriority(); + +  Status error; +  int status = -1; + +  while (1) { +    // Do a waitpid. +    ::pid_t child_pid = ::waitpid(m_pid, &status, 0); +    if (child_pid < 0) +      error.SetErrorToErrno(); +    if (error.Fail()) { +      if (error.GetError() == EINTR) { +        // This is okay, we can keep going. +        LLDB_LOGF(log, +                  "NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64 +                  ", &status, 0) interrupted, continuing", +                  __FUNCTION__, m_pid); +        continue; +      } + +      // This error is not okay, abort. +      LLDB_LOGF(log, +                "NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64 +                ", &status, 0) aborting due to error: %u (%s)", +                __FUNCTION__, m_pid, error.GetError(), error.AsCString()); +      break; +    } + +    // Log the successful result. +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64 +              ", &status, 0) => %i, status = %i", +              __FUNCTION__, m_pid, child_pid, status); + +    // Handle the result. +    if (WIFSTOPPED(status)) { +      LLDB_LOGF(log, +                "NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64 +                ") received a stop, continuing waitpid() loop", +                __FUNCTION__, m_pid); +      continue; +    } else // if (WIFEXITED(status) || WIFSIGNALED(status)) +    { +      LLDB_LOGF(log, +                "NativeProcessDarwin::%s(pid = %" PRIu64 "): " +                "waitpid thread is setting exit status for pid = " +                "%i to %i", +                __FUNCTION__, m_pid, child_pid, status); + +      error = SendInferiorExitStatusToMainLoop(child_pid, status); +      return nullptr; +    } +  } + +  // We should never exit as long as our child process is alive.  If we get +  // here, something completely unexpected went wrong and we should exit. +  LLDB_LOGF(log, +            "NativeProcessDarwin::%s(): internal error: waitpid thread " +            "exited out of its main loop in an unexpected way. pid = %" PRIu64 +            ". Sending exit status of -1.", +            __FUNCTION__, m_pid); + +  error = SendInferiorExitStatusToMainLoop((::pid_t)m_pid, -1); +  return nullptr; +} + +Status NativeProcessDarwin::SendInferiorExitStatusToMainLoop(::pid_t pid, +                                                             int status) { +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  size_t bytes_written = 0; + +  // Send the pid. +  error = m_waitpid_pipe.Write(&pid, sizeof(pid), bytes_written); +  if (error.Fail() || (bytes_written < sizeof(pid))) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s() - failed to write " +              "waitpid exiting pid to the pipe.  Client will not " +              "hear about inferior exit status!", +              __FUNCTION__); +    return error; +  } + +  // Send the status. +  bytes_written = 0; +  error = m_waitpid_pipe.Write(&status, sizeof(status), bytes_written); +  if (error.Fail() || (bytes_written < sizeof(status))) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s() - failed to write " +              "waitpid exit result to the pipe.  Client will not " +              "hear about inferior exit status!", +              __FUNCTION__); +  } +  return error; +} + +Status NativeProcessDarwin::HandleWaitpidResult() { +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  // Read the pid. +  const bool notify_status = true; + +  ::pid_t pid = -1; +  size_t bytes_read = 0; +  error = m_waitpid_pipe.Read(&pid, sizeof(pid), bytes_read); +  if (error.Fail() || (bytes_read < sizeof(pid))) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s() - failed to read " +              "waitpid exiting pid from the pipe.  Will notify " +              "as if parent process died with exit status -1.", +              __FUNCTION__); +    SetExitStatus(WaitStatus(WaitStatus::Exit, -1), notify_status); +    return error; +  } + +  // Read the status. +  int status = -1; +  error = m_waitpid_pipe.Read(&status, sizeof(status), bytes_read); +  if (error.Fail() || (bytes_read < sizeof(status))) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s() - failed to read " +              "waitpid exit status from the pipe.  Will notify " +              "as if parent process died with exit status -1.", +              __FUNCTION__); +    SetExitStatus(WaitStatus(WaitStatus::Exit, -1), notify_status); +    return error; +  } + +  // Notify the monitor that our state has changed. +  LLDB_LOGF(log, +            "NativeProcessDarwin::%s(): main loop received waitpid " +            "exit status info: pid=%i (%s), status=%i", +            __FUNCTION__, pid, +            (pid == m_pid) ? "the inferior" : "not the inferior", status); + +  SetExitStatus(WaitStatus::Decode(status), notify_status); +  return error; +} + +task_t NativeProcessDarwin::TaskPortForProcessID(Status &error, +                                                 bool force) const { +  if ((m_task == TASK_NULL) || force) { +    Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); +    if (m_pid == LLDB_INVALID_PROCESS_ID) { +      LLDB_LOGF(log, +                "NativeProcessDarwin::%s(): cannot get task due " +                "to invalid pid", +                __FUNCTION__); +      return TASK_NULL; +    } + +    const uint32_t num_retries = 10; +    const uint32_t usec_interval = 10000; + +    mach_port_t task_self = mach_task_self(); +    task_t task = TASK_NULL; + +    for (uint32_t i = 0; i < num_retries; i++) { +      kern_return_t err = ::task_for_pid(task_self, m_pid, &task); +      if (err == 0) { +        // Succeeded.  Save and return it. +        error.Clear(); +        m_task = task; +        LLDB_LOGF(log, +                  "NativeProcessDarwin::%s(): ::task_for_pid(" +                  "stub_port = 0x%4.4x, pid = %llu, &task) " +                  "succeeded: inferior task port = 0x%4.4x", +                  __FUNCTION__, task_self, m_pid, m_task); +        return m_task; +      } else { +        // Failed to get the task for the inferior process. +        error.SetError(err, eErrorTypeMachKernel); +        if (log) { +          LLDB_LOGF(log, +                    "NativeProcessDarwin::%s(): ::task_for_pid(" +                    "stub_port = 0x%4.4x, pid = %llu, &task) " +                    "failed, err = 0x%8.8x (%s)", +                    __FUNCTION__, task_self, m_pid, err, error.AsCString()); +        } +      } + +      // Sleep a bit and try again +      ::usleep(usec_interval); +    } + +    // We failed to get the task for the inferior process. Ensure that it is +    // cleared out. +    m_task = TASK_NULL; +  } +  return m_task; +} + +void NativeProcessDarwin::AttachToInferior(MainLoop &mainloop, lldb::pid_t pid, +                                           Status &error) { +  error.SetErrorString("TODO: implement"); +} + +Status NativeProcessDarwin::PrivateResume() { +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex); +  m_auto_resume_signo = m_sent_interrupt_signo; + +  if (log) { +    if (m_auto_resume_signo) +      LLDB_LOGF(log, +                "NativeProcessDarwin::%s(): task 0x%x resuming (with " +                "unhandled interrupt signal %i)...", +                __FUNCTION__, m_task, m_auto_resume_signo); +    else +      LLDB_LOGF(log, "NativeProcessDarwin::%s(): task 0x%x resuming...", +                __FUNCTION__, m_task); +  } + +  error = ReplyToAllExceptions(); +  if (error.Fail()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): aborting, failed to " +              "reply to exceptions: %s", +              __FUNCTION__, error.AsCString()); +    return error; +  } +  //    bool stepOverBreakInstruction = step; + +  // Let the thread prepare to resume and see if any threads want us to step +  // over a breakpoint instruction (ProcessWillResume will modify the value of +  // stepOverBreakInstruction). +  m_thread_list.ProcessWillResume(*this, m_thread_actions); + +  // Set our state accordingly +  if (m_thread_actions.NumActionsWithState(eStateStepping)) +    SetState(eStateStepping); +  else +    SetState(eStateRunning); + +  // Now resume our task. +  error = ResumeTask(); +  return error; +} + +Status NativeProcessDarwin::ReplyToAllExceptions() { +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); + +  TaskPortForProcessID(error); +  if (error.Fail()) { +    LLDB_LOGF(log, "NativeProcessDarwin::%s(): no task port, aborting", +              __FUNCTION__); +    return error; +  } + +  std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex); +  if (m_exception_messages.empty()) { +    // We're done. +    return error; +  } + +  size_t index = 0; +  for (auto &message : m_exception_messages) { +    if (log) { +      LLDB_LOGF(log, +                "NativeProcessDarwin::%s(): replying to exception " +                "%zu...", +                __FUNCTION__, index++); +    } + +    int thread_reply_signal = 0; + +    const tid_t tid = +        m_thread_list.GetThreadIDByMachPortNumber(message.state.thread_port); +    const ResumeAction *action = nullptr; +    if (tid != LLDB_INVALID_THREAD_ID) +      action = m_thread_actions.GetActionForThread(tid, false); + +    if (action) { +      thread_reply_signal = action->signal; +      if (thread_reply_signal) +        m_thread_actions.SetSignalHandledForThread(tid); +    } + +    error = message.Reply(m_pid, m_task, thread_reply_signal); +    if (error.Fail() && log) { +      // We log any error here, but we don't stop the exception response +      // handling. +      LLDB_LOGF(log, +                "NativeProcessDarwin::%s(): failed to reply to " +                "exception: %s", +                __FUNCTION__, error.AsCString()); +      error.Clear(); +    } +  } + +  // Erase all exception message as we should have used and replied to them all +  // already. +  m_exception_messages.clear(); +  return error; +} + +Status NativeProcessDarwin::ResumeTask() { +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  TaskPortForProcessID(error); +  if (error.Fail()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): failed to get task port " +              "for process when attempting to resume: %s", +              __FUNCTION__, error.AsCString()); +    return error; +  } +  if (m_task == TASK_NULL) { +    error.SetErrorString("task port retrieval succeeded but task port is " +                         "null when attempting to resume the task"); +    return error; +  } + +  LLDB_LOGF(log, +            "NativeProcessDarwin::%s(): requesting resume of task " +            "0x%4.4x", +            __FUNCTION__, m_task); + +  // Get the BasicInfo struct to verify that we're suspended before we try to +  // resume the task. +  struct task_basic_info task_info; +  error = GetTaskBasicInfo(m_task, &task_info); +  if (error.Fail()) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): failed to get task " +              "BasicInfo when attempting to resume: %s", +              __FUNCTION__, error.AsCString()); +    return error; +  } + +  // task_resume isn't counted like task_suspend calls are, so if the task is +  // not suspended, don't try and resume it since it is already running +  if (task_info.suspend_count > 0) { +    auto mach_err = ::task_resume(m_task); +    error.SetError(mach_err, eErrorTypeMachKernel); +    if (log) { +      if (error.Success()) +        LLDB_LOGF(log, "::task_resume(target_task = 0x%4.4x): success", m_task); +      else +        LLDB_LOGF(log, "::task_resume(target_task = 0x%4.4x) error: %s", m_task, +                  error.AsCString()); +    } +  } else { +    LLDB_LOGF(log, +              "::task_resume(target_task = 0x%4.4x): ignored, " +              "already running", +              m_task); +  } + +  return error; +} + +bool NativeProcessDarwin::IsTaskValid() const { +  if (m_task == TASK_NULL) +    return false; + +  struct task_basic_info task_info; +  return GetTaskBasicInfo(m_task, &task_info).Success(); +} + +bool NativeProcessDarwin::IsTaskValid(task_t task) const { +  if (task == TASK_NULL) +    return false; + +  struct task_basic_info task_info; +  return GetTaskBasicInfo(task, &task_info).Success(); +} + +mach_port_t NativeProcessDarwin::GetExceptionPort() const { +  return m_exception_port; +} + +bool NativeProcessDarwin::IsExceptionPortValid() const { +  return MACH_PORT_VALID(m_exception_port); +} + +Status +NativeProcessDarwin::GetTaskBasicInfo(task_t task, +                                      struct task_basic_info *info) const { +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  // Validate args. +  if (info == NULL) { +    error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): mandatory " +                                   "info arg is null", +                                   __FUNCTION__); +    return error; +  } + +  // Grab the task if we don't already have it. +  if (task == TASK_NULL) { +    error.SetErrorStringWithFormat("NativeProcessDarwin::%s(): given task " +                                   "is invalid", +                                   __FUNCTION__); +  } + +  mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT; +  auto err = ::task_info(m_task, TASK_BASIC_INFO, (task_info_t)info, &count); +  error.SetError(err, eErrorTypeMachKernel); +  if (error.Fail()) { +    LLDB_LOGF(log, +              "::task_info(target_task = 0x%4.4x, " +              "flavor = TASK_BASIC_INFO, task_info_out => %p, " +              "task_info_outCnt => %u) failed: %u (%s)", +              m_task, info, count, error.GetError(), error.AsCString()); +    return error; +  } + +  Log *verbose_log( +      GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE)); +  if (verbose_log) { +    float user = (float)info->user_time.seconds + +                 (float)info->user_time.microseconds / 1000000.0f; +    float system = (float)info->user_time.seconds + +                   (float)info->user_time.microseconds / 1000000.0f; +    verbose_LLDB_LOGF(log, +                      "task_basic_info = { suspend_count = %i, " +                      "virtual_size = 0x%8.8llx, resident_size = " +                      "0x%8.8llx, user_time = %f, system_time = %f }", +                      info->suspend_count, (uint64_t)info->virtual_size, +                      (uint64_t)info->resident_size, user, system); +  } +  return error; +} + +Status NativeProcessDarwin::SuspendTask() { +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  if (m_task == TASK_NULL) { +    error.SetErrorString("task port is null, cannot suspend task"); +    LLDB_LOGF(log, "NativeProcessDarwin::%s() failed: %s", __FUNCTION__, +              error.AsCString()); +    return error; +  } + +  auto mach_err = ::task_suspend(m_task); +  error.SetError(mach_err, eErrorTypeMachKernel); +  if (error.Fail() && log) +    LLDB_LOGF(log, "::task_suspend(target_task = 0x%4.4x)", m_task); + +  return error; +} + +Status NativeProcessDarwin::Resume(const ResumeActionList &resume_actions) { +  Status error; +  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +  LLDB_LOGF(log, "NativeProcessDarwin::%s() called", __FUNCTION__); + +  if (CanResume()) { +    m_thread_actions = resume_actions; +    error = PrivateResume(); +    return error; +  } + +  auto state = GetState(); +  if (state == eStateRunning) { +    LLDB_LOGF(log, +              "NativeProcessDarwin::%s(): task 0x%x is already " +              "running, ignoring...", +              __FUNCTION__, TaskPortForProcessID(error)); +    return error; +  } + +  // We can't resume from this state. +  error.SetErrorStringWithFormat("task 0x%x has state %s, can't resume", +                                 TaskPortForProcessID(error), +                                 StateAsCString(state)); +  return error; +} + +Status NativeProcessDarwin::Halt() { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +Status NativeProcessDarwin::Detach() { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +Status NativeProcessDarwin::Signal(int signo) { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +Status NativeProcessDarwin::Interrupt() { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +Status NativeProcessDarwin::Kill() { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +Status NativeProcessDarwin::GetMemoryRegionInfo(lldb::addr_t load_addr, +                                                MemoryRegionInfo &range_info) { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +Status NativeProcessDarwin::ReadMemory(lldb::addr_t addr, void *buf, +                                       size_t size, size_t &bytes_read) { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +Status NativeProcessDarwin::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, +                                                  size_t size, +                                                  size_t &bytes_read) { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +Status NativeProcessDarwin::WriteMemory(lldb::addr_t addr, const void *buf, +                                        size_t size, size_t &bytes_written) { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +Status NativeProcessDarwin::AllocateMemory(size_t size, uint32_t permissions, +                                           lldb::addr_t &addr) { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +Status NativeProcessDarwin::DeallocateMemory(lldb::addr_t addr) { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +lldb::addr_t NativeProcessDarwin::GetSharedLibraryInfoAddress() { +  return LLDB_INVALID_ADDRESS; +} + +size_t NativeProcessDarwin::UpdateThreads() { return 0; } + +bool NativeProcessDarwin::GetArchitecture(ArchSpec &arch) const { +  return false; +} + +Status NativeProcessDarwin::SetBreakpoint(lldb::addr_t addr, uint32_t size, +                                          bool hardware) { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +void NativeProcessDarwin::DoStopIDBumped(uint32_t newBumpId) {} + +Status NativeProcessDarwin::GetLoadedModuleFileSpec(const char *module_path, +                                                    FileSpec &file_spec) { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +Status NativeProcessDarwin::GetFileLoadAddress(const llvm::StringRef &file_name, +                                               lldb::addr_t &load_addr) { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +} + +// NativeProcessProtocol protected interface +Status NativeProcessDarwin::GetSoftwareBreakpointTrapOpcode( +    size_t trap_opcode_size_hint, size_t &actual_opcode_size, +    const uint8_t *&trap_opcode_bytes) { +  Status error; +  error.SetErrorString("TODO: implement"); +  return error; +}  | 
