Home | History | Annotate | Download | only in MacOSX
      1 //===-- MachTask.cpp --------------------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //----------------------------------------------------------------------
     10 //
     11 //  MachTask.cpp
     12 //  debugserver
     13 //
     14 //  Created by Greg Clayton on 12/5/08.
     15 //
     16 //===----------------------------------------------------------------------===//
     17 
     18 #include "MachTask.h"
     19 
     20 // C Includes
     21 
     22 #include <mach-o/dyld_images.h>
     23 #include <mach/mach_vm.h>
     24 #import <sys/sysctl.h>
     25 
     26 // C++ Includes
     27 #include <iomanip>
     28 #include <sstream>
     29 
     30 // Other libraries and framework includes
     31 // Project includes
     32 #include "CFUtils.h"
     33 #include "DNB.h"
     34 #include "DNBError.h"
     35 #include "DNBLog.h"
     36 #include "MachProcess.h"
     37 #include "DNBDataRef.h"
     38 #include "stack_logging.h"
     39 
     40 #ifdef WITH_SPRINGBOARD
     41 
     42 #include <CoreFoundation/CoreFoundation.h>
     43 #include <SpringBoardServices/SpringBoardServer.h>
     44 #include <SpringBoardServices/SBSWatchdogAssertion.h>
     45 
     46 #endif
     47 
     48 //----------------------------------------------------------------------
     49 // MachTask constructor
     50 //----------------------------------------------------------------------
     51 MachTask::MachTask(MachProcess *process) :
     52     m_process (process),
     53     m_task (TASK_NULL),
     54     m_vm_memory (),
     55     m_exception_thread (0),
     56     m_exception_port (MACH_PORT_NULL)
     57 {
     58     memset(&m_exc_port_info, 0, sizeof(m_exc_port_info));
     59 }
     60 
     61 //----------------------------------------------------------------------
     62 // Destructor
     63 //----------------------------------------------------------------------
     64 MachTask::~MachTask()
     65 {
     66     Clear();
     67 }
     68 
     69 
     70 //----------------------------------------------------------------------
     71 // MachTask::Suspend
     72 //----------------------------------------------------------------------
     73 kern_return_t
     74 MachTask::Suspend()
     75 {
     76     DNBError err;
     77     task_t task = TaskPort();
     78     err = ::task_suspend (task);
     79     if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
     80         err.LogThreaded("::task_suspend ( target_task = 0x%4.4x )", task);
     81     return err.Error();
     82 }
     83 
     84 
     85 //----------------------------------------------------------------------
     86 // MachTask::Resume
     87 //----------------------------------------------------------------------
     88 kern_return_t
     89 MachTask::Resume()
     90 {
     91     struct task_basic_info task_info;
     92     task_t task = TaskPort();
     93     if (task == TASK_NULL)
     94         return KERN_INVALID_ARGUMENT;
     95 
     96     DNBError err;
     97     err = BasicInfo(task, &task_info);
     98 
     99     if (err.Success())
    100     {
    101         // task_resume isn't counted like task_suspend calls are, are, so if the
    102         // task is not suspended, don't try and resume it since it is already
    103         // running
    104         if (task_info.suspend_count > 0)
    105         {
    106             err = ::task_resume (task);
    107             if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
    108                 err.LogThreaded("::task_resume ( target_task = 0x%4.4x )", task);
    109         }
    110     }
    111     return err.Error();
    112 }
    113 
    114 //----------------------------------------------------------------------
    115 // MachTask::ExceptionPort
    116 //----------------------------------------------------------------------
    117 mach_port_t
    118 MachTask::ExceptionPort() const
    119 {
    120     return m_exception_port;
    121 }
    122 
    123 //----------------------------------------------------------------------
    124 // MachTask::ExceptionPortIsValid
    125 //----------------------------------------------------------------------
    126 bool
    127 MachTask::ExceptionPortIsValid() const
    128 {
    129     return MACH_PORT_VALID(m_exception_port);
    130 }
    131 
    132 
    133 //----------------------------------------------------------------------
    134 // MachTask::Clear
    135 //----------------------------------------------------------------------
    136 void
    137 MachTask::Clear()
    138 {
    139     // Do any cleanup needed for this task
    140     m_task = TASK_NULL;
    141     m_exception_thread = 0;
    142     m_exception_port = MACH_PORT_NULL;
    143 
    144 }
    145 
    146 
    147 //----------------------------------------------------------------------
    148 // MachTask::SaveExceptionPortInfo
    149 //----------------------------------------------------------------------
    150 kern_return_t
    151 MachTask::SaveExceptionPortInfo()
    152 {
    153     return m_exc_port_info.Save(TaskPort());
    154 }
    155 
    156 //----------------------------------------------------------------------
    157 // MachTask::RestoreExceptionPortInfo
    158 //----------------------------------------------------------------------
    159 kern_return_t
    160 MachTask::RestoreExceptionPortInfo()
    161 {
    162     return m_exc_port_info.Restore(TaskPort());
    163 }
    164 
    165 
    166 //----------------------------------------------------------------------
    167 // MachTask::ReadMemory
    168 //----------------------------------------------------------------------
    169 nub_size_t
    170 MachTask::ReadMemory (nub_addr_t addr, nub_size_t size, void *buf)
    171 {
    172     nub_size_t n = 0;
    173     task_t task = TaskPort();
    174     if (task != TASK_NULL)
    175     {
    176         n = m_vm_memory.Read(task, addr, buf, size);
    177 
    178         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);
    179         if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8))
    180         {
    181             DNBDataRef data((uint8_t*)buf, n, false);
    182             data.Dump(0, n, addr, DNBDataRef::TypeUInt8, 16);
    183         }
    184     }
    185     return n;
    186 }
    187 
    188 
    189 //----------------------------------------------------------------------
    190 // MachTask::WriteMemory
    191 //----------------------------------------------------------------------
    192 nub_size_t
    193 MachTask::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf)
    194 {
    195     nub_size_t n = 0;
    196     task_t task = TaskPort();
    197     if (task != TASK_NULL)
    198     {
    199         n = m_vm_memory.Write(task, addr, buf, size);
    200         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);
    201         if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8))
    202         {
    203             DNBDataRef data((uint8_t*)buf, n, false);
    204             data.Dump(0, n, addr, DNBDataRef::TypeUInt8, 16);
    205         }
    206     }
    207     return n;
    208 }
    209 
    210 //----------------------------------------------------------------------
    211 // MachTask::MemoryRegionInfo
    212 //----------------------------------------------------------------------
    213 int
    214 MachTask::GetMemoryRegionInfo (nub_addr_t addr, DNBRegionInfo *region_info)
    215 {
    216     task_t task = TaskPort();
    217     if (task == TASK_NULL)
    218         return -1;
    219 
    220     int ret = m_vm_memory.GetMemoryRegionInfo(task, addr, region_info);
    221     DNBLogThreadedIf(LOG_MEMORY, "MachTask::MemoryRegionInfo ( addr = 0x%8.8llx ) => %i  (start = 0x%8.8llx, size = 0x%8.8llx, permissions = %u)",
    222                      (uint64_t)addr,
    223                      ret,
    224                      (uint64_t)region_info->addr,
    225                      (uint64_t)region_info->size,
    226                      region_info->permissions);
    227     return ret;
    228 }
    229 
    230 #define TIME_VALUE_TO_TIMEVAL(a, r) do {        \
    231 (r)->tv_sec = (a)->seconds;                     \
    232 (r)->tv_usec = (a)->microseconds;               \
    233 } while (0)
    234 
    235 // We should consider moving this into each MacThread.
    236 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)
    237 {
    238     kern_return_t kr;
    239     thread_act_array_t threads;
    240     mach_msg_type_number_t tcnt;
    241 
    242     kr = task_threads(task, &threads, &tcnt);
    243     if (kr != KERN_SUCCESS)
    244         return;
    245 
    246     for (int i = 0; i < tcnt; i++)
    247     {
    248         thread_identifier_info_data_t identifier_info;
    249         mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
    250         kr = ::thread_info(threads[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&identifier_info, &count);
    251         if (kr != KERN_SUCCESS) continue;
    252 
    253         thread_basic_info_data_t basic_info;
    254         count = THREAD_BASIC_INFO_COUNT;
    255         kr = ::thread_info(threads[i], THREAD_BASIC_INFO, (thread_info_t)&basic_info, &count);
    256         if (kr != KERN_SUCCESS) continue;
    257 
    258         if ((basic_info.flags & TH_FLAGS_IDLE) == 0)
    259         {
    260             nub_thread_t tid = MachThread::GetGloballyUniqueThreadIDForMachPortID (threads[i]);
    261             threads_id.push_back(tid);
    262 
    263             if ((scanType & eProfileThreadName) && (identifier_info.thread_handle != 0))
    264             {
    265                 struct proc_threadinfo proc_threadinfo;
    266                 int len = ::proc_pidinfo(pid, PROC_PIDTHREADINFO, identifier_info.thread_handle, &proc_threadinfo, PROC_PIDTHREADINFO_SIZE);
    267                 if (len && proc_threadinfo.pth_name[0])
    268                 {
    269                     threads_name.push_back(proc_threadinfo.pth_name);
    270                 }
    271                 else
    272                 {
    273                     threads_name.push_back("");
    274                 }
    275             }
    276             else
    277             {
    278                 threads_name.push_back("");
    279             }
    280             struct timeval tv;
    281             struct timeval thread_tv;
    282             TIME_VALUE_TO_TIMEVAL(&basic_info.user_time, &thread_tv);
    283             TIME_VALUE_TO_TIMEVAL(&basic_info.system_time, &tv);
    284             timeradd(&thread_tv, &tv, &thread_tv);
    285             uint64_t used_usec = thread_tv.tv_sec * 1000000ULL + thread_tv.tv_usec;
    286             threads_used_usec.push_back(used_usec);
    287         }
    288 
    289         kr = mach_port_deallocate(mach_task_self(), threads[i]);
    290     }
    291     kr = mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)(uintptr_t)threads, tcnt * sizeof(*threads));
    292 }
    293 
    294 #define RAW_HEXBASE     std::setfill('0') << std::hex << std::right
    295 #define DECIMAL         std::dec << std::setfill(' ')
    296 std::string
    297 MachTask::GetProfileData (DNBProfileDataScanType scanType)
    298 {
    299     std::string result;
    300 
    301     static int32_t numCPU = -1;
    302     struct host_cpu_load_info host_info;
    303     if (scanType & eProfileHostCPU)
    304     {
    305         int32_t mib[] = {CTL_HW, HW_AVAILCPU};
    306         size_t len = sizeof(numCPU);
    307         if (numCPU == -1)
    308         {
    309             if (sysctl(mib, sizeof(mib) / sizeof(int32_t), &numCPU, &len, NULL, 0) != 0)
    310                 return result;
    311         }
    312 
    313         mach_port_t localHost = mach_host_self();
    314         mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
    315         kern_return_t kr = host_statistics(localHost, HOST_CPU_LOAD_INFO, (host_info_t)&host_info, &count);
    316         if (kr != KERN_SUCCESS)
    317             return result;
    318     }
    319 
    320     task_t task = TaskPort();
    321     if (task == TASK_NULL)
    322         return result;
    323 
    324     struct task_basic_info task_info;
    325     DNBError err;
    326     err = BasicInfo(task, &task_info);
    327 
    328     if (!err.Success())
    329         return result;
    330 
    331     uint64_t elapsed_usec = 0;
    332     uint64_t task_used_usec = 0;
    333     if (scanType & eProfileCPU)
    334     {
    335         // Get current used time.
    336         struct timeval current_used_time;
    337         struct timeval tv;
    338         TIME_VALUE_TO_TIMEVAL(&task_info.user_time, &current_used_time);
    339         TIME_VALUE_TO_TIMEVAL(&task_info.system_time, &tv);
    340         timeradd(&current_used_time, &tv, &current_used_time);
    341         task_used_usec = current_used_time.tv_sec * 1000000ULL + current_used_time.tv_usec;
    342 
    343         struct timeval current_elapsed_time;
    344         int res = gettimeofday(&current_elapsed_time, NULL);
    345         if (res == 0)
    346         {
    347             elapsed_usec = current_elapsed_time.tv_sec * 1000000ULL + current_elapsed_time.tv_usec;
    348         }
    349     }
    350 
    351     std::vector<uint64_t> threads_id;
    352     std::vector<std::string> threads_name;
    353     std::vector<uint64_t> threads_used_usec;
    354 
    355     if (scanType & eProfileThreadsCPU)
    356     {
    357         get_threads_profile_data(scanType, task, m_process->ProcessID(), threads_id, threads_name, threads_used_usec);
    358     }
    359 
    360     struct vm_statistics vm_stats;
    361     uint64_t physical_memory;
    362     mach_vm_size_t rprvt = 0;
    363     mach_vm_size_t rsize = 0;
    364     mach_vm_size_t vprvt = 0;
    365     mach_vm_size_t vsize = 0;
    366     mach_vm_size_t dirty_size = 0;
    367     mach_vm_size_t purgeable = 0;
    368     mach_vm_size_t anonymous = 0;
    369     if (m_vm_memory.GetMemoryProfile(scanType, task, task_info, m_process->GetCPUType(), m_process->ProcessID(), vm_stats, physical_memory, rprvt, rsize, vprvt, vsize, dirty_size, purgeable, anonymous))
    370     {
    371         std::ostringstream profile_data_stream;
    372 
    373         if (scanType & eProfileHostCPU)
    374         {
    375             profile_data_stream << "num_cpu:" << numCPU << ';';
    376             profile_data_stream << "host_user_ticks:" << host_info.cpu_ticks[CPU_STATE_USER] << ';';
    377             profile_data_stream << "host_sys_ticks:" << host_info.cpu_ticks[CPU_STATE_SYSTEM] << ';';
    378             profile_data_stream << "host_idle_ticks:" << host_info.cpu_ticks[CPU_STATE_IDLE] << ';';
    379         }
    380 
    381         if (scanType & eProfileCPU)
    382         {
    383             profile_data_stream << "elapsed_usec:" << elapsed_usec << ';';
    384             profile_data_stream << "task_used_usec:" << task_used_usec << ';';
    385         }
    386 
    387         if (scanType & eProfileThreadsCPU)
    388         {
    389             int num_threads = threads_id.size();
    390             for (int i=0; i<num_threads; i++)
    391             {
    392                 profile_data_stream << "thread_used_id:" << std::hex << threads_id[i] << std::dec << ';';
    393                 profile_data_stream << "thread_used_usec:" << threads_used_usec[i] << ';';
    394 
    395                 if (scanType & eProfileThreadName)
    396                 {
    397                     profile_data_stream << "thread_used_name:";
    398                     int len = threads_name[i].size();
    399                     if (len)
    400                     {
    401                         const char *thread_name = threads_name[i].c_str();
    402                         // Make sure that thread name doesn't interfere with our delimiter.
    403                         profile_data_stream << RAW_HEXBASE << std::setw(2);
    404                         const uint8_t *ubuf8 = (const uint8_t *)(thread_name);
    405                         for (int j=0; j<len; j++)
    406                         {
    407                             profile_data_stream << (uint32_t)(ubuf8[j]);
    408                         }
    409                         // Reset back to DECIMAL.
    410                         profile_data_stream << DECIMAL;
    411                     }
    412                     profile_data_stream << ';';
    413                 }
    414             }
    415         }
    416 
    417         if (scanType & eProfileHostMemory)
    418             profile_data_stream << "total:" << physical_memory << ';';
    419 
    420         if (scanType & eProfileMemory)
    421         {
    422             static vm_size_t pagesize;
    423             static bool calculated = false;
    424             if (!calculated)
    425             {
    426                 calculated = true;
    427                 pagesize = PageSize();
    428             }
    429 
    430             profile_data_stream << "wired:" << vm_stats.wire_count * pagesize << ';';
    431             profile_data_stream << "active:" << vm_stats.active_count * pagesize << ';';
    432             profile_data_stream << "inactive:" << vm_stats.inactive_count * pagesize << ';';
    433             uint64_t total_used_count = vm_stats.wire_count + vm_stats.inactive_count + vm_stats.active_count;
    434             profile_data_stream << "used:" << total_used_count * pagesize << ';';
    435             profile_data_stream << "free:" << vm_stats.free_count * pagesize << ';';
    436 
    437             profile_data_stream << "rprvt:" << rprvt << ';';
    438             profile_data_stream << "rsize:" << rsize << ';';
    439             profile_data_stream << "vprvt:" << vprvt << ';';
    440             profile_data_stream << "vsize:" << vsize << ';';
    441 
    442             if (scanType & eProfileMemoryDirtyPage)
    443                 profile_data_stream << "dirty:" << dirty_size << ';';
    444 
    445             if (scanType & eProfileMemoryAnonymous)
    446             {
    447                 profile_data_stream << "purgeable:" << purgeable << ';';
    448                 profile_data_stream << "anonymous:" << anonymous << ';';
    449             }
    450         }
    451 
    452         profile_data_stream << "--end--;";
    453 
    454         result = profile_data_stream.str();
    455     }
    456 
    457     return result;
    458 }
    459 
    460 
    461 //----------------------------------------------------------------------
    462 // MachTask::TaskPortForProcessID
    463 //----------------------------------------------------------------------
    464 task_t
    465 MachTask::TaskPortForProcessID (DNBError &err)
    466 {
    467     if (m_task == TASK_NULL && m_process != NULL)
    468         m_task = MachTask::TaskPortForProcessID(m_process->ProcessID(), err);
    469     return m_task;
    470 }
    471 
    472 //----------------------------------------------------------------------
    473 // MachTask::TaskPortForProcessID
    474 //----------------------------------------------------------------------
    475 task_t
    476 MachTask::TaskPortForProcessID (pid_t pid, DNBError &err, uint32_t num_retries, uint32_t usec_interval)
    477 {
    478     if (pid != INVALID_NUB_PROCESS)
    479     {
    480         DNBError err;
    481         mach_port_t task_self = mach_task_self ();
    482         task_t task = TASK_NULL;
    483         for (uint32_t i=0; i<num_retries; i++)
    484         {
    485             err = ::task_for_pid ( task_self, pid, &task);
    486 
    487             if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
    488             {
    489                 char str[1024];
    490                 ::snprintf (str,
    491                             sizeof(str),
    492                             "::task_for_pid ( target_tport = 0x%4.4x, pid = %d, &task ) => err = 0x%8.8x (%s)",
    493                             task_self,
    494                             pid,
    495                             err.Error(),
    496                             err.AsString() ? err.AsString() : "success");
    497                 if (err.Fail())
    498                     err.SetErrorString(str);
    499                 err.LogThreaded(str);
    500             }
    501 
    502             if (err.Success())
    503                 return task;
    504 
    505             // Sleep a bit and try again
    506             ::usleep (usec_interval);
    507         }
    508     }
    509     return TASK_NULL;
    510 }
    511 
    512 
    513 //----------------------------------------------------------------------
    514 // MachTask::BasicInfo
    515 //----------------------------------------------------------------------
    516 kern_return_t
    517 MachTask::BasicInfo(struct task_basic_info *info)
    518 {
    519     return BasicInfo (TaskPort(), info);
    520 }
    521 
    522 //----------------------------------------------------------------------
    523 // MachTask::BasicInfo
    524 //----------------------------------------------------------------------
    525 kern_return_t
    526 MachTask::BasicInfo(task_t task, struct task_basic_info *info)
    527 {
    528     if (info == NULL)
    529         return KERN_INVALID_ARGUMENT;
    530 
    531     DNBError err;
    532     mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
    533     err = ::task_info (task, TASK_BASIC_INFO, (task_info_t)info, &count);
    534     const bool log_process = DNBLogCheckLogBit(LOG_TASK);
    535     if (log_process || err.Fail())
    536         err.LogThreaded("::task_info ( target_task = 0x%4.4x, flavor = TASK_BASIC_INFO, task_info_out => %p, task_info_outCnt => %u )", task, info, count);
    537     if (DNBLogCheckLogBit(LOG_TASK) && DNBLogCheckLogBit(LOG_VERBOSE) && err.Success())
    538     {
    539         float user = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f;
    540         float system = (float)info->user_time.seconds + (float)info->user_time.microseconds / 1000000.0f;
    541         DNBLogThreaded ("task_basic_info = { suspend_count = %i, virtual_size = 0x%8.8llx, resident_size = 0x%8.8llx, user_time = %f, system_time = %f }",
    542                         info->suspend_count,
    543                         (uint64_t)info->virtual_size,
    544                         (uint64_t)info->resident_size,
    545                         user,
    546                         system);
    547     }
    548     return err.Error();
    549 }
    550 
    551 
    552 //----------------------------------------------------------------------
    553 // MachTask::IsValid
    554 //
    555 // Returns true if a task is a valid task port for a current process.
    556 //----------------------------------------------------------------------
    557 bool
    558 MachTask::IsValid () const
    559 {
    560     return MachTask::IsValid(TaskPort());
    561 }
    562 
    563 //----------------------------------------------------------------------
    564 // MachTask::IsValid
    565 //
    566 // Returns true if a task is a valid task port for a current process.
    567 //----------------------------------------------------------------------
    568 bool
    569 MachTask::IsValid (task_t task)
    570 {
    571     if (task != TASK_NULL)
    572     {
    573         struct task_basic_info task_info;
    574         return BasicInfo(task, &task_info) == KERN_SUCCESS;
    575     }
    576     return false;
    577 }
    578 
    579 
    580 bool
    581 MachTask::StartExceptionThread(DNBError &err)
    582 {
    583     DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( )", __FUNCTION__);
    584     task_t task = TaskPortForProcessID(err);
    585     if (MachTask::IsValid(task))
    586     {
    587         // Got the mach port for the current process
    588         mach_port_t task_self = mach_task_self ();
    589 
    590         // Allocate an exception port that we will use to track our child process
    591         err = ::mach_port_allocate (task_self, MACH_PORT_RIGHT_RECEIVE, &m_exception_port);
    592         if (err.Fail())
    593             return false;
    594 
    595         // Add the ability to send messages on the new exception port
    596         err = ::mach_port_insert_right (task_self, m_exception_port, m_exception_port, MACH_MSG_TYPE_MAKE_SEND);
    597         if (err.Fail())
    598             return false;
    599 
    600         // Save the original state of the exception ports for our child process
    601         SaveExceptionPortInfo();
    602 
    603         // We weren't able to save the info for our exception ports, we must stop...
    604         if (m_exc_port_info.mask == 0)
    605         {
    606             err.SetErrorString("failed to get exception port info");
    607             return false;
    608         }
    609 
    610         // Set the ability to get all exceptions on this port
    611         err = ::task_set_exception_ports (task, m_exc_port_info.mask, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
    612         if (DNBLogCheckLogBit(LOG_EXCEPTIONS) || err.Fail())
    613         {
    614             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 )",
    615                             task,
    616                             m_exc_port_info.mask,
    617                             m_exception_port,
    618                             (EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES),
    619                             THREAD_STATE_NONE);
    620         }
    621 
    622         if (err.Fail())
    623             return false;
    624 
    625         // Create the exception thread
    626         err = ::pthread_create (&m_exception_thread, NULL, MachTask::ExceptionThread, this);
    627         return err.Success();
    628     }
    629     else
    630     {
    631         DNBLogError("MachTask::%s (): task invalid, exception thread start failed.", __FUNCTION__);
    632     }
    633     return false;
    634 }
    635 
    636 kern_return_t
    637 MachTask::ShutDownExcecptionThread()
    638 {
    639     DNBError err;
    640 
    641     err = RestoreExceptionPortInfo();
    642 
    643     // NULL our our exception port and let our exception thread exit
    644     mach_port_t exception_port = m_exception_port;
    645     m_exception_port = NULL;
    646 
    647     err.SetError(::pthread_cancel(m_exception_thread), DNBError::POSIX);
    648     if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
    649         err.LogThreaded("::pthread_cancel ( thread = %p )", m_exception_thread);
    650 
    651     err.SetError(::pthread_join(m_exception_thread, NULL), DNBError::POSIX);
    652     if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
    653         err.LogThreaded("::pthread_join ( thread = %p, value_ptr = NULL)", m_exception_thread);
    654 
    655     // Deallocate our exception port that we used to track our child process
    656     mach_port_t task_self = mach_task_self ();
    657     err = ::mach_port_deallocate (task_self, exception_port);
    658     if (DNBLogCheckLogBit(LOG_TASK) || err.Fail())
    659         err.LogThreaded("::mach_port_deallocate ( task = 0x%4.4x, name = 0x%4.4x )", task_self, exception_port);
    660 
    661     return err.Error();
    662 }
    663 
    664 
    665 void *
    666 MachTask::ExceptionThread (void *arg)
    667 {
    668     if (arg == NULL)
    669         return NULL;
    670 
    671     MachTask *mach_task = (MachTask*) arg;
    672     MachProcess *mach_proc = mach_task->Process();
    673     DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( arg = %p ) starting thread...", __FUNCTION__, arg);
    674 
    675     // We keep a count of the number of consecutive exceptions received so
    676     // we know to grab all exceptions without a timeout. We do this to get a
    677     // bunch of related exceptions on our exception port so we can process
    678     // then together. When we have multiple threads, we can get an exception
    679     // per thread and they will come in consecutively. The main loop in this
    680     // thread can stop periodically if needed to service things related to this
    681     // process.
    682     // flag set in the options, so we will wait forever for an exception on
    683     // our exception port. After we get one exception, we then will use the
    684     // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current
    685     // exceptions for our process. After we have received the last pending
    686     // exception, we will get a timeout which enables us to then notify
    687     // our main thread that we have an exception bundle avaiable. We then wait
    688     // for the main thread to tell this exception thread to start trying to get
    689     // exceptions messages again and we start again with a mach_msg read with
    690     // infinite timeout.
    691     uint32_t num_exceptions_received = 0;
    692     DNBError err;
    693     task_t task = mach_task->TaskPort();
    694     mach_msg_timeout_t periodic_timeout = 0;
    695 
    696 #ifdef WITH_SPRINGBOARD
    697     mach_msg_timeout_t watchdog_elapsed = 0;
    698     mach_msg_timeout_t watchdog_timeout = 60 * 1000;
    699     pid_t pid = mach_proc->ProcessID();
    700     CFReleaser<SBSWatchdogAssertionRef> watchdog;
    701 
    702     if (mach_proc->ProcessUsingSpringBoard())
    703     {
    704         // Request a renewal for every 60 seconds if we attached using SpringBoard
    705         watchdog.reset(::SBSWatchdogAssertionCreateForPID(NULL, pid, 60));
    706         DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionCreateForPID (NULL, %4.4x, 60 ) => %p", pid, watchdog.get());
    707 
    708         if (watchdog.get())
    709         {
    710             ::SBSWatchdogAssertionRenew (watchdog.get());
    711 
    712             CFTimeInterval watchdogRenewalInterval = ::SBSWatchdogAssertionGetRenewalInterval (watchdog.get());
    713             DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionGetRenewalInterval ( %p ) => %g seconds", watchdog.get(), watchdogRenewalInterval);
    714             if (watchdogRenewalInterval > 0.0)
    715             {
    716                 watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000;
    717                 if (watchdog_timeout > 3000)
    718                     watchdog_timeout -= 1000;   // Give us a second to renew our timeout
    719                 else if (watchdog_timeout > 1000)
    720                     watchdog_timeout -= 250;    // Give us a quarter of a second to renew our timeout
    721             }
    722         }
    723         if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout)
    724             periodic_timeout = watchdog_timeout;
    725     }
    726 #endif  // #ifdef WITH_SPRINGBOARD
    727 
    728     while (mach_task->ExceptionPortIsValid())
    729     {
    730         ::pthread_testcancel ();
    731 
    732         MachException::Message exception_message;
    733 
    734 
    735         if (num_exceptions_received > 0)
    736         {
    737             // No timeout, just receive as many exceptions as we can since we already have one and we want
    738             // to get all currently available exceptions for this task
    739             err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0);
    740         }
    741         else if (periodic_timeout > 0)
    742         {
    743             // We need to stop periodically in this loop, so try and get a mach message with a valid timeout (ms)
    744             err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, periodic_timeout);
    745         }
    746         else
    747         {
    748             // We don't need to parse all current exceptions or stop periodically,
    749             // just wait for an exception forever.
    750             err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0);
    751         }
    752 
    753         if (err.Error() == MACH_RCV_INTERRUPTED)
    754         {
    755             // If we have no task port we should exit this thread
    756             if (!mach_task->ExceptionPortIsValid())
    757             {
    758                 DNBLogThreadedIf(LOG_EXCEPTIONS, "thread cancelled...");
    759                 break;
    760             }
    761 
    762             // Make sure our task is still valid
    763             if (MachTask::IsValid(task))
    764             {
    765                 // Task is still ok
    766                 DNBLogThreadedIf(LOG_EXCEPTIONS, "interrupted, but task still valid, continuing...");
    767                 continue;
    768             }
    769             else
    770             {
    771                 DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited...");
    772                 mach_proc->SetState(eStateExited);
    773                 // Our task has died, exit the thread.
    774                 break;
    775             }
    776         }
    777         else if (err.Error() == MACH_RCV_TIMED_OUT)
    778         {
    779             if (num_exceptions_received > 0)
    780             {
    781                 // We were receiving all current exceptions with a timeout of zero
    782                 // it is time to go back to our normal looping mode
    783                 num_exceptions_received = 0;
    784 
    785                 // Notify our main thread we have a complete exception message
    786                 // bundle available.
    787                 mach_proc->ExceptionMessageBundleComplete();
    788 
    789                 // in case we use a timeout value when getting exceptions...
    790                 // Make sure our task is still valid
    791                 if (MachTask::IsValid(task))
    792                 {
    793                     // Task is still ok
    794                     DNBLogThreadedIf(LOG_EXCEPTIONS, "got a timeout, continuing...");
    795                     continue;
    796                 }
    797                 else
    798                 {
    799                     DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited...");
    800                     mach_proc->SetState(eStateExited);
    801                     // Our task has died, exit the thread.
    802                     break;
    803                 }
    804                 continue;
    805             }
    806 
    807 #ifdef WITH_SPRINGBOARD
    808             if (watchdog.get())
    809             {
    810                 watchdog_elapsed += periodic_timeout;
    811                 if (watchdog_elapsed >= watchdog_timeout)
    812                 {
    813                     DNBLogThreadedIf(LOG_TASK, "SBSWatchdogAssertionRenew ( %p )", watchdog.get());
    814                     ::SBSWatchdogAssertionRenew (watchdog.get());
    815                     watchdog_elapsed = 0;
    816                 }
    817             }
    818 #endif
    819         }
    820         else if (err.Error() != KERN_SUCCESS)
    821         {
    822             DNBLogThreadedIf(LOG_EXCEPTIONS, "got some other error, do something about it??? nah, continuing for now...");
    823             // TODO: notify of error?
    824         }
    825         else
    826         {
    827             if (exception_message.CatchExceptionRaise(task))
    828             {
    829                 ++num_exceptions_received;
    830                 mach_proc->ExceptionMessageReceived(exception_message);
    831             }
    832         }
    833     }
    834 
    835 #ifdef WITH_SPRINGBOARD
    836     if (watchdog.get())
    837     {
    838         // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel when we
    839         // all are up and running on systems that support it. The SBS framework has a #define
    840         // that will forward SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel for now
    841         // so it should still build either way.
    842         DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", watchdog.get());
    843         ::SBSWatchdogAssertionRelease (watchdog.get());
    844     }
    845 #endif  // #ifdef WITH_SPRINGBOARD
    846 
    847     DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s (%p): thread exiting...", __FUNCTION__, arg);
    848     return NULL;
    849 }
    850 
    851 
    852 // So the TASK_DYLD_INFO used to just return the address of the all image infos
    853 // as a single member called "all_image_info". Then someone decided it would be
    854 // a good idea to rename this first member to "all_image_info_addr" and add a
    855 // size member called "all_image_info_size". This of course can not be detected
    856 // using code or #defines. So to hack around this problem, we define our own
    857 // version of the TASK_DYLD_INFO structure so we can guarantee what is inside it.
    858 
    859 struct hack_task_dyld_info {
    860     mach_vm_address_t   all_image_info_addr;
    861     mach_vm_size_t      all_image_info_size;
    862 };
    863 
    864 nub_addr_t
    865 MachTask::GetDYLDAllImageInfosAddress (DNBError& err)
    866 {
    867     struct hack_task_dyld_info dyld_info;
    868     mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
    869     // Make sure that COUNT isn't bigger than our hacked up struct hack_task_dyld_info.
    870     // If it is, then make COUNT smaller to match.
    871     if (count > (sizeof(struct hack_task_dyld_info) / sizeof(natural_t)))
    872         count = (sizeof(struct hack_task_dyld_info) / sizeof(natural_t));
    873 
    874     task_t task = TaskPortForProcessID (err);
    875     if (err.Success())
    876     {
    877         err = ::task_info (task, TASK_DYLD_INFO, (task_info_t)&dyld_info, &count);
    878         if (err.Success())
    879         {
    880             // We now have the address of the all image infos structure
    881             return dyld_info.all_image_info_addr;
    882         }
    883     }
    884     return INVALID_NUB_ADDRESS;
    885 }
    886 
    887 
    888 //----------------------------------------------------------------------
    889 // MachTask::AllocateMemory
    890 //----------------------------------------------------------------------
    891 nub_addr_t
    892 MachTask::AllocateMemory (size_t size, uint32_t permissions)
    893 {
    894     mach_vm_address_t addr;
    895     task_t task = TaskPort();
    896     if (task == TASK_NULL)
    897         return INVALID_NUB_ADDRESS;
    898 
    899     DNBError err;
    900     err = ::mach_vm_allocate (task, &addr, size, TRUE);
    901     if (err.Error() == KERN_SUCCESS)
    902     {
    903         // Set the protections:
    904         vm_prot_t mach_prot = VM_PROT_NONE;
    905         if (permissions & eMemoryPermissionsReadable)
    906             mach_prot |= VM_PROT_READ;
    907         if (permissions & eMemoryPermissionsWritable)
    908             mach_prot |= VM_PROT_WRITE;
    909         if (permissions & eMemoryPermissionsExecutable)
    910             mach_prot |= VM_PROT_EXECUTE;
    911 
    912 
    913         err = ::mach_vm_protect (task, addr, size, 0, mach_prot);
    914         if (err.Error() == KERN_SUCCESS)
    915         {
    916             m_allocations.insert (std::make_pair(addr, size));
    917             return addr;
    918         }
    919         ::mach_vm_deallocate (task, addr, size);
    920     }
    921     return INVALID_NUB_ADDRESS;
    922 }
    923 
    924 //----------------------------------------------------------------------
    925 // MachTask::DeallocateMemory
    926 //----------------------------------------------------------------------
    927 nub_bool_t
    928 MachTask::DeallocateMemory (nub_addr_t addr)
    929 {
    930     task_t task = TaskPort();
    931     if (task == TASK_NULL)
    932         return false;
    933 
    934     // We have to stash away sizes for the allocations...
    935     allocation_collection::iterator pos, end = m_allocations.end();
    936     for (pos = m_allocations.begin(); pos != end; pos++)
    937     {
    938         if ((*pos).first == addr)
    939         {
    940             m_allocations.erase(pos);
    941 #define ALWAYS_ZOMBIE_ALLOCATIONS 0
    942             if (ALWAYS_ZOMBIE_ALLOCATIONS || getenv ("DEBUGSERVER_ZOMBIE_ALLOCATIONS"))
    943             {
    944                 ::mach_vm_protect (task, (*pos).first, (*pos).second, 0, VM_PROT_NONE);
    945                 return true;
    946             }
    947             else
    948                 return ::mach_vm_deallocate (task, (*pos).first, (*pos).second) == KERN_SUCCESS;
    949         }
    950 
    951     }
    952     return false;
    953 }
    954 
    955 static void foundStackLog(mach_stack_logging_record_t record, void *context) {
    956     *((bool*)context) = true;
    957 }
    958 
    959 bool
    960 MachTask::HasMallocLoggingEnabled ()
    961 {
    962     bool found = false;
    963 
    964     __mach_stack_logging_enumerate_records(m_task, 0x0, foundStackLog, &found);
    965     return found;
    966 }
    967 
    968 struct history_enumerator_impl_data
    969 {
    970     MachMallocEvent *buffer;
    971     uint32_t        *position;
    972     uint32_t         count;
    973 };
    974 
    975 static void history_enumerator_impl(mach_stack_logging_record_t record, void* enum_obj)
    976 {
    977     history_enumerator_impl_data *data = (history_enumerator_impl_data*)enum_obj;
    978 
    979     if (*data->position >= data->count)
    980         return;
    981 
    982     data->buffer[*data->position].m_base_address = record.address;
    983     data->buffer[*data->position].m_size = record.argument;
    984     data->buffer[*data->position].m_event_id = record.stack_identifier;
    985     data->buffer[*data->position].m_event_type = record.type_flags == stack_logging_type_alloc ?   eMachMallocEventTypeAlloc :
    986                                                  record.type_flags == stack_logging_type_dealloc ? eMachMallocEventTypeDealloc :
    987                                                                                                    eMachMallocEventTypeOther;
    988     *data->position+=1;
    989 }
    990 
    991 bool
    992 MachTask::EnumerateMallocRecords (MachMallocEvent *event_buffer,
    993                                   uint32_t buffer_size,
    994                                   uint32_t *count)
    995 {
    996     return EnumerateMallocRecords(0,
    997                                   event_buffer,
    998                                   buffer_size,
    999                                   count);
   1000 }
   1001 
   1002 bool
   1003 MachTask::EnumerateMallocRecords (mach_vm_address_t address,
   1004                                   MachMallocEvent *event_buffer,
   1005                                   uint32_t buffer_size,
   1006                                   uint32_t *count)
   1007 {
   1008     if (!event_buffer || !count)
   1009         return false;
   1010 
   1011     if (buffer_size == 0)
   1012         return false;
   1013 
   1014     *count = 0;
   1015     history_enumerator_impl_data data = { event_buffer, count, buffer_size };
   1016     __mach_stack_logging_enumerate_records(m_task, address, history_enumerator_impl, &data);
   1017     return (*count > 0);
   1018 }
   1019 
   1020 bool
   1021 MachTask::EnumerateMallocFrames (MachMallocEventId event_id,
   1022                                  mach_vm_address_t *function_addresses_buffer,
   1023                                  uint32_t buffer_size,
   1024                                  uint32_t *count)
   1025 {
   1026     if (!function_addresses_buffer || !count)
   1027         return false;
   1028 
   1029     if (buffer_size == 0)
   1030         return false;
   1031 
   1032     __mach_stack_logging_frames_for_uniqued_stack(m_task, event_id, &function_addresses_buffer[0], buffer_size, count);
   1033     *count -= 1;
   1034     if (function_addresses_buffer[*count-1] < PageSize())
   1035         *count -= 1;
   1036     return (*count > 0);
   1037 }
   1038 
   1039 nub_size_t
   1040 MachTask::PageSize ()
   1041 {
   1042     return m_vm_memory.PageSize (m_task);
   1043 }
   1044