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