0

Move code to compute macOS Energy Impact to //components/power_metrics.

This allows reuse by the code that records histograms in Chrome and
the "power_sampler" tool in //tools/mac/power.

Bug: 1254332
Change-Id: I9815af033848d4ea13ff39f724c68b2c66f41722
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3296547
Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
Auto-Submit: François Doray <fdoray@chromium.org>
Reviewed-by: Etienne Pierre-Doray <etiennep@chromium.org>
Cr-Commit-Position: refs/heads/main@{#945466}
This commit is contained in:
Francois Doray
2021-11-25 20:14:36 +00:00
committed by Chromium LUCI CQ
parent 60ac2fdec0
commit eb603288d1
13 changed files with 641 additions and 488 deletions

@ -5,6 +5,10 @@
if (is_mac) {
static_library("power_metrics") {
sources = [
"energy_impact_mac.h",
"energy_impact_mac.mm",
"mach_time_mac.h",
"mach_time_mac.mm",
"resource_coalition_internal_types_mac.h",
"resource_coalition_mac.h",
"resource_coalition_mac.mm",
@ -12,4 +16,18 @@ if (is_mac) {
deps = [ "//base" ]
}
source_set("unit_tests") {
testonly = true
sources = [ "energy_impact_mac_unittest.mm" ]
data = [ "test/data/" ]
deps = [
":power_metrics",
"//base",
"//testing/gtest",
]
}
}

@ -0,0 +1,95 @@
// 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.
#ifndef COMPONENTS_POWER_METRICS_ENERGY_IMPACT_MAC_H_
#define COMPONENTS_POWER_METRICS_ENERGY_IMPACT_MAC_H_
#include <string>
#include "base/files/file_path.h"
#include "base/time/time.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
struct coalition_resource_usage;
namespace power_metrics {
// Coefficients used to compute the Energy Impact score from resource
// coalition metrics. The order of members mimics the order of keys in the
// plist files in /usr/share/pmenergy.
struct EnergyImpactCoefficients {
double kcpu_time;
// In units of seconds/event.
double kcpu_wakeups;
// Coefficients for CPU usage at different QOS.
// Strangely there's no coefficient for maintenance QOS.
double kqos_default;
double kqos_background;
double kqos_utility;
double kqos_legacy;
double kqos_user_initiated;
double kqos_user_interactive;
double kdiskio_bytesread;
double kdiskio_byteswritten;
double kgpu_time;
double knetwork_recv_bytes;
double knetwork_recv_packets;
double knetwork_sent_bytes;
double knetwork_sent_packets;
};
// Reads the Energy Impact coefficients for the current machine from disk, or
// default coefficients if coefficients are not available for the current
// machine.
absl::optional<EnergyImpactCoefficients>
ReadCoefficientsForCurrentMachineOrDefault();
// Computes the Energy Impact score for the resource consumption data in
// |coalition_resource_usage| using |coefficients|.
//
// The Energy Impact (EI) score is referenced to CPU time, such that 10ms CPU
// time appears to be equivalent to 1 EI. The Activity Monitor presents EI
// rates to the user in units of 10ms/s of CPU time. This means a process that
// consumes 1000ms/s or 100% CPU, at default QOS, is rated 100 EI, making the
// two units somewhat relatable. Note that this only has relevance on Intel
// architecture, as it looks like on M1 architecture macOS implements more
// granular, and hopefully more accurate, energy metering on the fly.
double ComputeEnergyImpactForResourceUsage(
const coalition_resource_usage& coalition_resource_usage,
const EnergyImpactCoefficients& coefficients,
const mach_timebase_info_data_t& mach_timebase);
namespace internal {
// Reads the coefficients from the "energy_constants" sub-dictionary of the
// plist file at |plist_file|. This is exposed for testing, production code
// should use ReadCoefficientsForCurrentMachineOrDefault().
absl::optional<EnergyImpactCoefficients> ReadCoefficientsFromPath(
const base::FilePath& plist_file);
// Given a |directory| and a |board_id|, read the plist file for the board id
// from the directory, or if not available, read the default file.
// Returns true if either file can be loaded, false otherwise. This is exposed
// for testing, production code should use
// ReadCoefficientsForCurrentMachineOrDefault().
absl::optional<EnergyImpactCoefficients> ReadCoefficientsForBoardIdOrDefault(
const base::FilePath& directory,
const std::string& board_id);
// Returns the board id to use for reading the Energy Impact coefficients for
// the current machine. This appears to work for Intel Macs only. This is
// exposed for testing, production code should use
// ReadCoefficientsForCurrentMachineOrDefault().
absl::optional<std::string> GetBoardIdForThisMachine();
} // namespace internal
} // namespace power_metrics
#endif // COMPONENTS_POWER_METRICS_ENERGY_IMPACT_MAC_H_

@ -0,0 +1,195 @@
// 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/energy_impact_mac.h"
#include <Foundation/Foundation.h>
#import <IOKit/IOKitLib.h>
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/scoped_ioobject.h"
#include "base/strings/sys_string_conversions.h"
#include "base/time/time.h"
#include "components/power_metrics/mach_time_mac.h"
#include "components/power_metrics/resource_coalition_mac.h"
namespace power_metrics {
namespace {
NSDictionary* MaybeGetDictionaryFromPath(const base::FilePath& path) {
// The folder where the energy coefficient plist files are stored.
NSString* plist_path_string = base::SysUTF8ToNSString(path.value().c_str());
return [NSDictionary dictionaryWithContentsOfFile:plist_path_string];
}
double GetNamedCoefficientOrZero(NSDictionary* dict, NSString* key) {
NSObject* value = [dict objectForKey:key];
NSNumber* num = base::mac::ObjCCast<NSNumber>(value);
return [num floatValue];
}
} // namespace
absl::optional<EnergyImpactCoefficients>
ReadCoefficientsForCurrentMachineOrDefault() {
absl::optional<std::string> board_id = internal::GetBoardIdForThisMachine();
if (!board_id.has_value())
return absl::nullopt;
return internal::ReadCoefficientsForBoardIdOrDefault(
base::FilePath(FILE_PATH_LITERAL("/usr/share/pmenergy")),
board_id.value());
}
double ComputeEnergyImpactForResourceUsage(
const coalition_resource_usage& data_sample,
const EnergyImpactCoefficients& coefficients,
const mach_timebase_info_data_t& mach_timebase) {
// TODO(https://crbug.com/1249536): The below coefficients are not used
// for now. Their units are unknown, and in the case of the network-related
// coefficients, it's not clear how to sample the data.
//
// coefficients.kdiskio_bytesread;
// coefficients.kdiskio_byteswritten;
// coefficients.knetwork_recv_bytes;
// coefficients.knetwork_recv_packets;
// coefficients.knetwork_sent_bytes;
// coefficients.knetwork_sent_packets;
// The cumulative CPU usage in |data_sample| is in units of ns, and
// |cpu_time_equivalent_ns| is computed in CPU ns up to the end of this
// function, where it's converted to units of EnergyImpact.
double cpu_time_equivalent_ns = 0.0;
// The kcpu_wakeups coefficient on disk is in seconds, but our
// intermediate result is in ns, so convert to ns on the fly.
cpu_time_equivalent_ns += coefficients.kcpu_wakeups *
base::Time::kNanosecondsPerSecond *
data_sample.platform_idle_wakeups;
// Presumably the kgpu_time coefficient has suitable units for the
// conversion of GPU time energy to CPU time energy. There is a fairly
// wide spread on this constant seen in /usr/share/pmenergy. On
// macOS 11.5.2 the spread is from 0 through 5.9.
cpu_time_equivalent_ns += coefficients.kgpu_time *
MachTimeToNs(data_sample.gpu_time, mach_timebase);
cpu_time_equivalent_ns +=
coefficients.kqos_background *
MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_BACKGROUND],
mach_timebase);
cpu_time_equivalent_ns +=
coefficients.kqos_default *
MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_DEFAULT],
mach_timebase);
cpu_time_equivalent_ns +=
coefficients.kqos_legacy *
MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_LEGACY], mach_timebase);
cpu_time_equivalent_ns +=
coefficients.kqos_user_initiated *
MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_USER_INITIATED],
mach_timebase);
cpu_time_equivalent_ns +=
coefficients.kqos_user_interactive *
MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_USER_INTERACTIVE],
mach_timebase);
cpu_time_equivalent_ns +=
coefficients.kqos_utility *
MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_UTILITY],
mach_timebase);
// The conversion ratio for CPU time/EnergyImpact is ns/10ms
constexpr double kNsToEI = 1E-7;
return cpu_time_equivalent_ns * kNsToEI;
}
namespace internal {
absl::optional<EnergyImpactCoefficients> ReadCoefficientsFromPath(
const base::FilePath& plist_file) {
@autoreleasepool {
NSDictionary* dict = MaybeGetDictionaryFromPath(plist_file);
if (!dict)
return absl::nullopt;
NSDictionary* energy_constants = [dict objectForKey:@"energy_constants"];
if (!energy_constants)
return absl::nullopt;
EnergyImpactCoefficients coefficients{};
coefficients.kcpu_time =
GetNamedCoefficientOrZero(energy_constants, @"kcpu_time");
coefficients.kcpu_wakeups =
GetNamedCoefficientOrZero(energy_constants, @"kcpu_wakeups");
coefficients.kqos_default =
GetNamedCoefficientOrZero(energy_constants, @"kqos_default");
coefficients.kqos_background =
GetNamedCoefficientOrZero(energy_constants, @"kqos_background");
coefficients.kqos_utility =
GetNamedCoefficientOrZero(energy_constants, @"kqos_utility");
coefficients.kqos_legacy =
GetNamedCoefficientOrZero(energy_constants, @"kqos_legacy");
coefficients.kqos_user_initiated =
GetNamedCoefficientOrZero(energy_constants, @"kqos_user_initiated");
coefficients.kqos_user_interactive =
GetNamedCoefficientOrZero(energy_constants, @"kqos_user_interactive");
coefficients.kdiskio_bytesread =
GetNamedCoefficientOrZero(energy_constants, @"kdiskio_bytesread");
coefficients.kdiskio_byteswritten =
GetNamedCoefficientOrZero(energy_constants, @"kdiskio_byteswritten");
coefficients.kgpu_time =
GetNamedCoefficientOrZero(energy_constants, @"kgpu_time");
coefficients.knetwork_recv_bytes =
GetNamedCoefficientOrZero(energy_constants, @"knetwork_recv_bytes");
coefficients.knetwork_recv_packets =
GetNamedCoefficientOrZero(energy_constants, @"knetwork_recv_packets");
coefficients.knetwork_sent_bytes =
GetNamedCoefficientOrZero(energy_constants, @"knetwork_sent_bytes");
coefficients.knetwork_sent_packets =
GetNamedCoefficientOrZero(energy_constants, @"knetwork_sent_packets");
return coefficients;
}
}
absl::optional<EnergyImpactCoefficients> ReadCoefficientsForBoardIdOrDefault(
const base::FilePath& directory,
const std::string& board_id) {
auto coefficients = ReadCoefficientsFromPath(
directory.Append(board_id).AddExtension(FILE_PATH_LITERAL("plist")));
if (coefficients.has_value())
return coefficients;
return ReadCoefficientsFromPath(
directory.Append(FILE_PATH_LITERAL("default.plist")));
}
absl::optional<std::string> GetBoardIdForThisMachine() {
base::mac::ScopedIOObject<io_service_t> platform_expert(
IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("IOPlatformExpertDevice")));
if (!platform_expert)
return absl::nullopt;
// This is what libpmenergy is observed to do in order to retrieve the correct
// coefficients file for the local computer.
base::ScopedCFTypeRef<CFDataRef> board_id_data(
base::mac::CFCast<CFDataRef>(IORegistryEntryCreateCFProperty(
platform_expert, CFSTR("board-id"), kCFAllocatorDefault, 0)));
if (!board_id_data)
return absl::nullopt;
return reinterpret_cast<const char*>(CFDataGetBytePtr(board_id_data));
}
} // namespace internal
} // namespace power_metrics

