123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- // 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
|