//---------------------------------------------------------------------- // MachTask::AllocateMemory //---------------------------------------------------------------------- nub_addr_t MachTask::AllocateMemory (size_t size, uint32_t permissions) { mach_vm_address_t addr; task_t task = TaskPort(); if (task == TASK_NULL) return INVALID_NUB_ADDRESS; DNBError err; err = ::mach_vm_allocate (task, &addr, size, TRUE); if (err.Error() == KERN_SUCCESS) { // Set the protections: vm_prot_t mach_prot = VM_PROT_NONE; if (permissions & eMemoryPermissionsReadable) mach_prot |= VM_PROT_READ; if (permissions & eMemoryPermissionsWritable) mach_prot |= VM_PROT_WRITE; if (permissions & eMemoryPermissionsExecutable) mach_prot |= VM_PROT_EXECUTE; err = ::mach_vm_protect (task, addr, size, 0, mach_prot); if (err.Error() == KERN_SUCCESS) { m_allocations.insert (std::make_pair(addr, size)); return addr; } ::mach_vm_deallocate (task, addr, size); } return INVALID_NUB_ADDRESS; }
//---------------------------------------------------------------------- // MachTask::Resume //---------------------------------------------------------------------- kern_return_t MachTask::Resume() { struct task_basic_info task_info; task_t task = TaskPort(); if (task == TASK_NULL) return KERN_INVALID_ARGUMENT; DNBError err; err = BasicInfo(task, &task_info); if (err.Success()) { // task_resume isn't counted like task_suspend calls are, are, so if the // task is not suspended, don't try and resume it since it is already // running if (task_info.suspend_count > 0) { err = ::task_resume (task); if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) err.LogThreaded("::task_resume ( target_task = 0x%4.4x )", task); } } return err.Error(); }
//---------------------------------------------------------------------- // MachTask::DeallocateMemory //---------------------------------------------------------------------- nub_bool_t MachTask::DeallocateMemory (nub_addr_t addr) { task_t task = TaskPort(); if (task == TASK_NULL) return false; // We have to stash away sizes for the allocations... allocation_collection::iterator pos, end = m_allocations.end(); for (pos = m_allocations.begin(); pos != end; pos++) { if ((*pos).first == addr) { m_allocations.erase(pos); #define ALWAYS_ZOMBIE_ALLOCATIONS 0 if (ALWAYS_ZOMBIE_ALLOCATIONS || getenv ("DEBUGSERVER_ZOMBIE_ALLOCATIONS")) { ::mach_vm_protect (task, (*pos).first, (*pos).second, 0, VM_PROT_NONE); return true; } else return ::mach_vm_deallocate (task, (*pos).first, (*pos).second) == KERN_SUCCESS; } } return false; }
//---------------------------------------------------------------------- // MachTask::Suspend //---------------------------------------------------------------------- kern_return_t MachTask::Suspend() { DNBError err; task_t task = TaskPort(); err = ::task_suspend (task); if (DNBLogCheckLogBit(LOG_TASK) || err.Fail()) err.LogThreaded("::task_suspend ( target_task = 0x%4.4x )", task); return err.Error(); }
//---------------------------------------------------------------------- // MachTask::MemoryRegionInfo //---------------------------------------------------------------------- int MachTask::GetMemoryRegionInfo (nub_addr_t addr, DNBRegionInfo *region_info) { task_t task = TaskPort(); if (task == TASK_NULL) return -1; int ret = m_vm_memory.GetMemoryRegionInfo(task, addr, region_info); DNBLogThreadedIf(LOG_MEMORY, "MachTask::MemoryRegionInfo ( addr = 0x%8.8llx ) => %i (start = 0x%8.8llx, size = 0x%8.8llx, permissions = %u)", (uint64_t)addr, ret, (uint64_t)region_info->addr, (uint64_t)region_info->size, region_info->permissions); return ret; }
//---------------------------------------------------------------------- // MachTask::WriteMemory //---------------------------------------------------------------------- nub_size_t MachTask::WriteMemory (nub_addr_t addr, nub_size_t size, const void *buf) { nub_size_t n = 0; task_t task = TaskPort(); if (task != TASK_NULL) { n = m_vm_memory.Write(task, addr, buf, size); DNBLogThreadedIf(LOG_MEMORY, "MachTask::WriteMemory ( addr = 0x%8.8llx, size = %zu, buf = %8.8p) => %u bytes written", (uint64_t)addr, size, buf, n); if (DNBLogCheckLogBit(LOG_MEMORY_DATA_LONG) || (DNBLogCheckLogBit(LOG_MEMORY_DATA_SHORT) && size <= 8)) { DNBDataRef data((uint8_t*)buf, n, false); data.Dump(0, n, addr, DNBDataRef::TypeUInt8, 16); } } return n; }
//---------------------------------------------------------------------- // MachTask::IsValid // // Returns true if a task is a valid task port for a current process. //---------------------------------------------------------------------- bool MachTask::IsValid () const { return MachTask::IsValid(TaskPort()); }
//---------------------------------------------------------------------- // MachTask::BasicInfo //---------------------------------------------------------------------- kern_return_t MachTask::BasicInfo(struct task_basic_info *info) { return BasicInfo (TaskPort(), info); }
//---------------------------------------------------------------------- // MachTask::RestoreExceptionPortInfo //---------------------------------------------------------------------- kern_return_t MachTask::RestoreExceptionPortInfo() { return m_exc_port_info.Restore(TaskPort()); }
//---------------------------------------------------------------------- // MachTask::SaveExceptionPortInfo //---------------------------------------------------------------------- kern_return_t MachTask::SaveExceptionPortInfo() { return m_exc_port_info.Save(TaskPort()); }
std::string MachTask::GetProfileData (DNBProfileDataScanType scanType) { std::string result; static int32_t numCPU = -1; struct host_cpu_load_info host_info; if (scanType & eProfileHostCPU) { int32_t mib[] = {CTL_HW, HW_AVAILCPU}; size_t len = sizeof(numCPU); if (numCPU == -1) { if (sysctl(mib, sizeof(mib) / sizeof(int32_t), &numCPU, &len, NULL, 0) != 0) return result; } mach_port_t localHost = mach_host_self(); mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; kern_return_t kr = host_statistics(localHost, HOST_CPU_LOAD_INFO, (host_info_t)&host_info, &count); if (kr != KERN_SUCCESS) return result; } task_t task = TaskPort(); if (task == TASK_NULL) return result; struct task_basic_info task_info; DNBError err; err = BasicInfo(task, &task_info); if (!err.Success()) return result; uint64_t elapsed_usec = 0; uint64_t task_used_usec = 0; if (scanType & eProfileCPU) { // Get current used time. struct timeval current_used_time; struct timeval tv; TIME_VALUE_TO_TIMEVAL(&task_info.user_time, ¤t_used_time); TIME_VALUE_TO_TIMEVAL(&task_info.system_time, &tv); timeradd(¤t_used_time, &tv, ¤t_used_time); task_used_usec = current_used_time.tv_sec * 1000000ULL + current_used_time.tv_usec; struct timeval current_elapsed_time; int res = gettimeofday(¤t_elapsed_time, NULL); if (res == 0) { elapsed_usec = current_elapsed_time.tv_sec * 1000000ULL + current_elapsed_time.tv_usec; } } std::vector<uint64_t> threads_id; std::vector<std::string> threads_name; std::vector<uint64_t> threads_used_usec; if (scanType & eProfileThreadsCPU) { get_threads_profile_data(scanType, task, m_process->ProcessID(), threads_id, threads_name, threads_used_usec); } struct vm_statistics vm_stats; uint64_t physical_memory; mach_vm_size_t rprvt = 0; mach_vm_size_t rsize = 0; mach_vm_size_t vprvt = 0; mach_vm_size_t vsize = 0; mach_vm_size_t dirty_size = 0; mach_vm_size_t purgeable = 0; mach_vm_size_t anonymous = 0; 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)) { std::ostringstream profile_data_stream; if (scanType & eProfileHostCPU) { profile_data_stream << "num_cpu:" << numCPU << ';'; profile_data_stream << "host_user_ticks:" << host_info.cpu_ticks[CPU_STATE_USER] << ';'; profile_data_stream << "host_sys_ticks:" << host_info.cpu_ticks[CPU_STATE_SYSTEM] << ';'; profile_data_stream << "host_idle_ticks:" << host_info.cpu_ticks[CPU_STATE_IDLE] << ';'; } if (scanType & eProfileCPU) { profile_data_stream << "elapsed_usec:" << elapsed_usec << ';'; profile_data_stream << "task_used_usec:" << task_used_usec << ';'; } if (scanType & eProfileThreadsCPU) { int num_threads = threads_id.size(); for (int i=0; i<num_threads; i++) { profile_data_stream << "thread_used_id:" << std::hex << threads_id[i] << std::dec << ';'; profile_data_stream << "thread_used_usec:" << threads_used_usec[i] << ';'; if (scanType & eProfileThreadName) { profile_data_stream << "thread_used_name:"; int len = threads_name[i].size(); if (len) { const char *thread_name = threads_name[i].c_str(); // Make sure that thread name doesn't interfere with our delimiter. profile_data_stream << RAW_HEXBASE << std::setw(2); const uint8_t *ubuf8 = (const uint8_t *)(thread_name); for (int j=0; j<len; j++) { profile_data_stream << (uint32_t)(ubuf8[j]); } // Reset back to DECIMAL. profile_data_stream << DECIMAL; } profile_data_stream << ';'; } } } if (scanType & eProfileHostMemory) profile_data_stream << "total:" << physical_memory << ';'; if (scanType & eProfileMemory) { static vm_size_t pagesize; static bool calculated = false; if (!calculated) { calculated = true; pagesize = PageSize(); } profile_data_stream << "wired:" << vm_stats.wire_count * pagesize << ';'; profile_data_stream << "active:" << vm_stats.active_count * pagesize << ';'; profile_data_stream << "inactive:" << vm_stats.inactive_count * pagesize << ';'; uint64_t total_used_count = vm_stats.wire_count + vm_stats.inactive_count + vm_stats.active_count; profile_data_stream << "used:" << total_used_count * pagesize << ';'; profile_data_stream << "free:" << vm_stats.free_count * pagesize << ';'; profile_data_stream << "rprvt:" << rprvt << ';'; profile_data_stream << "rsize:" << rsize << ';'; profile_data_stream << "vprvt:" << vprvt << ';'; profile_data_stream << "vsize:" << vsize << ';'; if (scanType & eProfileMemoryDirtyPage) profile_data_stream << "dirty:" << dirty_size << ';'; if (scanType & eProfileMemoryAnonymous) { profile_data_stream << "purgeable:" << purgeable << ';'; profile_data_stream << "anonymous:" << anonymous << ';'; } } profile_data_stream << "--end--;"; result = profile_data_stream.str(); } return result; }