diff options
Diffstat (limited to 'lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp')
-rw-r--r-- | lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp | 1825 |
1 files changed, 1825 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 00000000000..d0eaac3c793 --- /dev/null +++ b/lldb/source/Plugins/Process/Darwin/NativeProcessDarwin.cpp @@ -0,0 +1,1825 @@ +//===-- NativeProcessDarwin.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/ProcessLaunchInfo.h" +#include "lldb/Utility/PseudoTerminal.h" + +#include "CFBundle.h" +#include "CFString.h" +#include "DarwinProcessLauncher.h" + +#include "MachException.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 +// ----------------------------------------------------------------------------- + +Error +NativeProcessProtocol::Launch(ProcessLaunchInfo &launch_info, + NativeProcessProtocol::NativeDelegate + &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &native_process_sp) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + Error error; + + // Verify the working directory is valid if one was specified. + FileSpec working_dir(launch_info.GetWorkingDirectory()); + if (working_dir && + (!working_dir.ResolvePath() || + working_dir.GetFileType() != FileSpec::eFileTypeDirectory)) + { + 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()) + { + if (log) + log->Printf("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) + { + if (log) + log->Printf("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()) + { + if (log) + log->Printf("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; +} + +Error +NativeProcessProtocol::Attach(lldb::pid_t pid, + NativeProcessProtocol::NativeDelegate + &native_delegate, + MainLoop &mainloop, + NativeProcessProtocolSP &native_process_sp) +{ + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf ("NativeProcessDarwin::%s(pid = %" PRIi64 ")", __FUNCTION__, + pid); + + // Retrieve the architecture for the running process. + ArchSpec process_arch; + Error 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 +// ----------------------------------------------------------------------------- + +Error +NativeProcessDarwin::FinalizeLaunch(LaunchFlavor launch_flavor, + MainLoop &main_loop) +{ + Error error; + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + +#if 0 + m_path = path; + size_t i; + char const *arg; + for (i=0; (arg = argv[i]) != NULL; i++) + m_args.push_back(arg); +#endif + + error = StartExceptionThread(); + if (!error.Success()) + { + if (log) + log->Printf("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; + if (log) + log->Printf("NativeProcessDarwin::%s(): successfully spawned " + "process with pid %" PRIu64, __FUNCTION__, m_pid); + } + else + { + error.SetErrorToErrno(); + SetState(eStateExited); + if (log) + log->Printf("NativeProcessDarwin::%s(): error: failed to " + "attach to spawned pid %" PRIu64 " (error=%d (%s))", + __FUNCTION__, m_pid, (int)error.GetError(), + error.AsCString()); + return error; + } + } + + if (log) + log->Printf("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()) + { + if (log) + log->Printf("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. + if (log) + log->Printf("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; +} + +Error +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); + + if (log) + log->Printf("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) + { + if (log) + log->Printf("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)); + + if (log) + log->Printf("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; + Error 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)); + if (log) + log->Printf("::SBSWatchdogAssertionCreateForPID(NULL, %4.4x, 60) " + "=> %p", pid, watchdog.get()); + + if (watchdog.get()) + { + ::SBSWatchdogAssertionRenew (watchdog.get()); + + CFTimeInterval watchdogRenewalInterval = + ::SBSWatchdogAssertionGetRenewalInterval (watchdog.get()); + if (log) + log->Printf("::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()) + { + if (log) + log->Printf("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. + if (log) + log->Printf("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. + if (log) + log->Printf("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. + if (log) + log->Printf("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. + if (log) + log->Printf("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) + { + if (log) + log->Printf("SBSWatchdogAssertionRenew(%p)", + watchdog.get()); + ::SBSWatchdogAssertionRenew (watchdog.get()); + watchdog_elapsed = 0; + } + } +#endif + } + else + { + if (log) + log->Printf("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) + + if (log) + log->Printf("NativeProcessDarwin::%s(%p): thread exiting...", + __FUNCTION__, this); + return nullptr; +} + +Error +NativeProcessDarwin::StartExceptionThread() +{ + Error error; + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (log) + log->Printf("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()) + { + if (log) + log->Printf("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()) + { + if (log) + log->Printf("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)) + { + if (log) + log->Printf("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()) + { + if (log) + log->Printf("::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()) + { + if (log) + log->Printf("NativeProcessDarwin::%s(): failed to create Mach " + "exception-handling thread: %u (%s)", __FUNCTION__, + error.GetError(), error.AsCString()); + } + + return error; +} + +lldb::addr_t +NativeProcessDarwin::GetDYLDAllImageInfosAddress(Error &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. + Error error; + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | + LIBLLDB_LOG_VERBOSE)); + + std::lock_guard<std::recursive_mutex> locker(m_exception_messages_mutex); + if (log) + log->Printf("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; + Error 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) + { + if (log) + log->Printf("exec: inferior task port changed " + "from 0x%4.4x to 0x%4.4x", old_task, + new_task); + } + } + } + else + { + if (log) + log->Printf("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) + { + if (log) + log->Printf("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) + { + if (log) + log->Printf("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; + if (log) + log->Printf("NativeProcessDarwin::%s(): auto " + "resuming due to unhandled interrupt " + "signal %i", __FUNCTION__, + m_auto_resume_signo); + } + m_auto_resume_signo = 0; + } + } + else + { + if (log) + log->Printf("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 +} + +Error +NativeProcessDarwin::StartWaitpidThread(MainLoop &main_loop) +{ + Error 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()) + { + if (log) + log->Printf("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 File(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()) + { + if (log) + log->Printf("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) + { + if (log) + log->Printf("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) + { + if (log) + log->Printf("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(); + + Error 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. + if (log) + log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" + PRIu64 ", &status, 0) interrupted, continuing", + __FUNCTION__, m_pid); + continue; + } + + // This error is not okay, abort. + if (log) + log->Printf("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. + if (log) + log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64 + ", &status, 0) => %i, status = %i", __FUNCTION__, + m_pid, child_pid, status); + + // Handle the result. + if (WIFSTOPPED(status)) + { + if (log) + log->Printf("NativeProcessDarwin::%s(): waitpid(pid = %" PRIu64 + ") received a stop, continuing waitpid() loop", + __FUNCTION__, m_pid); + continue; + } + else // if (WIFEXITED(status) || WIFSIGNALED(status)) + { + if (log) + log->Printf("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. + if (log) + log->Printf("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; +} + +Error +NativeProcessDarwin::SendInferiorExitStatusToMainLoop(::pid_t pid, int status) +{ + Error 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))) + { + if (log) + log->Printf("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))) + { + if (log) + log->Printf("NativeProcessDarwin::%s() - failed to write " + "waitpid exit result to the pipe. Client will not " + "hear about inferior exit status!", + __FUNCTION__); + } + return error; +} + +Error +NativeProcessDarwin::HandleWaitpidResult() +{ + Error 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))) + { + if (log) + log->Printf("NativeProcessDarwin::%s() - failed to read " + "waitpid exiting pid from the pipe. Will notify " + "as if parent process died with exit status -1.", + __FUNCTION__); + SetExitStatus(eExitTypeInvalid, -1, "failed to receive waitpid result", + 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))) + { + if (log) + log->Printf("NativeProcessDarwin::%s() - failed to read " + "waitpid exit status from the pipe. Will notify " + "as if parent process died with exit status -1.", + __FUNCTION__); + SetExitStatus(eExitTypeInvalid, -1, "failed to receive waitpid result", + notify_status); + return error; + } + + // Notify the monitor that our state has changed. + if (log) + log->Printf("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); + + ExitType exit_type = eExitTypeInvalid; + int exit_status = -1; + + if (WIFEXITED(status)) + { + exit_type = eExitTypeExit; + exit_status = WEXITSTATUS(status); + } + else if (WIFSIGNALED(status)) + { + exit_type = eExitTypeSignal; + exit_status = WTERMSIG(status); + } + + SetExitStatus(exit_type, exit_status, nullptr, notify_status); + return error; +} + +task_t +NativeProcessDarwin::TaskPortForProcessID(Error &error, bool force) const +{ + if ((m_task == TASK_NULL) || force) + { + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + if (m_pid == LLDB_INVALID_PROCESS_ID) + { + if (log) + log->Printf("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; + log->Printf("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) + { + log->Printf("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, + Error &error) +{ + error.SetErrorString("TODO: implement"); +} + +Error +NativeProcessDarwin::PrivateResume() +{ + Error 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) + log->Printf("NativeProcessDarwin::%s(): task 0x%x resuming (with " + "unhandled interrupt signal %i)...", __FUNCTION__, + m_task, m_auto_resume_signo); + else + log->Printf("NativeProcessDarwin::%s(): task 0x%x resuming...", + __FUNCTION__, m_task); + } + + error = ReplyToAllExceptions(); + if (error.Fail()) + { + if (log) + log->Printf("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; +} + +Error +NativeProcessDarwin::ReplyToAllExceptions() +{ + Error error; + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS | + LIBLLDB_LOG_VERBOSE)); + + TaskPortForProcessID(error); + if (error.Fail()) + { + if (log) + log->Printf("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) + { + log->Printf("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. + log->Printf("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; +} + +Error +NativeProcessDarwin::ResumeTask() +{ + Error error; + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + TaskPortForProcessID(error); + if (error.Fail()) + { + if (log) + log->Printf("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; + } + + if (log) + log->Printf("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()) + { + if (log) + log->Printf("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()) + log->Printf("::task_resume(target_task = 0x%4.4x): success", + m_task); + else + log->Printf("::task_resume(target_task = 0x%4.4x) error: %s", + m_task, error.AsCString()); + } + } + else + { + if (log) + log->Printf("::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); +} + +Error +NativeProcessDarwin::GetTaskBasicInfo(task_t task, + struct task_basic_info *info) const +{ + Error 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()) + { + if (log) + log->Printf("::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_log->Printf("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; +} + +Error +NativeProcessDarwin::SuspendTask() +{ + Error error; + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + if (m_task == TASK_NULL) + { + error.SetErrorString("task port is null, cannot suspend task"); + if (log) + log->Printf("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) + log->Printf("::task_suspend(target_task = 0x%4.4x)", m_task); + + return error; +} + +Error +NativeProcessDarwin::Resume(const ResumeActionList &resume_actions) +{ + Error error; + Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS)); + + if (log) + log->Printf("NativeProcessDarwin::%s() called", __FUNCTION__); + + if (CanResume()) + { + m_thread_actions = resume_actions; + error = PrivateResume(); + return error; + } + + auto state = GetState(); + if (state == eStateRunning) + { + if (log) + log->Printf("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; +} + +Error +NativeProcessDarwin::Halt() +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +Error +NativeProcessDarwin::Detach() +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +Error +NativeProcessDarwin::Signal(int signo) +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +Error +NativeProcessDarwin::Interrupt() +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +Error +NativeProcessDarwin::Kill() +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +Error +NativeProcessDarwin::GetMemoryRegionInfo(lldb::addr_t load_addr, + MemoryRegionInfo &range_info) +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +Error +NativeProcessDarwin::ReadMemory(lldb::addr_t addr, void *buf, size_t size, + size_t &bytes_read) +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +Error +NativeProcessDarwin::ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, + size_t size, size_t &bytes_read) +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +Error +NativeProcessDarwin::WriteMemory(lldb::addr_t addr, const void *buf, + size_t size, size_t &bytes_written) +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +Error +NativeProcessDarwin::AllocateMemory(size_t size, uint32_t permissions, + lldb::addr_t &addr) +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +Error +NativeProcessDarwin::DeallocateMemory(lldb::addr_t addr) +{ + Error 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; +} + +Error +NativeProcessDarwin::SetBreakpoint(lldb::addr_t addr, uint32_t size, + bool hardware) +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +void +NativeProcessDarwin::DoStopIDBumped(uint32_t newBumpId) +{ +} + +Error +NativeProcessDarwin::GetLoadedModuleFileSpec(const char* module_path, + FileSpec& file_spec) +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +Error +NativeProcessDarwin::GetFileLoadAddress(const llvm::StringRef& file_name, + lldb::addr_t& load_addr) +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} + +// ----------------------------------------------------------------- +// NativeProcessProtocol protected interface +// ----------------------------------------------------------------- +Error +NativeProcessDarwin::GetSoftwareBreakpointTrapOpcode(size_t + trap_opcode_size_hint, + size_t &actual_opcode_size, + const uint8_t + *&trap_opcode_bytes) +{ + Error error; + error.SetErrorString("TODO: implement"); + return error; +} |