diff options
Diffstat (limited to 'tools/debugserver/source/MacOSX/MachTask.mm')
-rw-r--r-- | tools/debugserver/source/MacOSX/MachTask.mm | 962 |
1 files changed, 0 insertions, 962 deletions
diff --git a/tools/debugserver/source/MacOSX/MachTask.mm b/tools/debugserver/source/MacOSX/MachTask.mm deleted file mode 100644 index d05f50029f16..000000000000 --- a/tools/debugserver/source/MacOSX/MachTask.mm +++ /dev/null @@ -1,962 +0,0 @@ -//===-- MachTask.cpp --------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -//---------------------------------------------------------------------- -// -// MachTask.cpp -// debugserver -// -// Created by Greg Clayton on 12/5/08. -// -//===----------------------------------------------------------------------===// - -#include "MachTask.h" - -// C Includes - -#include <mach-o/dyld_images.h> -#include <mach/mach_vm.h> -#import <sys/sysctl.h> - -#if defined(__APPLE__) -#include <pthread.h> -#include <sched.h> -#endif - -// C++ Includes -#include <iomanip> -#include <sstream> - -// Other libraries and framework includes -// Project includes -#include "CFUtils.h" -#include "DNB.h" -#include "DNBDataRef.h" -#include "DNBError.h" -#include "DNBLog.h" -#include "MachProcess.h" - -#ifdef WITH_SPRINGBOARD - -#include <CoreFoundation/CoreFoundation.h> -#include <SpringBoardServices/SBSWatchdogAssertion.h> -#include <SpringBoardServices/SpringBoardServer.h> - -#endif - -#ifdef WITH_BKS -extern "C" { -#import <BackBoardServices/BKSWatchdogAssertion.h> -#import <BackBoardServices/BackBoardServices.h> -#import <Foundation/Foundation.h> -} -#endif - -#include <AvailabilityMacros.h> - -#ifdef LLDB_ENERGY -#include <mach/mach_time.h> -#include <pmenergy.h> -#include <pmsample.h> -#endif - -//---------------------------------------------------------------------- -// MachTask constructor -//---------------------------------------------------------------------- -MachTask::MachTask(MachProcess *process) - : m_process(process), m_task(TASK_NULL), m_vm_memory(), - m_exception_thread(0), m_exception_port(MACH_PORT_NULL) { - memset(&m_exc_port_info, 0, sizeof(m_exc_port_info)); -} - -//---------------------------------------------------------------------- -// Destructor -//---------------------------------------------------------------------- -MachTask::~MachTask() { Clear(); } - -//---------------------------------------------------------------------- -// MachTask::Suspend -//---------------------------------------------------------------------- -kern_return_t MachTask::Suspend() { - DNBError err; - task_t task = TaskPort(); - err = ::task_suspend(task); - if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) - err.LogThreaded("::task_suspend ( target_task = 0x%4.4x )", task); - return err.Status(); -} - -//---------------------------------------------------------------------- -// MachTask::Resume -//---------------------------------------------------------------------- -kern_return_t MachTask::Resume() { - struct task_basic_info task_info; - task_t task = TaskPort(); - if (task == TASK_NULL) - return KERN_INVALID_ARGUMENT; - - DNBError err; - err = BasicInfo(task, &task_info); - - if (err.Success()) { - // task_resume isn't counted like task_suspend calls are, 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) { - err = ::task_resume(task); - if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) - err.LogThreaded("::task_resume ( target_task = 0x%4.4x )", task); - } - } - return err.Status(); -} - -//---------------------------------------------------------------------- -// MachTask::ExceptionPort -//---------------------------------------------------------------------- -mach_port_t MachTask::ExceptionPort() const { return m_exception_port; } - -//---------------------------------------------------------------------- -// MachTask::ExceptionPortIsValid -//---------------------------------------------------------------------- -bool MachTask::ExceptionPortIsValid() const { - return MACH_PORT_VALID(m_exception_port); -} - -//---------------------------------------------------------------------- -// MachTask::Clear -//---------------------------------------------------------------------- -void MachTask::Clear() { - // Do any cleanup needed for this task - m_task = TASK_NULL; - m_exception_thread = 0; - m_exception_port = MACH_PORT_NULL; -} - -//---------------------------------------------------------------------- -// MachTask::SaveExceptionPortInfo -//---------------------------------------------------------------------- -kern_return_t MachTask::SaveExceptionPortInfo() { - return m_exc_port_info.Save(TaskPort()); -} - -//---------------------------------------------------------------------- -// MachTask::RestoreExceptionPortInfo -//---------------------------------------------------------------------- -kern_return_t MachTask::RestoreExceptionPortInfo() { - return m_exc_port_info.Restore(TaskPort()); -} - -//---------------------------------------------------------------------- -// MachTask::ReadMemory -//---------------------------------------------------------------------- -nub_size_t MachTask::ReadMemory(nub_addr_t addr, nub_size_t size, void *buf) { - nub_size_t n = 0; - task_t task = TaskPort(); - if (task != TASK_NULL) { - n = m_vm_memory.Read(task, addr, buf, size); - - DNBLogThreadedIf(LOG_MEMORY, "MachTask::ReadMemory ( addr = 0x%8.8llx, " - "size = %llu, buf = %p) => %llu bytes read", - (uint64_t)addr, (uint64_t)size, buf, (uint64_t)n); - if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || - (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8)) { - DNBDataRef data((uint8_t *)buf, n, false); - data.Dump(0, static_cast<DNBDataRef::offset_t>(n), addr, - DNBDataRef::TypeUInt8, 16); - } - } - return n; -} - -//---------------------------------------------------------------------- -// MachTask::WriteMemory -//---------------------------------------------------------------------- -nub_size_t MachTask::WriteMemory(nub_addr_t addr, nub_size_t size, - const void *buf) { - nub_size_t n = 0; - task_t task = TaskPort(); - if (task != TASK_NULL) { - n = m_vm_memory.Write(task, addr, buf, size); - DNBLogThreadedIf(LOG_MEMORY, "MachTask::WriteMemory ( addr = 0x%8.8llx, " - "size = %llu, buf = %p) => %llu bytes written", - (uint64_t)addr, (uint64_t)size, buf, (uint64_t)n); - if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || - (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8)) { - DNBDataRef data((const uint8_t *)buf, n, false); - data.Dump(0, static_cast<DNBDataRef::offset_t>(n), addr, - DNBDataRef::TypeUInt8, 16); - } - } - return n; -} - -//---------------------------------------------------------------------- -// MachTask::MemoryRegionInfo -//---------------------------------------------------------------------- -int MachTask::GetMemoryRegionInfo(nub_addr_t addr, DNBRegionInfo *region_info) { - task_t task = TaskPort(); - if (task == TASK_NULL) - return -1; - - int ret = m_vm_memory.GetMemoryRegionInfo(task, addr, region_info); - DNBLogThreadedIf(LOG_MEMORY, "MachTask::MemoryRegionInfo ( addr = 0x%8.8llx " - ") => %i (start = 0x%8.8llx, size = 0x%8.8llx, " - "permissions = %u)", - (uint64_t)addr, ret, (uint64_t)region_info->addr, - (uint64_t)region_info->size, region_info->permissions); - return ret; -} - -#define TIME_VALUE_TO_TIMEVAL(a, r) \ - do { \ - (r)->tv_sec = (a)->seconds; \ - (r)->tv_usec = (a)->microseconds; \ - } while (0) - -// We should consider moving this into each MacThread. -static void get_threads_profile_data(DNBProfileDataScanType scanType, - task_t task, nub_process_t pid, - std::vector<uint64_t> &threads_id, - std::vector<std::string> &threads_name, - std::vector<uint64_t> &threads_used_usec) { - kern_return_t kr; - thread_act_array_t threads; - mach_msg_type_number_t tcnt; - - kr = task_threads(task, &threads, &tcnt); - if (kr != KERN_SUCCESS) - return; - - for (mach_msg_type_number_t i = 0; i < tcnt; i++) { - thread_identifier_info_data_t identifier_info; - mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; - kr = ::thread_info(threads[i], THREAD_IDENTIFIER_INFO, - (thread_info_t)&identifier_info, &count); - if (kr != KERN_SUCCESS) - continue; - - thread_basic_info_data_t basic_info; - count = THREAD_BASIC_INFO_COUNT; - kr = ::thread_info(threads[i], THREAD_BASIC_INFO, - (thread_info_t)&basic_info, &count); - if (kr != KERN_SUCCESS) - continue; - - if ((basic_info.flags & TH_FLAGS_IDLE) == 0) { - nub_thread_t tid = - MachThread::GetGloballyUniqueThreadIDForMachPortID(threads[i]); - threads_id.push_back(tid); - - if ((scanType & eProfileThreadName) && - (identifier_info.thread_handle != 0)) { - struct proc_threadinfo proc_threadinfo; - int len = ::proc_pidinfo(pid, PROC_PIDTHREADINFO, - identifier_info.thread_handle, - &proc_threadinfo, PROC_PIDTHREADINFO_SIZE); - if (len && proc_threadinfo.pth_name[0]) { - threads_name.push_back(proc_threadinfo.pth_name); - } else { - threads_name.push_back(""); - } - } else { - threads_name.push_back(""); - } - struct timeval tv; - struct timeval thread_tv; - TIME_VALUE_TO_TIMEVAL(&basic_info.user_time, &thread_tv); - TIME_VALUE_TO_TIMEVAL(&basic_info.system_time, &tv); - timeradd(&thread_tv, &tv, &thread_tv); - uint64_t used_usec = thread_tv.tv_sec * 1000000ULL + thread_tv.tv_usec; - threads_used_usec.push_back(used_usec); - } - - mach_port_deallocate(mach_task_self(), threads[i]); - } - mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)threads, - tcnt * sizeof(*threads)); -} - -#define RAW_HEXBASE std::setfill('0') << std::hex << std::right -#define DECIMAL std::dec << std::setfill(' ') -std::string MachTask::GetProfileData(DNBProfileDataScanType scanType) { - std::string result; - - static int32_t numCPU = -1; - struct host_cpu_load_info host_info; - if (scanType & eProfileHostCPU) { - int32_t mib[] = {CTL_HW, HW_AVAILCPU}; - size_t len = sizeof(numCPU); - if (numCPU == -1) { - if (sysctl(mib, sizeof(mib) / sizeof(int32_t), &numCPU, &len, NULL, 0) != - 0) - return result; - } - - mach_port_t localHost = mach_host_self(); - mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; - kern_return_t kr = host_statistics(localHost, HOST_CPU_LOAD_INFO, - (host_info_t)&host_info, &count); - if (kr != KERN_SUCCESS) - return result; - } - - task_t task = TaskPort(); - if (task == TASK_NULL) - return result; - - pid_t pid = m_process->ProcessID(); - - struct task_basic_info task_info; - DNBError err; - err = BasicInfo(task, &task_info); - - if (!err.Success()) - return result; - - uint64_t elapsed_usec = 0; - uint64_t task_used_usec = 0; - if (scanType & eProfileCPU) { - // Get current used time. - struct timeval current_used_time; - struct timeval tv; - TIME_VALUE_TO_TIMEVAL(&task_info.user_time, ¤t_used_time); - TIME_VALUE_TO_TIMEVAL(&task_info.system_time, &tv); - timeradd(¤t_used_time, &tv, ¤t_used_time); - task_used_usec = - current_used_time.tv_sec * 1000000ULL + current_used_time.tv_usec; - - struct timeval current_elapsed_time; - int res = gettimeofday(¤t_elapsed_time, NULL); - if (res == 0) { - elapsed_usec = current_elapsed_time.tv_sec * 1000000ULL + - current_elapsed_time.tv_usec; - } - } - - std::vector<uint64_t> threads_id; - std::vector<std::string> threads_name; - std::vector<uint64_t> threads_used_usec; - - if (scanType & eProfileThreadsCPU) { - get_threads_profile_data(scanType, task, pid, threads_id, threads_name, - threads_used_usec); - } - - vm_statistics64_data_t vminfo; - uint64_t physical_memory = 0; - uint64_t anonymous = 0; - uint64_t phys_footprint = 0; - uint64_t memory_cap = 0; - if (m_vm_memory.GetMemoryProfile(scanType, task, task_info, - m_process->GetCPUType(), pid, vminfo, - physical_memory, anonymous, - phys_footprint, memory_cap)) { - std::ostringstream profile_data_stream; - - if (scanType & eProfileHostCPU) { - profile_data_stream << "num_cpu:" << numCPU << ';'; - profile_data_stream << "host_user_ticks:" - << host_info.cpu_ticks[CPU_STATE_USER] << ';'; - profile_data_stream << "host_sys_ticks:" - << host_info.cpu_ticks[CPU_STATE_SYSTEM] << ';'; - profile_data_stream << "host_idle_ticks:" - << host_info.cpu_ticks[CPU_STATE_IDLE] << ';'; - } - - if (scanType & eProfileCPU) { - profile_data_stream << "elapsed_usec:" << elapsed_usec << ';'; - profile_data_stream << "task_used_usec:" << task_used_usec << ';'; - } - - if (scanType & eProfileThreadsCPU) { - const size_t num_threads = threads_id.size(); - for (size_t i = 0; i < num_threads; i++) { - profile_data_stream << "thread_used_id:" << std::hex << threads_id[i] - << std::dec << ';'; - profile_data_stream << "thread_used_usec:" << threads_used_usec[i] - << ';'; - - if (scanType & eProfileThreadName) { - profile_data_stream << "thread_used_name:"; - const size_t len = threads_name[i].size(); - if (len) { - const char *thread_name = threads_name[i].c_str(); - // Make sure that thread name doesn't interfere with our delimiter. - profile_data_stream << RAW_HEXBASE << std::setw(2); - const uint8_t *ubuf8 = (const uint8_t *)(thread_name); - for (size_t j = 0; j < len; j++) { - profile_data_stream << (uint32_t)(ubuf8[j]); - } - // Reset back to DECIMAL. - profile_data_stream << DECIMAL; - } - profile_data_stream << ';'; - } - } - } - - if (scanType & eProfileHostMemory) - profile_data_stream << "total:" << physical_memory << ';'; - - if (scanType & eProfileMemory) { - static vm_size_t pagesize = vm_kernel_page_size; - - // This mimicks Activity Monitor. - uint64_t total_used_count = - (physical_memory / pagesize) - - (vminfo.free_count - vminfo.speculative_count) - - vminfo.external_page_count - vminfo.purgeable_count; - profile_data_stream << "used:" << total_used_count * pagesize << ';'; - - if (scanType & eProfileMemoryAnonymous) { - profile_data_stream << "anonymous:" << anonymous << ';'; - } - - profile_data_stream << "phys_footprint:" << phys_footprint << ';'; - } - - if (scanType & eProfileMemoryCap) { - profile_data_stream << "mem_cap:" << memory_cap << ';'; - } - -#ifdef LLDB_ENERGY - if (scanType & eProfileEnergy) { - struct rusage_info_v2 info; - int rc = proc_pid_rusage(pid, RUSAGE_INFO_V2, (rusage_info_t *)&info); - if (rc == 0) { - uint64_t now = mach_absolute_time(); - pm_task_energy_data_t pm_energy; - memset(&pm_energy, 0, sizeof(pm_energy)); - /* - * Disable most features of pm_sample_pid. It will gather - * network/GPU/WindowServer information; fill in the rest. - */ - pm_sample_task_and_pid(task, pid, &pm_energy, now, - PM_SAMPLE_ALL & ~PM_SAMPLE_NAME & - ~PM_SAMPLE_INTERVAL & ~PM_SAMPLE_CPU & - ~PM_SAMPLE_DISK); - pm_energy.sti.total_user = info.ri_user_time; - pm_energy.sti.total_system = info.ri_system_time; - pm_energy.sti.task_interrupt_wakeups = info.ri_interrupt_wkups; - pm_energy.sti.task_platform_idle_wakeups = info.ri_pkg_idle_wkups; - pm_energy.diskio_bytesread = info.ri_diskio_bytesread; - pm_energy.diskio_byteswritten = info.ri_diskio_byteswritten; - pm_energy.pageins = info.ri_pageins; - - uint64_t total_energy = - (uint64_t)(pm_energy_impact(&pm_energy) * NSEC_PER_SEC); - // uint64_t process_age = now - info.ri_proc_start_abstime; - // uint64_t avg_energy = 100.0 * (double)total_energy / - // (double)process_age; - - profile_data_stream << "energy:" << total_energy << ';'; - } - } -#endif - - profile_data_stream << "--end--;"; - - result = profile_data_stream.str(); - } - - return result; -} - -//---------------------------------------------------------------------- -// MachTask::TaskPortForProcessID -//---------------------------------------------------------------------- -task_t MachTask::TaskPortForProcessID(DNBError &err, bool force) { - if (((m_task == TASK_NULL) || force) && m_process != NULL) - m_task = MachTask::TaskPortForProcessID(m_process->ProcessID(), err); - return m_task; -} - -//---------------------------------------------------------------------- -// MachTask::TaskPortForProcessID -//---------------------------------------------------------------------- -task_t MachTask::TaskPortForProcessID(pid_t pid, DNBError &err, - uint32_t num_retries, - uint32_t usec_interval) { - if (pid != INVALID_NUB_PROCESS) { - DNBError err; - mach_port_t task_self = mach_task_self(); - task_t task = TASK_NULL; - for (uint32_t i = 0; i < num_retries; i++) { - err = ::task_for_pid(task_self, pid, &task); - - if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) { - char str[1024]; - ::snprintf(str, sizeof(str), "::task_for_pid ( target_tport = 0x%4.4x, " - "pid = %d, &task ) => err = 0x%8.8x (%s)", - task_self, pid, err.Status(), - err.AsString() ? err.AsString() : "success"); - if (err.Fail()) - err.SetErrorString(str); - err.LogThreaded(str); - } - - if (err.Success()) - return task; - - // Sleep a bit and try again - ::usleep(usec_interval); - } - } - return TASK_NULL; -} - -//---------------------------------------------------------------------- -// MachTask::BasicInfo -//---------------------------------------------------------------------- -kern_return_t MachTask::BasicInfo(struct task_basic_info *info) { - return BasicInfo(TaskPort(), info); -} - -//---------------------------------------------------------------------- -// MachTask::BasicInfo -//---------------------------------------------------------------------- -kern_return_t MachTask::BasicInfo(task_t task, struct task_basic_info *info) { - if (info == NULL) - return KERN_INVALID_ARGUMENT; - - DNBError err; - mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT; - err = ::task_info(task, TASK_BASIC_INFO, (task_info_t)info, &count); - const bool log_process = DNBLogCheckLogBit(LOG_TASK); - if (log_process || err.Fail()) - err.LogThreaded("::task_info ( target_task = 0x%4.4x, flavor = " - "TASK_BASIC_INFO, task_info_out => %p, task_info_outCnt => " - "%u )", - task, info, count); - if (DNBLogCheckLogBit(LOG_TASK) && DNBLogCheckLogBit(LOG_VERBOSE) && - err.Success()) { - 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; - DNBLogThreaded("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 err.Status(); -} - -//---------------------------------------------------------------------- -// MachTask::IsValid -// -// Returns true if a task is a valid task port for a current process. -//---------------------------------------------------------------------- -bool MachTask::IsValid() const { return MachTask::IsValid(TaskPort()); } - -//---------------------------------------------------------------------- -// MachTask::IsValid -// -// Returns true if a task is a valid task port for a current process. -//---------------------------------------------------------------------- -bool MachTask::IsValid(task_t task) { - if (task != TASK_NULL) { - struct task_basic_info task_info; - return BasicInfo(task, &task_info) == KERN_SUCCESS; - } - return false; -} - -bool MachTask::StartExceptionThread(DNBError &err) { - DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( )", __FUNCTION__); - - task_t task = TaskPortForProcessID(err); - if (MachTask::IsValid(task)) { - // Got the mach port for the current process - mach_port_t task_self = mach_task_self(); - - // Allocate an exception port that we will use to track our child process - err = ::mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE, - &m_exception_port); - if (err.Fail()) - return false; - - // Add the ability to send messages on the new exception port - err = ::mach_port_insert_right(task_self, m_exception_port, - m_exception_port, MACH_MSG_TYPE_MAKE_SEND); - if (err.Fail()) - return false; - - // Save the original state of the exception ports for our child process - SaveExceptionPortInfo(); - - // We weren't able to save the info for our exception ports, we must stop... - if (m_exc_port_info.mask == 0) { - err.SetErrorString("failed to get exception port info"); - return false; - } - - // Set the ability to get all exceptions on this port - err = ::task_set_exception_ports( - task, m_exc_port_info.mask, m_exception_port, - EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE); - if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail()) { - err.LogThreaded("::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 )", - task, m_exc_port_info.mask, m_exception_port, - (EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES), - THREAD_STATE_NONE); - } - - if (err.Fail()) - return false; - - // Create the exception thread - err = ::pthread_create(&m_exception_thread, NULL, MachTask::ExceptionThread, - this); - return err.Success(); - } else { - DNBLogError("MachTask::%s (): task invalid, exception thread start failed.", - __FUNCTION__); - } - return false; -} - -kern_return_t MachTask::ShutDownExcecptionThread() { - DNBError err; - - err = RestoreExceptionPortInfo(); - - // NULL our our exception port and let our exception thread exit - mach_port_t exception_port = m_exception_port; - m_exception_port = 0; - - err.SetError(::pthread_cancel(m_exception_thread), DNBError::POSIX); - if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) - err.LogThreaded("::pthread_cancel ( thread = %p )", m_exception_thread); - - err.SetError(::pthread_join(m_exception_thread, NULL), DNBError::POSIX); - if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) - err.LogThreaded("::pthread_join ( thread = %p, value_ptr = NULL)", - m_exception_thread); - - // Deallocate our exception port that we used to track our child process - mach_port_t task_self = mach_task_self(); - err = ::mach_port_deallocate(task_self, exception_port); - if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) - err.LogThreaded("::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )", - task_self, exception_port); - - return err.Status(); -} - -void *MachTask::ExceptionThread(void *arg) { - if (arg == NULL) - return NULL; - - MachTask *mach_task = (MachTask *)arg; - MachProcess *mach_proc = mach_task->Process(); - DNBLogThreadedIf(LOG_EXCEPTIONS, - "MachTask::%s ( arg = %p ) starting thread...", __FUNCTION__, - arg); - -#if defined(__APPLE__) - pthread_setname_np("exception monitoring thread"); -#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 -#endif - - // 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. - // flag set in the options, so we will wait forever for an exception on - // 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. - uint32_t num_exceptions_received = 0; - DNBError err; - task_t task = mach_task->TaskPort(); - 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 = mach_proc->ProcessID(); - CFReleaser<SBSWatchdogAssertionRef> watchdog; - - if (mach_proc->ProcessUsingSpringBoard()) { - // Request a renewal for every 60 seconds if we attached using SpringBoard - watchdog.reset(::SBSWatchdogAssertionCreateForPID(NULL, pid, 60)); - DNBLogThreadedIf( - LOG_TASK, "::SBSWatchdogAssertionCreateForPID (NULL, %4.4x, 60 ) => %p", - pid, watchdog.get()); - - if (watchdog.get()) { - ::SBSWatchdogAssertionRenew(watchdog.get()); - - CFTimeInterval watchdogRenewalInterval = - ::SBSWatchdogAssertionGetRenewalInterval(watchdog.get()); - DNBLogThreadedIf( - LOG_TASK, - "::SBSWatchdogAssertionGetRenewalInterval ( %p ) => %g seconds", - watchdog.get(), watchdogRenewalInterval); - if (watchdogRenewalInterval > 0.0) { - watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000; - if (watchdog_timeout > 3000) - watchdog_timeout -= 1000; // Give us a second to renew our timeout - else if (watchdog_timeout > 1000) - watchdog_timeout -= - 250; // Give us a quarter of a second to renew our timeout - } - } - 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 (mach_proc->ProcessUsingBackBoard()) { - pid_t pid = mach_proc->ProcessID(); - CFAllocatorRef alloc = kCFAllocatorDefault; - watchdog.reset(::BKSWatchdogAssertionCreateForPID(alloc, pid)); - } -#endif // #ifdef WITH_BKS - - while (mach_task->ExceptionPortIsValid()) { - ::pthread_testcancel(); - - MachException::Message exception_message; - - if (num_exceptions_received > 0) { - // No timeout, just receive as many exceptions as we can since we already - // have one and we want - // to get all currently available exceptions for this task - err = exception_message.Receive( - mach_task->ExceptionPort(), - 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) - err = exception_message.Receive(mach_task->ExceptionPort(), - 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. - err = exception_message.Receive(mach_task->ExceptionPort(), - MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0); - } - - if (err.Status() == MACH_RCV_INTERRUPTED) { - // If we have no task port we should exit this thread - if (!mach_task->ExceptionPortIsValid()) { - DNBLogThreadedIf(LOG_EXCEPTIONS, "thread cancelled..."); - break; - } - - // Make sure our task is still valid - if (MachTask::IsValid(task)) { - // Task is still ok - DNBLogThreadedIf(LOG_EXCEPTIONS, - "interrupted, but task still valid, continuing..."); - continue; - } else { - DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited..."); - mach_proc->SetState(eStateExited); - // Our task has died, exit the thread. - break; - } - } else if (err.Status() == MACH_RCV_TIMED_OUT) { - 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 and get the possibly updated task port back - // from the process in case we exec'ed and our task port changed - task = mach_proc->ExceptionMessageBundleComplete(); - - // in case we use a timeout value when getting exceptions... - // Make sure our task is still valid - if (MachTask::IsValid(task)) { - // Task is still ok - DNBLogThreadedIf(LOG_EXCEPTIONS, "got a timeout, continuing..."); - continue; - } else { - DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited..."); - mach_proc->SetState(eStateExited); - // Our task has died, exit the thread. - break; - } - } - -#if defined(WITH_SPRINGBOARD) && !defined(WITH_BKS) - if (watchdog.get()) { - watchdog_elapsed += periodic_timeout; - if (watchdog_elapsed >= watchdog_timeout) { - DNBLogThreadedIf(LOG_TASK, "SBSWatchdogAssertionRenew ( %p )", - watchdog.get()); - ::SBSWatchdogAssertionRenew(watchdog.get()); - watchdog_elapsed = 0; - } - } -#endif - } else if (err.Status() != KERN_SUCCESS) { - DNBLogThreadedIf(LOG_EXCEPTIONS, "got some other error, do something " - "about it??? nah, continuing for " - "now..."); - // TODO: notify of error? - } else { - if (exception_message.CatchExceptionRaise(task)) { - if (exception_message.state.task_port != task) { - if (exception_message.state.IsValid()) { - // We exec'ed and our task port changed on us. - DNBLogThreadedIf(LOG_EXCEPTIONS, - "task port changed from 0x%4.4x to 0x%4.4x", - task, exception_message.state.task_port); - task = exception_message.state.task_port; - mach_task->TaskPortChanged(exception_message.state.task_port); - } - } - ++num_exceptions_received; - mach_proc->ExceptionMessageReceived(exception_message); - } - } - } - -#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) - - DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s (%p): thread exiting...", - __FUNCTION__, arg); - return NULL; -} - -// So the TASK_DYLD_INFO used to just return the address of the all image infos -// as a single member called "all_image_info". Then someone decided it would be -// a good idea to rename this first member to "all_image_info_addr" and add a -// size member called "all_image_info_size". This of course can not be detected -// using code or #defines. So to hack around this problem, we define our own -// version of the TASK_DYLD_INFO structure so we can guarantee what is inside -// it. - -struct hack_task_dyld_info { - mach_vm_address_t all_image_info_addr; - mach_vm_size_t all_image_info_size; -}; - -nub_addr_t MachTask::GetDYLDAllImageInfosAddress(DNBError &err) { - 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)); - - task_t task = TaskPortForProcessID(err); - if (err.Success()) { - err = ::task_info(task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count); - if (err.Success()) { - // We now have the address of the all image infos structure - return dyld_info.all_image_info_addr; - } - } - return INVALID_NUB_ADDRESS; -} - -//---------------------------------------------------------------------- -// MachTask::AllocateMemory -//---------------------------------------------------------------------- -nub_addr_t MachTask::AllocateMemory(size_t size, uint32_t permissions) { - mach_vm_address_t addr; - task_t task = TaskPort(); - if (task == TASK_NULL) - return INVALID_NUB_ADDRESS; - - DNBError err; - err = ::mach_vm_allocate(task, &addr, size, TRUE); - if (err.Status() == KERN_SUCCESS) { - // Set the protections: - vm_prot_t mach_prot = VM_PROT_NONE; - if (permissions & eMemoryPermissionsReadable) - mach_prot |= VM_PROT_READ; - if (permissions & eMemoryPermissionsWritable) - mach_prot |= VM_PROT_WRITE; - if (permissions & eMemoryPermissionsExecutable) - mach_prot |= VM_PROT_EXECUTE; - - err = ::mach_vm_protect(task, addr, size, 0, mach_prot); - if (err.Status() == KERN_SUCCESS) { - m_allocations.insert(std::make_pair(addr, size)); - return addr; - } - ::mach_vm_deallocate(task, addr, size); - } - return INVALID_NUB_ADDRESS; -} - -//---------------------------------------------------------------------- -// MachTask::DeallocateMemory -//---------------------------------------------------------------------- -nub_bool_t MachTask::DeallocateMemory(nub_addr_t addr) { - task_t task = TaskPort(); - if (task == TASK_NULL) - return false; - - // We have to stash away sizes for the allocations... - allocation_collection::iterator pos, end = m_allocations.end(); - for (pos = m_allocations.begin(); pos != end; pos++) { - if ((*pos).first == addr) { - m_allocations.erase(pos); -#define ALWAYS_ZOMBIE_ALLOCATIONS 0 - if (ALWAYS_ZOMBIE_ALLOCATIONS || - getenv("DEBUGSERVER_ZOMBIE_ALLOCATIONS")) { - ::mach_vm_protect(task, (*pos).first, (*pos).second, 0, VM_PROT_NONE); - return true; - } else - return ::mach_vm_deallocate(task, (*pos).first, (*pos).second) == - KERN_SUCCESS; - } - } - return false; -} - -void MachTask::TaskPortChanged(task_t task) -{ - m_task = task; -} |