1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/power_metrics/resource_coalition_mac.h"
9 #include "base/check_op.h"
10 #include "components/power_metrics/energy_impact_mac.h"
11 #include "components/power_metrics/mach_time_mac.h"
13 extern "C" int coalition_info_resource_usage(
15 struct coalition_resource_usage* cru,
18 namespace power_metrics {
20 absl::optional<uint64_t> GetProcessCoalitionId(base::ProcessId pid) {
21 proc_pidcoalitioninfo coalition_info = {};
22 int res = proc_pidinfo(pid, PROC_PIDCOALITIONINFO, 0, &coalition_info,
23 sizeof(coalition_info));
25 if (res != sizeof(coalition_info))
28 return coalition_info.coalition_id[COALITION_TYPE_RESOURCE];
31 std::unique_ptr<coalition_resource_usage> GetCoalitionResourceUsage(
32 int64_t coalition_id) {
33 auto cru = std::make_unique<coalition_resource_usage>();
34 uint64_t res = coalition_info_resource_usage(
35 coalition_id, cru.get(), sizeof(coalition_resource_usage));
41 coalition_resource_usage GetCoalitionResourceUsageDifference(
42 const coalition_resource_usage& left,
43 const coalition_resource_usage& right) {
44 DCHECK_GE(left.tasks_started, right.tasks_started);
45 DCHECK_GE(left.tasks_exited, right.tasks_exited);
46 DCHECK_GE(left.time_nonempty, right.time_nonempty);
47 DCHECK_GE(left.cpu_time, right.cpu_time);
48 DCHECK_GE(left.interrupt_wakeups, right.interrupt_wakeups);
49 DCHECK_GE(left.platform_idle_wakeups, right.platform_idle_wakeups);
50 DCHECK_GE(left.bytesread, right.bytesread);
51 DCHECK_GE(left.byteswritten, right.byteswritten);
52 DCHECK_GE(left.gpu_time, right.gpu_time);
53 DCHECK_GE(left.cpu_time_billed_to_me, right.cpu_time_billed_to_me);
54 DCHECK_GE(left.cpu_time_billed_to_others, right.cpu_time_billed_to_others);
55 DCHECK_GE(left.energy, right.energy);
56 DCHECK_GE(left.logical_immediate_writes, right.logical_immediate_writes);
57 DCHECK_GE(left.logical_deferred_writes, right.logical_deferred_writes);
58 DCHECK_GE(left.logical_invalidated_writes, right.logical_invalidated_writes);
59 DCHECK_GE(left.logical_metadata_writes, right.logical_metadata_writes);
60 DCHECK_GE(left.logical_immediate_writes_to_external,
61 right.logical_immediate_writes_to_external);
62 DCHECK_GE(left.logical_deferred_writes_to_external,
63 right.logical_deferred_writes_to_external);
64 DCHECK_GE(left.logical_invalidated_writes_to_external,
65 right.logical_invalidated_writes_to_external);
66 DCHECK_GE(left.logical_metadata_writes_to_external,
67 right.logical_metadata_writes_to_external);
68 DCHECK_GE(left.energy_billed_to_me, right.energy_billed_to_me);
69 DCHECK_GE(left.energy_billed_to_others, right.energy_billed_to_others);
70 DCHECK_GE(left.cpu_ptime, right.cpu_ptime);
71 DCHECK_GE(left.cpu_instructions, right.cpu_instructions);
72 DCHECK_GE(left.cpu_cycles, right.cpu_cycles);
73 DCHECK_GE(left.fs_metadata_writes, right.fs_metadata_writes);
74 DCHECK_GE(left.pm_writes, right.pm_writes);
76 coalition_resource_usage ret;
78 ret.tasks_started = left.tasks_started - right.tasks_started;
79 ret.tasks_exited = left.tasks_exited - right.tasks_exited;
80 ret.time_nonempty = left.time_nonempty - right.time_nonempty;
81 ret.cpu_time = left.cpu_time - right.cpu_time;
82 ret.interrupt_wakeups = left.interrupt_wakeups - right.interrupt_wakeups;
83 ret.platform_idle_wakeups =
84 left.platform_idle_wakeups - right.platform_idle_wakeups;
85 ret.bytesread = left.bytesread - right.bytesread;
86 ret.byteswritten = left.byteswritten - right.byteswritten;
87 ret.gpu_time = left.gpu_time - right.gpu_time;
88 ret.cpu_time_billed_to_me =
89 left.cpu_time_billed_to_me - right.cpu_time_billed_to_me;
90 ret.cpu_time_billed_to_others =
91 left.cpu_time_billed_to_others - right.cpu_time_billed_to_others;
92 ret.energy = left.energy - right.energy;
93 ret.logical_immediate_writes =
94 left.logical_immediate_writes - right.logical_immediate_writes;
95 ret.logical_deferred_writes =
96 left.logical_deferred_writes - right.logical_deferred_writes;
97 ret.logical_invalidated_writes =
98 left.logical_invalidated_writes - right.logical_invalidated_writes;
99 ret.logical_metadata_writes =
100 left.logical_metadata_writes - right.logical_metadata_writes;
101 ret.logical_immediate_writes_to_external =
102 left.logical_immediate_writes_to_external -
103 right.logical_immediate_writes_to_external;
104 ret.logical_deferred_writes_to_external =
105 left.logical_deferred_writes_to_external -
106 right.logical_deferred_writes_to_external;
107 ret.logical_invalidated_writes_to_external =
108 left.logical_invalidated_writes_to_external -
109 right.logical_invalidated_writes_to_external;
110 ret.logical_metadata_writes_to_external =
111 left.logical_metadata_writes_to_external -
112 right.logical_metadata_writes_to_external;
113 ret.energy_billed_to_me =
114 left.energy_billed_to_me - right.energy_billed_to_me;
115 ret.energy_billed_to_others =
116 left.energy_billed_to_others - right.energy_billed_to_others;
117 ret.cpu_ptime = left.cpu_ptime - right.cpu_ptime;
119 DCHECK_EQ(left.cpu_time_eqos_len,
120 static_cast<uint64_t>(COALITION_NUM_THREAD_QOS_TYPES));
121 DCHECK_EQ(right.cpu_time_eqos_len,
122 static_cast<uint64_t>(COALITION_NUM_THREAD_QOS_TYPES));
124 ret.cpu_time_eqos_len = left.cpu_time_eqos_len;
125 for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
126 if (right.cpu_time_eqos[i] > left.cpu_time_eqos[i]) {
127 // TODO(fdoray): Investigate why this happens. In the meantime, pretend
128 // that there was no CPU time at this QoS.
129 ret.cpu_time_eqos[i] = 0;
131 ret.cpu_time_eqos[i] = left.cpu_time_eqos[i] - right.cpu_time_eqos[i];
135 ret.cpu_instructions = left.cpu_instructions - right.cpu_instructions;
136 ret.cpu_cycles = left.cpu_cycles - right.cpu_cycles;
137 ret.fs_metadata_writes = left.fs_metadata_writes - right.fs_metadata_writes;
138 ret.pm_writes = left.pm_writes - right.pm_writes;
143 absl::optional<CoalitionResourceUsageRate> GetCoalitionResourceUsageRate(
144 const coalition_resource_usage& begin,
145 const coalition_resource_usage& end,
146 base::TimeDelta interval_duration,
147 mach_timebase_info_data_t timebase,
148 absl::optional<EnergyImpactCoefficients> energy_impact_coefficients) {
149 // Validate that |end| >= |begin|.
150 bool end_greater_or_equal_begin =
151 std::tie(end.cpu_time, end.interrupt_wakeups, end.platform_idle_wakeups,
152 end.bytesread, end.byteswritten, end.gpu_time, end.energy) >=
153 std::tie(begin.cpu_time, begin.interrupt_wakeups,
154 begin.platform_idle_wakeups, begin.bytesread, begin.byteswritten,
155 begin.gpu_time, begin.energy);
156 for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
157 if (end.cpu_time_eqos[i] < begin.cpu_time_eqos[i])
158 end_greater_or_equal_begin = false;
160 if (!end_greater_or_equal_begin)
161 return absl::nullopt;
163 auto get_rate_per_second = [&interval_duration](uint64_t begin,
164 uint64_t end) -> double {
165 DCHECK_GE(end, begin);
166 uint64_t diff = end - begin;
167 return diff / interval_duration.InSecondsF();
170 auto get_time_rate_per_second = [&interval_duration, &timebase](
171 uint64_t begin, uint64_t end) -> double {
172 DCHECK_GE(end, begin);
173 // Compute the delta in s, being careful to avoid truncation due to integral
175 double delta_sample_s =
176 power_metrics::MachTimeToNs(end - begin, timebase) /
177 static_cast<double>(base::Time::kNanosecondsPerSecond);
178 return delta_sample_s / interval_duration.InSecondsF();
181 CoalitionResourceUsageRate result;
183 result.cpu_time_per_second =
184 get_time_rate_per_second(begin.cpu_time, end.cpu_time);
185 result.interrupt_wakeups_per_second =
186 get_rate_per_second(begin.interrupt_wakeups, end.interrupt_wakeups);
187 result.platform_idle_wakeups_per_second = get_rate_per_second(
188 begin.platform_idle_wakeups, end.platform_idle_wakeups);
189 result.bytesread_per_second =
190 get_rate_per_second(begin.bytesread, end.bytesread);
191 result.byteswritten_per_second =
192 get_rate_per_second(begin.byteswritten, end.byteswritten);
193 result.gpu_time_per_second =
194 get_time_rate_per_second(begin.gpu_time, end.gpu_time);
195 result.power_nw = get_rate_per_second(begin.energy, end.energy);
197 for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
198 result.qos_time_per_second[i] =
199 get_time_rate_per_second(begin.cpu_time_eqos[i], end.cpu_time_eqos[i]);
202 if (energy_impact_coefficients.has_value()) {
203 result.energy_impact_per_second =
204 (ComputeEnergyImpactForResourceUsage(
205 end, energy_impact_coefficients.value(), timebase) -
206 ComputeEnergyImpactForResourceUsage(
207 begin, energy_impact_coefficients.value(), timebase)) /
208 interval_duration.InSecondsF();