@ -0,0 +1,251 @@
// 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/energy_impact_mac.h"
#include "base/base_paths.h"
#include "base/path_service.h"
#include "components/power_metrics/resource_coalition_mac.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace power_metrics {
namespace {
constexpr mach_timebase_info_data_t kIntelTimebase = {1, 1};
constexpr mach_timebase_info_data_t kM1Timebase = {125, 3};
base::FilePath GetTestDataPath() {
base::FilePath test_path;
EXPECT_TRUE(base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &test_path));
test_path = test_path.Append(FILE_PATH_LITERAL("components"));
test_path = test_path.Append(FILE_PATH_LITERAL("power_metrics"));
test_path = test_path.Append(FILE_PATH_LITERAL("test"));
test_path = test_path.Append(FILE_PATH_LITERAL("data"));
return test_path;
}
coalition_resource_usage MakeResourceUsageWithQOS(int qos_level,
base::TimeDelta cpu_time) {
coalition_resource_usage result{};
result.cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES;
result.cpu_time_eqos[qos_level] = cpu_time.InNanoseconds();
return result;
}
// Scales a time given in ns to mach_time in |timebase|.
uint64_t NsScaleToTimebase(const mach_timebase_info_data_t& timebase,
int64_t time_ns) {
return time_ns * timebase.denom / timebase.numer;
}
} // namespace
TEST(EnergyImpactTest, ReadCoefficientsFromPath) {
base::FilePath test_path = GetTestDataPath();
// Validate that attempting to read from a non-exisent file fails.
auto coefficients = internal::ReadCoefficientsFromPath(
test_path.Append(FILE_PATH_LITERAL("does-not-exist.plist")));
EXPECT_FALSE(coefficients.has_value());
// Validate that a well-formed file returns the expected coefficients.
coefficients = internal::ReadCoefficientsFromPath(
test_path.Append(FILE_PATH_LITERAL("test.plist")));
ASSERT_TRUE(coefficients.has_value());
const EnergyImpactCoefficients& value = coefficients.value();
EXPECT_FLOAT_EQ(value.kcpu_time, 1.23);
EXPECT_FLOAT_EQ(value.kdiskio_bytesread, 7.89);
EXPECT_FLOAT_EQ(value.kdiskio_byteswritten, 1.2345);
EXPECT_FLOAT_EQ(value.kgpu_time, 6.789);
EXPECT_FLOAT_EQ(value.knetwork_recv_bytes, 12.3);
EXPECT_FLOAT_EQ(value.knetwork_recv_packets, 45.6);
EXPECT_FLOAT_EQ(value.knetwork_sent_bytes, 67.8);
EXPECT_FLOAT_EQ(value.knetwork_sent_packets, 89);
EXPECT_FLOAT_EQ(value.kqos_background, 8.9);
EXPECT_FLOAT_EQ(value.kqos_default, 6.78);
EXPECT_FLOAT_EQ(value.kqos_legacy, 5.678);
EXPECT_FLOAT_EQ(value.kqos_user_initiated, 9.012);
EXPECT_FLOAT_EQ(value.kqos_user_interactive, 3.456);
EXPECT_FLOAT_EQ(value.kqos_utility, 1.234);
EXPECT_FLOAT_EQ(value.kcpu_wakeups, 3.45);
}
TEST(EnergyImpactTest, ReadCoefficientsForBoardIdOrDefault_Exists) {
// This board-id should exist.
auto coefficients = internal::ReadCoefficientsForBoardIdOrDefault(
GetTestDataPath(), "Mac-7BA5B2DFE22DDD8C");
ASSERT_TRUE(coefficients.has_value());
// Validate that the default coefficients haven't been loaded.
EXPECT_FLOAT_EQ(3.4, coefficients.value().kgpu_time);
EXPECT_FLOAT_EQ(0.39, coefficients.value().kqos_background);
}
TEST(EnergyImpactTest, ReadCoefficientsForBoardIdOrDefault_Default) {
// This board-id should not exist.
auto coefficients = internal::ReadCoefficientsForBoardIdOrDefault(
GetTestDataPath(), "Mac-031B6874CF7F642A");
ASSERT_TRUE(coefficients.has_value());
// Validate that the default coefficients were loaded.
EXPECT_FLOAT_EQ(0, coefficients.value().kgpu_time);
EXPECT_FLOAT_EQ(0.8, coefficients.value().kqos_background);
}
TEST(EnergyImpactTest,
ReadCoefficientsForBoardIdOrDefault_NonExistentDirectory) {
// This directory shouldn't exist, so nothing should be loaded.
EXPECT_FALSE(
internal::ReadCoefficientsForBoardIdOrDefault(
GetTestDataPath().Append("nonexistent"), "Mac-7BA5B2DFE22DDD8C")
.has_value());
}
TEST(EnergyImpactTest, GetBoardIdForThisMachine) {
// This can't really be tested except that the contract holds one way
// or the other.
auto board_id = internal::GetBoardIdForThisMachine();
if (board_id.has_value()) {
EXPECT_FALSE(board_id.value().empty());
}
}
// Verify the Energy Impact score when there is a single source of energy
// consumption (only one member set in `coalition_resource_usage`).
TEST(EnergyImpactTest, ComputeEnergyImpactForResourceUsage_Individual) {
EXPECT_EQ(0.0, ComputeEnergyImpactForResourceUsage(coalition_resource_usage(),
EnergyImpactCoefficients{},
kIntelTimebase));
// Test the coefficients and sample factors individually.
EXPECT_DOUBLE_EQ(
2.66, ComputeEnergyImpactForResourceUsage(
coalition_resource_usage{.platform_idle_wakeups = 133},
EnergyImpactCoefficients{
.kcpu_wakeups = base::Microseconds(200).InSecondsF()},
kIntelTimebase));
// Test 100 ms of CPU, which should come out to 8% of a CPU second with a
// background QOS discount of rate of 0.8.
EXPECT_DOUBLE_EQ(8.0, ComputeEnergyImpactForResourceUsage(
MakeResourceUsageWithQOS(THREAD_QOS_BACKGROUND,
base::Milliseconds(100)),
EnergyImpactCoefficients{.kqos_background = 0.8},
kIntelTimebase));
EXPECT_DOUBLE_EQ(
5.0,
ComputeEnergyImpactForResourceUsage(
MakeResourceUsageWithQOS(THREAD_QOS_DEFAULT, base::Milliseconds(50)),
EnergyImpactCoefficients{.kqos_default = 1.0}, kIntelTimebase));
EXPECT_DOUBLE_EQ(
10.0,
ComputeEnergyImpactForResourceUsage(
MakeResourceUsageWithQOS(THREAD_QOS_UTILITY, base::Milliseconds(100)),
EnergyImpactCoefficients{.kqos_utility = 1.0}, kIntelTimebase));
EXPECT_DOUBLE_EQ(
1.0,
ComputeEnergyImpactForResourceUsage(
MakeResourceUsageWithQOS(THREAD_QOS_LEGACY, base::Milliseconds(10)),
EnergyImpactCoefficients{.kqos_legacy = 1.0}, kIntelTimebase));
EXPECT_DOUBLE_EQ(1.0,
ComputeEnergyImpactForResourceUsage(
MakeResourceUsageWithQOS(THREAD_QOS_USER_INITIATED,
base::Milliseconds(10)),
EnergyImpactCoefficients{.kqos_user_initiated = 1.0},
kIntelTimebase));
EXPECT_DOUBLE_EQ(1.0,
ComputeEnergyImpactForResourceUsage(
MakeResourceUsageWithQOS(THREAD_QOS_USER_INTERACTIVE,
base::Milliseconds(10)),
EnergyImpactCoefficients{.kqos_user_interactive = 1.0},
kIntelTimebase));
EXPECT_DOUBLE_EQ(
1.0, ComputeEnergyImpactForResourceUsage(
coalition_resource_usage{
.gpu_time = base::Milliseconds(4).InNanoseconds()},
EnergyImpactCoefficients{.kgpu_time = 2.5}, kIntelTimebase));
}
// Verify the Energy Impact score when there are multiple sources of energy
// consumption (multiple members set in `coalition_resource_usage`).
TEST(EnergyImpactTest, ComputeEnergyImpactForResourceUsage_Combined) {
EnergyImpactCoefficients coefficients{
.kcpu_wakeups = base::Microseconds(200).InSecondsF(),
.kqos_default = 1.0,
.kqos_background = 0.8,
.kqos_utility = 1.0,
.kqos_legacy = 1.0,
.kqos_user_initiated = 1.0,
.kqos_user_interactive = 1.0,
.kgpu_time = 2.5,
};
coalition_resource_usage sample{
.platform_idle_wakeups = 133,
.gpu_time =
NsScaleToTimebase(kM1Timebase, base::Milliseconds(4).InNanoseconds()),
.cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES,
};
sample.cpu_time_eqos[THREAD_QOS_BACKGROUND] =
NsScaleToTimebase(kM1Timebase, base::Milliseconds(100).InNanoseconds());
sample.cpu_time_eqos[THREAD_QOS_DEFAULT] =
NsScaleToTimebase(kM1Timebase, base::Milliseconds(50).InNanoseconds());
sample.cpu_time_eqos[THREAD_QOS_UTILITY] =
NsScaleToTimebase(kM1Timebase, base::Milliseconds(100).InNanoseconds());
sample.cpu_time_eqos[THREAD_QOS_LEGACY] =
NsScaleToTimebase(kM1Timebase, base::Milliseconds(10).InNanoseconds());
sample.cpu_time_eqos[THREAD_QOS_USER_INITIATED] =
NsScaleToTimebase(kM1Timebase, base::Milliseconds(10).InNanoseconds());
sample.cpu_time_eqos[THREAD_QOS_USER_INTERACTIVE] =
NsScaleToTimebase(kM1Timebase, base::Milliseconds(10).InNanoseconds());
EXPECT_DOUBLE_EQ(29.66, ComputeEnergyImpactForResourceUsage(
sample, coefficients, kM1Timebase));
}
// Verify the Energy Impact score when fields of `coalition_resource_usage` that
// don't contribute to the score are set.
TEST(EnergyImpactTest, ComputeEnergyImpactForResourceUsage_Unused) {
EnergyImpactCoefficients coefficients{
.kdiskio_bytesread = 1000,
.kdiskio_byteswritten = 1000,
.knetwork_recv_bytes = 1000,
.knetwork_recv_packets = 1000,
.knetwork_sent_bytes = 1000,
.knetwork_sent_packets = 1000,
};
coalition_resource_usage sample{
.tasks_started = 1000,
.tasks_exited = 1000,
.time_nonempty = 1000,
.cpu_time = 1000,
.interrupt_wakeups = 1000,
.bytesread = 1000,
.byteswritten = 1000,
.cpu_time_billed_to_me = 1000,
.cpu_time_billed_to_others = 1000,
.energy = 1000,
.logical_immediate_writes = 1000,
.logical_deferred_writes = 1000,
.logical_invalidated_writes = 1000,
.logical_metadata_writes = 1000,
.logical_immediate_writes_to_external = 1000,
.logical_deferred_writes_to_external = 1000,
.logical_invalidated_writes_to_external = 1000,
.logical_metadata_writes_to_external = 1000,
.energy_billed_to_me = 1000,
.energy_billed_to_others = 1000,
.cpu_ptime = 1000,
.cpu_instructions = 1000,
.cpu_cycles = 1000,
.fs_metadata_writes = 1000,
.pm_writes = 1000,
};
EXPECT_EQ(0, ComputeEnergyImpactForResourceUsage(sample, coefficients,
kM1Timebase));
}
} // namespace power_metrics

