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/energy_impact_mac.h"
7 #include "base/base_paths.h"
8 #include "base/path_service.h"
9 #include "components/power_metrics/resource_coalition_mac.h"
10 #include "testing/gtest/include/gtest/gtest.h"
12 namespace power_metrics {
16 constexpr mach_timebase_info_data_t kIntelTimebase = {1, 1};
17 constexpr mach_timebase_info_data_t kM1Timebase = {125, 3};
19 base::FilePath GetTestDataPath() {
20 base::FilePath test_path;
21 EXPECT_TRUE(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &test_path));
22 test_path = test_path.Append(FILE_PATH_LITERAL("components"));
23 test_path = test_path.Append(FILE_PATH_LITERAL("power_metrics"));
24 test_path = test_path.Append(FILE_PATH_LITERAL("test"));
25 test_path = test_path.Append(FILE_PATH_LITERAL("data"));
29 coalition_resource_usage MakeResourceUsageWithQOS(int qos_level,
30 base::TimeDelta cpu_time) {
31 coalition_resource_usage result{};
32 result.cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES;
33 result.cpu_time_eqos[qos_level] = cpu_time.InNanoseconds();
37 // Scales a time given in ns to mach_time in |timebase|.
38 uint64_t NsScaleToTimebase(const mach_timebase_info_data_t& timebase,
40 return time_ns * timebase.denom / timebase.numer;
45 TEST(EnergyImpactTest, ReadCoefficientsFromPath) {
46 base::FilePath test_path = GetTestDataPath();
48 // Validate that attempting to read from a non-existent file fails.
49 auto coefficients = internal::ReadCoefficientsFromPath(
50 test_path.Append(FILE_PATH_LITERAL("does-not-exist.plist")));
51 EXPECT_FALSE(coefficients.has_value());
53 // Validate that a well-formed file returns the expected coefficients.
54 coefficients = internal::ReadCoefficientsFromPath(
55 test_path.Append(FILE_PATH_LITERAL("test.plist")));
56 ASSERT_TRUE(coefficients.has_value());
58 const EnergyImpactCoefficients& value = coefficients.value();
59 EXPECT_FLOAT_EQ(value.kcpu_time, 1.23);
60 EXPECT_FLOAT_EQ(value.kdiskio_bytesread, 7.89);
61 EXPECT_FLOAT_EQ(value.kdiskio_byteswritten, 1.2345);
62 EXPECT_FLOAT_EQ(value.kgpu_time, 6.789);
63 EXPECT_FLOAT_EQ(value.knetwork_recv_bytes, 12.3);
64 EXPECT_FLOAT_EQ(value.knetwork_recv_packets, 45.6);
65 EXPECT_FLOAT_EQ(value.knetwork_sent_bytes, 67.8);
66 EXPECT_FLOAT_EQ(value.knetwork_sent_packets, 89);
67 EXPECT_FLOAT_EQ(value.kqos_background, 8.9);
68 EXPECT_FLOAT_EQ(value.kqos_default, 6.78);
69 EXPECT_FLOAT_EQ(value.kqos_legacy, 5.678);
70 EXPECT_FLOAT_EQ(value.kqos_user_initiated, 9.012);
71 EXPECT_FLOAT_EQ(value.kqos_user_interactive, 3.456);
72 EXPECT_FLOAT_EQ(value.kqos_utility, 1.234);
73 EXPECT_FLOAT_EQ(value.kcpu_wakeups, 3.45);
76 TEST(EnergyImpactTest, ReadCoefficientsForBoardIdOrDefault_Exists) {
77 // This board-id should exist.
78 auto coefficients = internal::ReadCoefficientsForBoardIdOrDefault(
79 GetTestDataPath(), "Mac-7BA5B2DFE22DDD8C");
80 ASSERT_TRUE(coefficients.has_value());
82 // Validate that the default coefficients haven't been loaded.
83 EXPECT_FLOAT_EQ(3.4, coefficients.value().kgpu_time);
84 EXPECT_FLOAT_EQ(0.39, coefficients.value().kqos_background);
87 TEST(EnergyImpactTest, ReadCoefficientsForBoardIdOrDefault_Default) {
88 // This board-id should not exist.
89 auto coefficients = internal::ReadCoefficientsForBoardIdOrDefault(
90 GetTestDataPath(), "Mac-031B6874CF7F642A");
91 ASSERT_TRUE(coefficients.has_value());
93 // Validate that the default coefficients were loaded.
94 EXPECT_FLOAT_EQ(0, coefficients.value().kgpu_time);
95 EXPECT_FLOAT_EQ(0.8, coefficients.value().kqos_background);
98 TEST(EnergyImpactTest,
99 ReadCoefficientsForBoardIdOrDefault_NonExistentDirectory) {
100 // This directory shouldn't exist, so nothing should be loaded.
102 internal::ReadCoefficientsForBoardIdOrDefault(
103 GetTestDataPath().Append("nonexistent"), "Mac-7BA5B2DFE22DDD8C")
107 TEST(EnergyImpactTest, GetBoardIdForThisMachine) {
108 // This can't really be tested except that the contract holds one way
110 auto board_id = internal::GetBoardIdForThisMachine();
111 if (board_id.has_value()) {
112 EXPECT_FALSE(board_id.value().empty());
116 // Verify the Energy Impact score when there is a single source of energy
117 // consumption (only one member set in `coalition_resource_usage`).
118 TEST(EnergyImpactTest, ComputeEnergyImpactForResourceUsage_Individual) {
119 EXPECT_EQ(0.0, ComputeEnergyImpactForResourceUsage(coalition_resource_usage(),
120 EnergyImpactCoefficients{},
123 // Test the coefficients and sample factors individually.
125 2.66, ComputeEnergyImpactForResourceUsage(
126 coalition_resource_usage{.platform_idle_wakeups = 133},
127 EnergyImpactCoefficients{
128 .kcpu_wakeups = base::Microseconds(200).InSecondsF()},
131 // Test 100 ms of CPU, which should come out to 8% of a CPU second with a
132 // background QOS discount of rate of 0.8.
133 EXPECT_DOUBLE_EQ(8.0, ComputeEnergyImpactForResourceUsage(
134 MakeResourceUsageWithQOS(THREAD_QOS_BACKGROUND,
135 base::Milliseconds(100)),
136 EnergyImpactCoefficients{.kqos_background = 0.8},
140 ComputeEnergyImpactForResourceUsage(
141 MakeResourceUsageWithQOS(THREAD_QOS_DEFAULT, base::Milliseconds(50)),
142 EnergyImpactCoefficients{.kqos_default = 1.0}, kIntelTimebase));
145 ComputeEnergyImpactForResourceUsage(
146 MakeResourceUsageWithQOS(THREAD_QOS_UTILITY, base::Milliseconds(100)),
147 EnergyImpactCoefficients{.kqos_utility = 1.0}, kIntelTimebase));
150 ComputeEnergyImpactForResourceUsage(
151 MakeResourceUsageWithQOS(THREAD_QOS_LEGACY, base::Milliseconds(10)),
152 EnergyImpactCoefficients{.kqos_legacy = 1.0}, kIntelTimebase));
153 EXPECT_DOUBLE_EQ(1.0,
154 ComputeEnergyImpactForResourceUsage(
155 MakeResourceUsageWithQOS(THREAD_QOS_USER_INITIATED,
156 base::Milliseconds(10)),
157 EnergyImpactCoefficients{.kqos_user_initiated = 1.0},
159 EXPECT_DOUBLE_EQ(1.0,
160 ComputeEnergyImpactForResourceUsage(
161 MakeResourceUsageWithQOS(THREAD_QOS_USER_INTERACTIVE,
162 base::Milliseconds(10)),
163 EnergyImpactCoefficients{.kqos_user_interactive = 1.0},
166 1.0, ComputeEnergyImpactForResourceUsage(
167 coalition_resource_usage{
168 .gpu_time = base::Milliseconds(4).InNanoseconds()},
169 EnergyImpactCoefficients{.kgpu_time = 2.5}, kIntelTimebase));
172 // Verify the Energy Impact score when there are multiple sources of energy
173 // consumption (multiple members set in `coalition_resource_usage`).
174 TEST(EnergyImpactTest, ComputeEnergyImpactForResourceUsage_Combined) {
175 EnergyImpactCoefficients coefficients{
176 .kcpu_wakeups = base::Microseconds(200).InSecondsF(),
178 .kqos_background = 0.8,
181 .kqos_user_initiated = 1.0,
182 .kqos_user_interactive = 1.0,
185 coalition_resource_usage sample{
186 .platform_idle_wakeups = 133,
188 NsScaleToTimebase(kM1Timebase, base::Milliseconds(4).InNanoseconds()),
189 .cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES,
191 sample.cpu_time_eqos[THREAD_QOS_BACKGROUND] =
192 NsScaleToTimebase(kM1Timebase, base::Milliseconds(100).InNanoseconds());
193 sample.cpu_time_eqos[THREAD_QOS_DEFAULT] =
194 NsScaleToTimebase(kM1Timebase, base::Milliseconds(50).InNanoseconds());
195 sample.cpu_time_eqos[THREAD_QOS_UTILITY] =
196 NsScaleToTimebase(kM1Timebase, base::Milliseconds(100).InNanoseconds());
197 sample.cpu_time_eqos[THREAD_QOS_LEGACY] =
198 NsScaleToTimebase(kM1Timebase, base::Milliseconds(10).InNanoseconds());
199 sample.cpu_time_eqos[THREAD_QOS_USER_INITIATED] =
200 NsScaleToTimebase(kM1Timebase, base::Milliseconds(10).InNanoseconds());
201 sample.cpu_time_eqos[THREAD_QOS_USER_INTERACTIVE] =
202 NsScaleToTimebase(kM1Timebase, base::Milliseconds(10).InNanoseconds());
204 EXPECT_DOUBLE_EQ(29.66, ComputeEnergyImpactForResourceUsage(
205 sample, coefficients, kM1Timebase));
208 // Verify the Energy Impact score when fields of `coalition_resource_usage` that
209 // don't contribute to the score are set.
210 TEST(EnergyImpactTest, ComputeEnergyImpactForResourceUsage_Unused) {
211 EnergyImpactCoefficients coefficients{
212 .kdiskio_bytesread = 1000,
213 .kdiskio_byteswritten = 1000,
214 .knetwork_recv_bytes = 1000,
215 .knetwork_recv_packets = 1000,
216 .knetwork_sent_bytes = 1000,
217 .knetwork_sent_packets = 1000,
219 coalition_resource_usage sample{
220 .tasks_started = 1000,
221 .tasks_exited = 1000,
222 .time_nonempty = 1000,
224 .interrupt_wakeups = 1000,
226 .byteswritten = 1000,
227 .cpu_time_billed_to_me = 1000,
228 .cpu_time_billed_to_others = 1000,
230 .logical_immediate_writes = 1000,
231 .logical_deferred_writes = 1000,
232 .logical_invalidated_writes = 1000,
233 .logical_metadata_writes = 1000,
234 .logical_immediate_writes_to_external = 1000,
235 .logical_deferred_writes_to_external = 1000,
236 .logical_invalidated_writes_to_external = 1000,
237 .logical_metadata_writes_to_external = 1000,
238 .energy_billed_to_me = 1000,
239 .energy_billed_to_others = 1000,
241 .cpu_instructions = 1000,
243 .fs_metadata_writes = 1000,
247 EXPECT_EQ(0, ComputeEnergyImpactForResourceUsage(sample, coefficients,
251 } // namespace power_metrics