+ vm_info->virtual_size = (typeof(vm_info->virtual_size))map->size;
+ vm_info->region_count = map->hdr.nentries;
+ vm_info->page_size = vm_map_page_size(map);
+
+ vm_info->resident_size = pmap_resident_count(map->pmap);
+ vm_info->resident_size *= PAGE_SIZE;
+ vm_info->resident_size_peak = pmap_resident_max(map->pmap);
+ vm_info->resident_size_peak *= PAGE_SIZE;
+
+#define _VM_INFO(_name) \
+ vm_info->_name = ((mach_vm_size_t) map->pmap->stats._name) * PAGE_SIZE
+
+ _VM_INFO(device);
+ _VM_INFO(device_peak);
+ _VM_INFO(external);
+ _VM_INFO(external_peak);
+ _VM_INFO(internal);
+ _VM_INFO(internal_peak);
+ _VM_INFO(reusable);
+ _VM_INFO(reusable_peak);
+ _VM_INFO(compressed);
+ _VM_INFO(compressed_peak);
+ _VM_INFO(compressed_lifetime);
+
+ vm_info->purgeable_volatile_pmap = 0;
+ vm_info->purgeable_volatile_resident = 0;
+ vm_info->purgeable_volatile_virtual = 0;
+ if (task == kernel_task) {
+ /*
+ * We do not maintain the detailed stats for the
+ * kernel_pmap, so just count everything as
+ * "internal"...
+ */
+ vm_info->internal = vm_info->resident_size;
+ /*
+ * ... but since the memory held by the VM compressor
+ * in the kernel address space ought to be attributed
+ * to user-space tasks, we subtract it from "internal"
+ * to give memory reporting tools a more accurate idea
+ * of what the kernel itself is actually using, instead
+ * of making it look like the kernel is leaking memory
+ * when the system is under memory pressure.
+ */
+ vm_info->internal -= (VM_PAGE_COMPRESSOR_COUNT *
+ PAGE_SIZE);
+ } else {
+ mach_vm_size_t volatile_virtual_size;
+ mach_vm_size_t volatile_resident_size;
+ mach_vm_size_t volatile_compressed_size;
+ mach_vm_size_t volatile_pmap_size;
+ mach_vm_size_t volatile_compressed_pmap_size;
+ kern_return_t kr;
+
+ if (flavor == TASK_VM_INFO_PURGEABLE) {
+ kr = vm_map_query_volatile(
+ map,
+ &volatile_virtual_size,
+ &volatile_resident_size,
+ &volatile_compressed_size,
+ &volatile_pmap_size,
+ &volatile_compressed_pmap_size);
+ if (kr == KERN_SUCCESS) {
+ vm_info->purgeable_volatile_pmap =
+ volatile_pmap_size;
+ if (radar_20146450) {
+ vm_info->compressed -=
+ volatile_compressed_pmap_size;
+ }
+ vm_info->purgeable_volatile_resident =
+ volatile_resident_size;
+ vm_info->purgeable_volatile_virtual =
+ volatile_virtual_size;
+ }
+ }
+ }
+ *task_info_count = TASK_VM_INFO_REV0_COUNT;
+
+ if (original_task_info_count >= TASK_VM_INFO_REV1_COUNT) {
+ vm_info->phys_footprint =
+ (mach_vm_size_t) get_task_phys_footprint(task);
+ *task_info_count = TASK_VM_INFO_REV1_COUNT;
+ }
+ if (original_task_info_count >= TASK_VM_INFO_REV2_COUNT) {
+ vm_info->min_address = map->min_offset;
+ vm_info->max_address = map->max_offset;
+ *task_info_count = TASK_VM_INFO_REV2_COUNT;
+ }
+ if (original_task_info_count >= TASK_VM_INFO_REV3_COUNT) {
+ ledger_get_lifetime_max(task->ledger,
+ task_ledgers.phys_footprint,
+ &vm_info->ledger_phys_footprint_peak);
+ ledger_get_balance(task->ledger,
+ task_ledgers.purgeable_nonvolatile,
+ &vm_info->ledger_purgeable_nonvolatile);
+ ledger_get_balance(task->ledger,
+ task_ledgers.purgeable_nonvolatile_compressed,
+ &vm_info->ledger_purgeable_novolatile_compressed);
+ ledger_get_balance(task->ledger,
+ task_ledgers.purgeable_volatile,
+ &vm_info->ledger_purgeable_volatile);
+ ledger_get_balance(task->ledger,
+ task_ledgers.purgeable_volatile_compressed,
+ &vm_info->ledger_purgeable_volatile_compressed);
+ ledger_get_balance(task->ledger,
+ task_ledgers.network_nonvolatile,
+ &vm_info->ledger_tag_network_nonvolatile);
+ ledger_get_balance(task->ledger,
+ task_ledgers.network_nonvolatile_compressed,
+ &vm_info->ledger_tag_network_nonvolatile_compressed);
+ ledger_get_balance(task->ledger,
+ task_ledgers.network_volatile,
+ &vm_info->ledger_tag_network_volatile);
+ ledger_get_balance(task->ledger,
+ task_ledgers.network_volatile_compressed,
+ &vm_info->ledger_tag_network_volatile_compressed);
+ ledger_get_balance(task->ledger,
+ task_ledgers.media_footprint,
+ &vm_info->ledger_tag_media_footprint);
+ ledger_get_balance(task->ledger,
+ task_ledgers.media_footprint_compressed,
+ &vm_info->ledger_tag_media_footprint_compressed);
+ ledger_get_balance(task->ledger,
+ task_ledgers.media_nofootprint,
+ &vm_info->ledger_tag_media_nofootprint);
+ ledger_get_balance(task->ledger,
+ task_ledgers.media_nofootprint_compressed,
+ &vm_info->ledger_tag_media_nofootprint_compressed);
+ ledger_get_balance(task->ledger,
+ task_ledgers.graphics_footprint,
+ &vm_info->ledger_tag_graphics_footprint);
+ ledger_get_balance(task->ledger,
+ task_ledgers.graphics_footprint_compressed,
+ &vm_info->ledger_tag_graphics_footprint_compressed);
+ ledger_get_balance(task->ledger,
+ task_ledgers.graphics_nofootprint,
+ &vm_info->ledger_tag_graphics_nofootprint);
+ ledger_get_balance(task->ledger,
+ task_ledgers.graphics_nofootprint_compressed,
+ &vm_info->ledger_tag_graphics_nofootprint_compressed);
+ ledger_get_balance(task->ledger,
+ task_ledgers.neural_footprint,
+ &vm_info->ledger_tag_neural_footprint);
+ ledger_get_balance(task->ledger,
+ task_ledgers.neural_footprint_compressed,
+ &vm_info->ledger_tag_neural_footprint_compressed);
+ ledger_get_balance(task->ledger,
+ task_ledgers.neural_nofootprint,
+ &vm_info->ledger_tag_neural_nofootprint);
+ ledger_get_balance(task->ledger,
+ task_ledgers.neural_nofootprint_compressed,
+ &vm_info->ledger_tag_neural_nofootprint_compressed);
+ *task_info_count = TASK_VM_INFO_REV3_COUNT;
+ }
+ if (original_task_info_count >= TASK_VM_INFO_REV4_COUNT) {
+ if (task->bsd_info) {
+ vm_info->limit_bytes_remaining =
+ memorystatus_available_memory_internal(task->bsd_info);
+ } else {
+ vm_info->limit_bytes_remaining = 0;
+ }
+ *task_info_count = TASK_VM_INFO_REV4_COUNT;
+ }
+ if (original_task_info_count >= TASK_VM_INFO_REV5_COUNT) {
+ thread_t thread;
+ integer_t total = task->decompressions;
+ queue_iterate(&task->threads, thread, thread_t, task_threads) {
+ total += thread->decompressions;
+ }
+ vm_info->decompressions = total;
+ *task_info_count = TASK_VM_INFO_REV5_COUNT;
+ }
+
+ if (task != kernel_task) {
+ vm_map_unlock_read(map);
+ }
+
+ break;
+ }
+
+ case TASK_WAIT_STATE_INFO:
+ {
+ /*
+ * Deprecated flavor. Currently allowing some results until all users
+ * stop calling it. The results may not be accurate.
+ */
+ task_wait_state_info_t wait_state_info;
+ uint64_t total_sfi_ledger_val = 0;
+
+ if (*task_info_count < TASK_WAIT_STATE_INFO_COUNT) {
+ error = KERN_INVALID_ARGUMENT;
+ break;
+ }
+
+ wait_state_info = (task_wait_state_info_t) task_info_out;
+
+ wait_state_info->total_wait_state_time = 0;
+ bzero(wait_state_info->_reserved, sizeof(wait_state_info->_reserved));
+
+#if CONFIG_SCHED_SFI
+ int i, prev_lentry = -1;
+ int64_t val_credit, val_debit;
+
+ for (i = 0; i < MAX_SFI_CLASS_ID; i++) {
+ val_credit = 0;
+ /*
+ * checking with prev_lentry != entry ensures adjacent classes
+ * which share the same ledger do not add wait times twice.
+ * Note: Use ledger() call to get data for each individual sfi class.
+ */
+ if (prev_lentry != task_ledgers.sfi_wait_times[i] &&
+ KERN_SUCCESS == ledger_get_entries(task->ledger,
+ task_ledgers.sfi_wait_times[i], &val_credit, &val_debit)) {
+ total_sfi_ledger_val += val_credit;
+ }
+ prev_lentry = task_ledgers.sfi_wait_times[i];
+ }
+
+#endif /* CONFIG_SCHED_SFI */
+ wait_state_info->total_wait_sfi_state_time = total_sfi_ledger_val;
+ *task_info_count = TASK_WAIT_STATE_INFO_COUNT;
+
+ break;
+ }
+ case TASK_VM_INFO_PURGEABLE_ACCOUNT:
+ {
+#if DEVELOPMENT || DEBUG
+ pvm_account_info_t acnt_info;
+
+ if (*task_info_count < PVM_ACCOUNT_INFO_COUNT) {
+ error = KERN_INVALID_ARGUMENT;
+ break;
+ }
+
+ if (task_info_out == NULL) {
+ error = KERN_INVALID_ARGUMENT;
+ break;
+ }
+
+ acnt_info = (pvm_account_info_t) task_info_out;
+
+ error = vm_purgeable_account(task, acnt_info);
+
+ *task_info_count = PVM_ACCOUNT_INFO_COUNT;
+
+ break;
+#else /* DEVELOPMENT || DEBUG */
+ error = KERN_NOT_SUPPORTED;
+ break;
+#endif /* DEVELOPMENT || DEBUG */
+ }
+ case TASK_FLAGS_INFO:
+ {
+ task_flags_info_t flags_info;
+
+ if (*task_info_count < TASK_FLAGS_INFO_COUNT) {
+ error = KERN_INVALID_ARGUMENT;
+ break;
+ }
+
+ flags_info = (task_flags_info_t)task_info_out;
+
+ /* only publish the 64-bit flag of the task */
+ flags_info->flags = task->t_flags & (TF_64B_ADDR | TF_64B_DATA);
+
+ *task_info_count = TASK_FLAGS_INFO_COUNT;
+ break;
+ }
+
+ case TASK_DEBUG_INFO_INTERNAL:
+ {
+#if DEVELOPMENT || DEBUG
+ task_debug_info_internal_t dbg_info;
+ ipc_space_t space = task->itk_space;
+ if (*task_info_count < TASK_DEBUG_INFO_INTERNAL_COUNT) {
+ error = KERN_NOT_SUPPORTED;
+ break;
+ }
+
+ if (task_info_out == NULL) {
+ error = KERN_INVALID_ARGUMENT;
+ break;
+ }
+ dbg_info = (task_debug_info_internal_t) task_info_out;
+ dbg_info->ipc_space_size = 0;
+
+ if (space) {
+ is_read_lock(space);
+ dbg_info->ipc_space_size = space->is_table_size;
+ is_read_unlock(space);
+ }
+
+ dbg_info->suspend_count = task->suspend_count;
+
+ error = KERN_SUCCESS;
+ *task_info_count = TASK_DEBUG_INFO_INTERNAL_COUNT;
+ break;
+#else /* DEVELOPMENT || DEBUG */
+ error = KERN_NOT_SUPPORTED;
+ break;
+#endif /* DEVELOPMENT || DEBUG */
+ }
+ default:
+ error = KERN_INVALID_ARGUMENT;
+ }
+
+ task_unlock(task);
+ return error;
+}
+
+/*
+ * task_info_from_user
+ *
+ * When calling task_info from user space,
+ * this function will be executed as mig server side
+ * instead of calling directly into task_info.
+ * This gives the possibility to perform more security
+ * checks on task_port.
+ *
+ * In the case of TASK_DYLD_INFO, we require the more
+ * privileged task_port not the less-privileged task_name_port.
+ *
+ */
+kern_return_t
+task_info_from_user(
+ mach_port_t task_port,
+ task_flavor_t flavor,
+ task_info_t task_info_out,
+ mach_msg_type_number_t *task_info_count)
+{
+ task_t task;
+ kern_return_t ret;
+
+ if (flavor == TASK_DYLD_INFO) {
+ task = convert_port_to_task(task_port);
+ } else {
+ task = convert_port_to_task_name(task_port);
+ }
+
+ ret = task_info(task, flavor, task_info_out, task_info_count);
+
+ task_deallocate(task);
+
+ return ret;
+}
+
+/*
+ * task_power_info
+ *
+ * Returns power stats for the task.
+ * Note: Called with task locked.
+ */
+void
+task_power_info_locked(
+ task_t task,
+ task_power_info_t info,
+ gpu_energy_data_t ginfo,
+ task_power_info_v2_t infov2,
+ uint64_t *runnable_time)
+{
+ thread_t thread;
+ ledger_amount_t tmp;
+
+ uint64_t runnable_time_sum = 0;
+
+ task_lock_assert_owned(task);
+
+ ledger_get_entries(task->ledger, task_ledgers.interrupt_wakeups,
+ (ledger_amount_t *)&info->task_interrupt_wakeups, &tmp);
+ ledger_get_entries(task->ledger, task_ledgers.platform_idle_wakeups,
+ (ledger_amount_t *)&info->task_platform_idle_wakeups, &tmp);
+
+ info->task_timer_wakeups_bin_1 = task->task_timer_wakeups_bin_1;
+ info->task_timer_wakeups_bin_2 = task->task_timer_wakeups_bin_2;
+
+ info->total_user = task->total_user_time;
+ info->total_system = task->total_system_time;
+ runnable_time_sum = task->total_runnable_time;
+
+#if CONFIG_EMBEDDED
+ if (infov2) {
+ infov2->task_energy = task->task_energy;
+ }
+#endif
+
+ if (ginfo) {
+ ginfo->task_gpu_utilisation = task->task_gpu_ns;
+ }
+
+ if (infov2) {
+ infov2->task_ptime = task->total_ptime;
+ infov2->task_pset_switches = task->ps_switch;
+ }
+
+ queue_iterate(&task->threads, thread, thread_t, task_threads) {
+ uint64_t tval;
+ spl_t x;
+
+ if (thread->options & TH_OPT_IDLE_THREAD) {
+ continue;
+ }
+
+ x = splsched();
+ thread_lock(thread);
+
+ info->task_timer_wakeups_bin_1 += thread->thread_timer_wakeups_bin_1;
+ info->task_timer_wakeups_bin_2 += thread->thread_timer_wakeups_bin_2;
+
+#if CONFIG_EMBEDDED
+ if (infov2) {
+ infov2->task_energy += ml_energy_stat(thread);
+ }
+#endif
+
+ tval = timer_grab(&thread->user_timer);
+ info->total_user += tval;
+
+ if (infov2) {
+ tval = timer_grab(&thread->ptime);
+ infov2->task_ptime += tval;
+ infov2->task_pset_switches += thread->ps_switch;
+ }
+
+ tval = timer_grab(&thread->system_timer);
+ if (thread->precise_user_kernel_time) {
+ info->total_system += tval;
+ } else {
+ /* system_timer may represent either sys or user */
+ info->total_user += tval;
+ }
+
+ tval = timer_grab(&thread->runnable_timer);
+
+ runnable_time_sum += tval;
+
+ if (ginfo) {
+ ginfo->task_gpu_utilisation += ml_gpu_stat(thread);
+ }
+ thread_unlock(thread);
+ splx(x);
+ }
+
+ if (runnable_time) {
+ *runnable_time = runnable_time_sum;
+ }
+}
+
+/*
+ * task_gpu_utilisation
+ *
+ * Returns the total gpu time used by the all the threads of the task
+ * (both dead and alive)
+ */
+uint64_t
+task_gpu_utilisation(
+ task_t task)
+{
+ uint64_t gpu_time = 0;
+#if !CONFIG_EMBEDDED
+ thread_t thread;
+
+ task_lock(task);
+ gpu_time += task->task_gpu_ns;
+
+ queue_iterate(&task->threads, thread, thread_t, task_threads) {
+ spl_t x;
+ x = splsched();
+ thread_lock(thread);
+ gpu_time += ml_gpu_stat(thread);
+ thread_unlock(thread);
+ splx(x);
+ }
+
+ task_unlock(task);
+#else /* CONFIG_EMBEDDED */
+ /* silence compiler warning */
+ (void)task;
+#endif /* !CONFIG_EMBEDDED */
+ return gpu_time;
+}
+
+/*
+ * task_energy
+ *
+ * Returns the total energy used by the all the threads of the task
+ * (both dead and alive)
+ */
+uint64_t
+task_energy(
+ task_t task)
+{
+ uint64_t energy = 0;
+ thread_t thread;
+
+ task_lock(task);
+ energy += task->task_energy;
+
+ queue_iterate(&task->threads, thread, thread_t, task_threads) {
+ spl_t x;
+ x = splsched();
+ thread_lock(thread);
+ energy += ml_energy_stat(thread);
+ thread_unlock(thread);
+ splx(x);
+ }
+
+ task_unlock(task);
+ return energy;
+}
+
+#if __AMP__
+
+uint64_t
+task_cpu_ptime(
+ task_t task)
+{
+ uint64_t cpu_ptime = 0;
+ thread_t thread;
+
+ task_lock(task);
+ cpu_ptime += task->total_ptime;
+
+ queue_iterate(&task->threads, thread, thread_t, task_threads) {
+ cpu_ptime += timer_grab(&thread->ptime);
+ }
+
+ task_unlock(task);
+ return cpu_ptime;
+}
+
+#else /* __AMP__ */
+
+uint64_t
+task_cpu_ptime(
+ __unused task_t task)
+{
+ return 0;
+}
+
+#endif /* __AMP__ */
+
+/* This function updates the cpu time in the arrays for each
+ * effective and requested QoS class
+ */
+void
+task_update_cpu_time_qos_stats(
+ task_t task,
+ uint64_t *eqos_stats,
+ uint64_t *rqos_stats)
+{
+ if (!eqos_stats && !rqos_stats) {
+ return;
+ }
+
+ task_lock(task);
+ thread_t thread;
+ queue_iterate(&task->threads, thread, thread_t, task_threads) {
+ if (thread->options & TH_OPT_IDLE_THREAD) {
+ continue;
+ }
+
+ thread_update_qos_cpu_time(thread);
+ }
+
+ if (eqos_stats) {
+ eqos_stats[THREAD_QOS_DEFAULT] += task->cpu_time_eqos_stats.cpu_time_qos_default;
+ eqos_stats[THREAD_QOS_MAINTENANCE] += task->cpu_time_eqos_stats.cpu_time_qos_maintenance;
+ eqos_stats[THREAD_QOS_BACKGROUND] += task->cpu_time_eqos_stats.cpu_time_qos_background;
+ eqos_stats[THREAD_QOS_UTILITY] += task->cpu_time_eqos_stats.cpu_time_qos_utility;
+ eqos_stats[THREAD_QOS_LEGACY] += task->cpu_time_eqos_stats.cpu_time_qos_legacy;
+ eqos_stats[THREAD_QOS_USER_INITIATED] += task->cpu_time_eqos_stats.cpu_time_qos_user_initiated;
+ eqos_stats[THREAD_QOS_USER_INTERACTIVE] += task->cpu_time_eqos_stats.cpu_time_qos_user_interactive;
+ }
+
+ if (rqos_stats) {
+ rqos_stats[THREAD_QOS_DEFAULT] += task->cpu_time_rqos_stats.cpu_time_qos_default;
+ rqos_stats[THREAD_QOS_MAINTENANCE] += task->cpu_time_rqos_stats.cpu_time_qos_maintenance;
+ rqos_stats[THREAD_QOS_BACKGROUND] += task->cpu_time_rqos_stats.cpu_time_qos_background;
+ rqos_stats[THREAD_QOS_UTILITY] += task->cpu_time_rqos_stats.cpu_time_qos_utility;
+ rqos_stats[THREAD_QOS_LEGACY] += task->cpu_time_rqos_stats.cpu_time_qos_legacy;
+ rqos_stats[THREAD_QOS_USER_INITIATED] += task->cpu_time_rqos_stats.cpu_time_qos_user_initiated;
+ rqos_stats[THREAD_QOS_USER_INTERACTIVE] += task->cpu_time_rqos_stats.cpu_time_qos_user_interactive;
+ }
+
+ task_unlock(task);
+}
+
+kern_return_t
+task_purgable_info(
+ task_t task,
+ task_purgable_info_t *stats)
+{
+ if (task == TASK_NULL || stats == NULL) {
+ return KERN_INVALID_ARGUMENT;
+ }
+ /* Take task reference */
+ task_reference(task);
+ vm_purgeable_stats((vm_purgeable_info_t)stats, task);
+ /* Drop task reference */
+ task_deallocate(task);
+ return KERN_SUCCESS;
+}
+
+void
+task_vtimer_set(
+ task_t task,
+ integer_t which)
+{
+ thread_t thread;
+ spl_t x;
+
+ task_lock(task);
+
+ task->vtimers |= which;
+
+ switch (which) {
+ case TASK_VTIMER_USER:
+ queue_iterate(&task->threads, thread, thread_t, task_threads) {
+ x = splsched();
+ thread_lock(thread);
+ if (thread->precise_user_kernel_time) {
+ thread->vtimer_user_save = timer_grab(&thread->user_timer);
+ } else {
+ thread->vtimer_user_save = timer_grab(&thread->system_timer);
+ }
+ thread_unlock(thread);
+ splx(x);
+ }
+ break;
+
+ case TASK_VTIMER_PROF:
+ queue_iterate(&task->threads, thread, thread_t, task_threads) {
+ x = splsched();
+ thread_lock(thread);
+ thread->vtimer_prof_save = timer_grab(&thread->user_timer);
+ thread->vtimer_prof_save += timer_grab(&thread->system_timer);
+ thread_unlock(thread);
+ splx(x);
+ }
+ break;
+
+ case TASK_VTIMER_RLIM:
+ queue_iterate(&task->threads, thread, thread_t, task_threads) {
+ x = splsched();
+ thread_lock(thread);
+ thread->vtimer_rlim_save = timer_grab(&thread->user_timer);
+ thread->vtimer_rlim_save += timer_grab(&thread->system_timer);
+ thread_unlock(thread);
+ splx(x);
+ }
+ break;
+ }
+
+ task_unlock(task);
+}
+
+void
+task_vtimer_clear(
+ task_t task,
+ integer_t which)
+{
+ assert(task == current_task());
+
+ task_lock(task);