@ -0,0 +1,23 @@
// 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.
#ifndef COMPONENTS_POWER_METRICS_MACH_TIME_MAC_H_
#define COMPONENTS_POWER_METRICS_MACH_TIME_MAC_H_
#include <mach/mach_time.h>
#include <stdint.h>
namespace power_metrics {
// Converts |mach_time| to nanoseconds, using the multiplier in |mach_timebase|.
uint64_t MachTimeToNs(uint64_t mach_time,
const mach_timebase_info_data_t& mach_timebase);
// Retrieves the |mach_timebase| to convert |mach_time| obtained on this system
// to nanoseconds.
mach_timebase_info_data_t GetSystemMachTimeBase();
} // namespace power_metrics
#endif // COMPONENTS_POWER_METRICS_MACH_TIME_MAC_H_

@ -0,0 +1,30 @@
// 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/mach_time_mac.h"
#include "base/check.h"
#include "base/mac/mach_logging.h"
namespace power_metrics {
uint64_t MachTimeToNs(uint64_t mach_time,
const mach_timebase_info_data_t& mach_timebase) {
if (mach_timebase.numer == mach_timebase.denom)
return mach_time;
CHECK(!__builtin_umulll_overflow(mach_time, mach_timebase.numer, &mach_time));
return mach_time / mach_timebase.denom;
}
mach_timebase_info_data_t GetSystemMachTimeBase() {
mach_timebase_info_data_t info;
kern_return_t kr = mach_timebase_info(&info);
MACH_DCHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info";
DCHECK(info.numer);
DCHECK(info.denom);
return info;
}
} // namespace power_metrics

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>energy_constants</key>
<dict>
<key>kcpu_time</key>
<real>1.0</real>
<key>kcpu_wakeups</key>
<real>2.0e-4</real>
<key>kqos_default</key>
<real>1.0e+00</real>
<key>kqos_background</key>
<real>3.9e-01</real>
<key>kqos_utility</key>
<real>1.0e+00</real>
<key>kqos_legacy</key>
<real>1.0e+00</real>
<key>kqos_user_initiated</key>
<real>1.0e+00</real>
<key>kqos_user_interactive</key>
<real>1.0e+00</real>
<key>kdiskio_bytesread</key>
<real>2.8e-09</real>
<key>kdiskio_byteswritten</key>
<real>3.8e-10</real>
<key>kgpu_time</key>
<real>3.4e+00</real>
<key>knetwork_recv_bytes</key>
<real>0.0</real>
<key>knetwork_recv_packets</key>
<real>4.0e-6</real>
<key>knetwork_sent_bytes</key>
<real>0.0</real>
<key>knetwork_sent_packets</key>
<real>4.0e-6</real>
</dict>
</dict>
</plist>

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>energy_constants</key>
<dict>
<key>kcpu_time</key>
<real>1.0</real>
<key>kcpu_wakeups</key>
<real>2.0e-4</real>
<key>kqos_default</key>
<real>1.0</real>
<key>kqos_background</key>
<real>8.0e-01</real>
<key>kqos_utility</key>
<real>1.0</real>
<key>kqos_legacy</key>
<real>1.0</real>
<key>kqos_user_initiated</key>
<real>1.0</real>
<key>kqos_user_interactive</key>
<real>1.0</real>
<key>kdiskio_bytesread</key>
<real>4.5e-10</real>
<key>kdiskio_byteswritten</key>
<real>2.4e-10</real>
<key>kgpu_time</key>
<real>0.0</real>
<key>knetwork_recv_bytes</key>
<real>0.0</real>
<key>knetwork_recv_packets</key>
<real>4.0e-6</real>
<key>knetwork_sent_bytes</key>
<real>0.0</real>
<key>knetwork_sent_packets</key>
<real>4.0e-6</real>
</dict>
</dict>
</plist>

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>energy_constants</key>
<dict>
<key>kcpu_time</key>
<real>1.23</real>
<key>kcpu_wakeups</key>
<real>3.45</real>
<key>kqos_default</key>
<real>6.78</real>
<key>kqos_background</key>
<real>8.90</real>
<key>kqos_utility</key>
<real>1.234</real>
<key>kqos_legacy</key>
<real>5.678</real>
<key>kqos_user_initiated</key>
<real>9.012</real>
<key>kqos_user_interactive</key>
<real>3.456</real>
<key>kdiskio_bytesread</key>
<real>7.890</real>
<key>kdiskio_byteswritten</key>
<real>1.2345</real>
<key>kgpu_time</key>
<real>6.7890</real>
<key>knetwork_recv_bytes</key>
<real>12.3</real>
<key>knetwork_recv_packets</key>
<real>45.6</real>
<key>knetwork_sent_bytes</key>
<real>67.8</real>
<key>knetwork_sent_packets</key>
<real>89.0</real>
</dict>
</dict>
</plist>