// Copyright 2021 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/power_metrics/resource_coalition_mac.h" #include #include "base/check_op.h" #include "components/power_metrics/energy_impact_mac.h" #include "components/power_metrics/mach_time_mac.h" extern "C" int coalition_info_resource_usage( uint64_t cid, struct coalition_resource_usage* cru, size_t sz); namespace power_metrics { absl::optional GetProcessCoalitionId(base::ProcessId pid) { proc_pidcoalitioninfo coalition_info = {}; int res = proc_pidinfo(pid, PROC_PIDCOALITIONINFO, 0, &coalition_info, sizeof(coalition_info)); if (res != sizeof(coalition_info)) return absl::nullopt; return coalition_info.coalition_id[COALITION_TYPE_RESOURCE]; } std::unique_ptr GetCoalitionResourceUsage( int64_t coalition_id) { auto cru = std::make_unique(); uint64_t res = coalition_info_resource_usage( coalition_id, cru.get(), sizeof(coalition_resource_usage)); if (res == 0U) return cru; return nullptr; } coalition_resource_usage GetCoalitionResourceUsageDifference( const coalition_resource_usage& left, const coalition_resource_usage& right) { DCHECK_GE(left.tasks_started, right.tasks_started); DCHECK_GE(left.tasks_exited, right.tasks_exited); DCHECK_GE(left.time_nonempty, right.time_nonempty); DCHECK_GE(left.cpu_time, right.cpu_time); DCHECK_GE(left.interrupt_wakeups, right.interrupt_wakeups); DCHECK_GE(left.platform_idle_wakeups, right.platform_idle_wakeups); DCHECK_GE(left.bytesread, right.bytesread); DCHECK_GE(left.byteswritten, right.byteswritten); DCHECK_GE(left.gpu_time, right.gpu_time); DCHECK_GE(left.cpu_time_billed_to_me, right.cpu_time_billed_to_me); DCHECK_GE(left.cpu_time_billed_to_others, right.cpu_time_billed_to_others); DCHECK_GE(left.energy, right.energy); DCHECK_GE(left.logical_immediate_writes, right.logical_immediate_writes); DCHECK_GE(left.logical_deferred_writes, right.logical_deferred_writes); DCHECK_GE(left.logical_invalidated_writes, right.logical_invalidated_writes); DCHECK_GE(left.logical_metadata_writes, right.logical_metadata_writes); DCHECK_GE(left.logical_immediate_writes_to_external, right.logical_immediate_writes_to_external); DCHECK_GE(left.logical_deferred_writes_to_external, right.logical_deferred_writes_to_external); DCHECK_GE(left.logical_invalidated_writes_to_external, right.logical_invalidated_writes_to_external); DCHECK_GE(left.logical_metadata_writes_to_external, right.logical_metadata_writes_to_external); DCHECK_GE(left.energy_billed_to_me, right.energy_billed_to_me); DCHECK_GE(left.energy_billed_to_others, right.energy_billed_to_others); DCHECK_GE(left.cpu_ptime, right.cpu_ptime); DCHECK_GE(left.cpu_instructions, right.cpu_instructions); DCHECK_GE(left.cpu_cycles, right.cpu_cycles); DCHECK_GE(left.fs_metadata_writes, right.fs_metadata_writes); DCHECK_GE(left.pm_writes, right.pm_writes); coalition_resource_usage ret; ret.tasks_started = left.tasks_started - right.tasks_started; ret.tasks_exited = left.tasks_exited - right.tasks_exited; ret.time_nonempty = left.time_nonempty - right.time_nonempty; ret.cpu_time = left.cpu_time - right.cpu_time; ret.interrupt_wakeups = left.interrupt_wakeups - right.interrupt_wakeups; ret.platform_idle_wakeups = left.platform_idle_wakeups - right.platform_idle_wakeups; ret.bytesread = left.bytesread - right.bytesread; ret.byteswritten = left.byteswritten - right.byteswritten; ret.gpu_time = left.gpu_time - right.gpu_time; ret.cpu_time_billed_to_me = left.cpu_time_billed_to_me - right.cpu_time_billed_to_me; ret.cpu_time_billed_to_others = left.cpu_time_billed_to_others - right.cpu_time_billed_to_others; ret.energy = left.energy - right.energy; ret.logical_immediate_writes = left.logical_immediate_writes - right.logical_immediate_writes; ret.logical_deferred_writes = left.logical_deferred_writes - right.logical_deferred_writes; ret.logical_invalidated_writes = left.logical_invalidated_writes - right.logical_invalidated_writes; ret.logical_metadata_writes = left.logical_metadata_writes - right.logical_metadata_writes; ret.logical_immediate_writes_to_external = left.logical_immediate_writes_to_external - right.logical_immediate_writes_to_external; ret.logical_deferred_writes_to_external = left.logical_deferred_writes_to_external - right.logical_deferred_writes_to_external; ret.logical_invalidated_writes_to_external = left.logical_invalidated_writes_to_external - right.logical_invalidated_writes_to_external; ret.logical_metadata_writes_to_external = left.logical_metadata_writes_to_external - right.logical_metadata_writes_to_external; ret.energy_billed_to_me = left.energy_billed_to_me - right.energy_billed_to_me; ret.energy_billed_to_others = left.energy_billed_to_others - right.energy_billed_to_others; ret.cpu_ptime = left.cpu_ptime - right.cpu_ptime; DCHECK_EQ(left.cpu_time_eqos_len, static_cast(COALITION_NUM_THREAD_QOS_TYPES)); DCHECK_EQ(right.cpu_time_eqos_len, static_cast(COALITION_NUM_THREAD_QOS_TYPES)); ret.cpu_time_eqos_len = left.cpu_time_eqos_len; for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) { DCHECK_GE(left.cpu_time_eqos[i], right.cpu_time_eqos[i]); ret.cpu_time_eqos[i] = left.cpu_time_eqos[i] - right.cpu_time_eqos[i]; } ret.cpu_instructions = left.cpu_instructions - right.cpu_instructions; ret.cpu_cycles = left.cpu_cycles - right.cpu_cycles; ret.fs_metadata_writes = left.fs_metadata_writes - right.fs_metadata_writes; ret.pm_writes = left.pm_writes - right.pm_writes; return ret; } absl::optional GetCoalitionResourceUsageRate( const coalition_resource_usage& begin, const coalition_resource_usage& end, base::TimeDelta interval_duration, mach_timebase_info_data_t timebase, absl::optional energy_impact_coefficients) { // Validate that |end| >= |begin|. bool end_greater_or_equal_begin = std::tie(end.cpu_time, end.interrupt_wakeups, end.platform_idle_wakeups, end.bytesread, end.byteswritten, end.gpu_time, end.energy) >= std::tie(begin.cpu_time, begin.interrupt_wakeups, begin.platform_idle_wakeups, begin.bytesread, begin.byteswritten, begin.gpu_time, begin.energy); for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) { if (end.cpu_time_eqos[i] < begin.cpu_time_eqos[i]) end_greater_or_equal_begin = false; } if (!end_greater_or_equal_begin) return absl::nullopt; auto get_rate_per_second = [&interval_duration](uint64_t begin, uint64_t end) -> double { DCHECK_GE(end, begin); uint64_t diff = end - begin; return diff / interval_duration.InSecondsF(); }; auto get_time_rate_per_second = [&interval_duration, &timebase]( uint64_t begin, uint64_t end) -> double { DCHECK_GE(end, begin); // Compute the delta in s, being careful to avoid truncation due to integral // division. double delta_sample_s = power_metrics::MachTimeToNs(end - begin, timebase) / static_cast(base::Time::kNanosecondsPerSecond); return delta_sample_s / interval_duration.InSecondsF(); }; CoalitionResourceUsageRate result; result.cpu_time_per_second = get_time_rate_per_second(begin.cpu_time, end.cpu_time); result.interrupt_wakeups_per_second = get_rate_per_second(begin.interrupt_wakeups, end.interrupt_wakeups); result.platform_idle_wakeups_per_second = get_rate_per_second( begin.platform_idle_wakeups, end.platform_idle_wakeups); result.bytesread_per_second = get_rate_per_second(begin.bytesread, end.bytesread); result.byteswritten_per_second = get_rate_per_second(begin.byteswritten, end.byteswritten); result.gpu_time_per_second = get_time_rate_per_second(begin.gpu_time, end.gpu_time); result.power_nw = get_rate_per_second(begin.energy, end.energy); for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) { result.qos_time_per_second[i] = get_time_rate_per_second(begin.cpu_time_eqos[i], end.cpu_time_eqos[i]); } if (energy_impact_coefficients.has_value()) { result.energy_impact_per_second = (ComputeEnergyImpactForResourceUsage( end, energy_impact_coefficients.value(), timebase) - ComputeEnergyImpactForResourceUsage( begin, energy_impact_coefficients.value(), timebase)) / interval_duration.InSecondsF(); } return result; } } // power_metrics