energy_impact_mac.mm 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/power_metrics/energy_impact_mac.h"
  5. #include <Foundation/Foundation.h>
  6. #import <IOKit/IOKitLib.h>
  7. #include "base/mac/foundation_util.h"
  8. #include "base/mac/scoped_cftyperef.h"
  9. #include "base/mac/scoped_ioobject.h"
  10. #include "base/strings/sys_string_conversions.h"
  11. #include "base/time/time.h"
  12. #include "components/power_metrics/mach_time_mac.h"
  13. #include "components/power_metrics/resource_coalition_mac.h"
  14. namespace power_metrics {
  15. namespace {
  16. NSDictionary* MaybeGetDictionaryFromPath(const base::FilePath& path) {
  17. // The folder where the energy coefficient plist files are stored.
  18. NSString* plist_path_string = base::SysUTF8ToNSString(path.value().c_str());
  19. return [NSDictionary dictionaryWithContentsOfFile:plist_path_string];
  20. }
  21. double GetNamedCoefficientOrZero(NSDictionary* dict, NSString* key) {
  22. NSObject* value = [dict objectForKey:key];
  23. NSNumber* num = base::mac::ObjCCast<NSNumber>(value);
  24. return [num floatValue];
  25. }
  26. } // namespace
  27. absl::optional<EnergyImpactCoefficients>
  28. ReadCoefficientsForCurrentMachineOrDefault() {
  29. absl::optional<std::string> board_id = internal::GetBoardIdForThisMachine();
  30. if (!board_id.has_value())
  31. return absl::nullopt;
  32. return internal::ReadCoefficientsForBoardIdOrDefault(
  33. base::FilePath(FILE_PATH_LITERAL("/usr/share/pmenergy")),
  34. board_id.value());
  35. }
  36. double ComputeEnergyImpactForResourceUsage(
  37. const coalition_resource_usage& data_sample,
  38. const EnergyImpactCoefficients& coefficients,
  39. const mach_timebase_info_data_t& mach_timebase) {
  40. // TODO(https://crbug.com/1249536): The below coefficients are not used
  41. // for now. Their units are unknown, and in the case of the network-related
  42. // coefficients, it's not clear how to sample the data.
  43. //
  44. // coefficients.kdiskio_bytesread;
  45. // coefficients.kdiskio_byteswritten;
  46. // coefficients.knetwork_recv_bytes;
  47. // coefficients.knetwork_recv_packets;
  48. // coefficients.knetwork_sent_bytes;
  49. // coefficients.knetwork_sent_packets;
  50. // The cumulative CPU usage in |data_sample| is in units of ns, and
  51. // |cpu_time_equivalent_ns| is computed in CPU ns up to the end of this
  52. // function, where it's converted to units of EnergyImpact.
  53. double cpu_time_equivalent_ns = 0.0;
  54. // The kcpu_wakeups coefficient on disk is in seconds, but our
  55. // intermediate result is in ns, so convert to ns on the fly.
  56. cpu_time_equivalent_ns += coefficients.kcpu_wakeups *
  57. base::Time::kNanosecondsPerSecond *
  58. data_sample.platform_idle_wakeups;
  59. // Presumably the kgpu_time coefficient has suitable units for the
  60. // conversion of GPU time energy to CPU time energy. There is a fairly
  61. // wide spread on this constant seen in /usr/share/pmenergy. On
  62. // macOS 11.5.2 the spread is from 0 through 5.9.
  63. cpu_time_equivalent_ns += coefficients.kgpu_time *
  64. MachTimeToNs(data_sample.gpu_time, mach_timebase);
  65. cpu_time_equivalent_ns +=
  66. coefficients.kqos_background *
  67. MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_BACKGROUND],
  68. mach_timebase);
  69. cpu_time_equivalent_ns +=
  70. coefficients.kqos_default *
  71. MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_DEFAULT],
  72. mach_timebase);
  73. cpu_time_equivalent_ns +=
  74. coefficients.kqos_legacy *
  75. MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_LEGACY], mach_timebase);
  76. cpu_time_equivalent_ns +=
  77. coefficients.kqos_user_initiated *
  78. MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_USER_INITIATED],
  79. mach_timebase);
  80. cpu_time_equivalent_ns +=
  81. coefficients.kqos_user_interactive *
  82. MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_USER_INTERACTIVE],
  83. mach_timebase);
  84. cpu_time_equivalent_ns +=
  85. coefficients.kqos_utility *
  86. MachTimeToNs(data_sample.cpu_time_eqos[THREAD_QOS_UTILITY],
  87. mach_timebase);
  88. // The conversion ratio for CPU time/EnergyImpact is ns/10ms
  89. constexpr double kNsToEI = 1E-7;
  90. return cpu_time_equivalent_ns * kNsToEI;
  91. }
  92. namespace internal {
  93. absl::optional<EnergyImpactCoefficients> ReadCoefficientsFromPath(
  94. const base::FilePath& plist_file) {
  95. @autoreleasepool {
  96. NSDictionary* dict = MaybeGetDictionaryFromPath(plist_file);
  97. if (!dict)
  98. return absl::nullopt;
  99. NSDictionary* energy_constants = [dict objectForKey:@"energy_constants"];
  100. if (!energy_constants)
  101. return absl::nullopt;
  102. EnergyImpactCoefficients coefficients{};
  103. coefficients.kcpu_time =
  104. GetNamedCoefficientOrZero(energy_constants, @"kcpu_time");
  105. coefficients.kcpu_wakeups =
  106. GetNamedCoefficientOrZero(energy_constants, @"kcpu_wakeups");
  107. coefficients.kqos_default =
  108. GetNamedCoefficientOrZero(energy_constants, @"kqos_default");
  109. coefficients.kqos_background =
  110. GetNamedCoefficientOrZero(energy_constants, @"kqos_background");
  111. coefficients.kqos_utility =
  112. GetNamedCoefficientOrZero(energy_constants, @"kqos_utility");
  113. coefficients.kqos_legacy =
  114. GetNamedCoefficientOrZero(energy_constants, @"kqos_legacy");
  115. coefficients.kqos_user_initiated =
  116. GetNamedCoefficientOrZero(energy_constants, @"kqos_user_initiated");
  117. coefficients.kqos_user_interactive =
  118. GetNamedCoefficientOrZero(energy_constants, @"kqos_user_interactive");
  119. coefficients.kdiskio_bytesread =
  120. GetNamedCoefficientOrZero(energy_constants, @"kdiskio_bytesread");
  121. coefficients.kdiskio_byteswritten =
  122. GetNamedCoefficientOrZero(energy_constants, @"kdiskio_byteswritten");
  123. coefficients.kgpu_time =
  124. GetNamedCoefficientOrZero(energy_constants, @"kgpu_time");
  125. coefficients.knetwork_recv_bytes =
  126. GetNamedCoefficientOrZero(energy_constants, @"knetwork_recv_bytes");
  127. coefficients.knetwork_recv_packets =
  128. GetNamedCoefficientOrZero(energy_constants, @"knetwork_recv_packets");
  129. coefficients.knetwork_sent_bytes =
  130. GetNamedCoefficientOrZero(energy_constants, @"knetwork_sent_bytes");
  131. coefficients.knetwork_sent_packets =
  132. GetNamedCoefficientOrZero(energy_constants, @"knetwork_sent_packets");
  133. return coefficients;
  134. }
  135. }
  136. absl::optional<EnergyImpactCoefficients> ReadCoefficientsForBoardIdOrDefault(
  137. const base::FilePath& directory,
  138. const std::string& board_id) {
  139. auto coefficients = ReadCoefficientsFromPath(
  140. directory.Append(board_id).AddExtension(FILE_PATH_LITERAL("plist")));
  141. if (coefficients.has_value())
  142. return coefficients;
  143. return ReadCoefficientsFromPath(
  144. directory.Append(FILE_PATH_LITERAL("default.plist")));
  145. }
  146. absl::optional<std::string> GetBoardIdForThisMachine() {
  147. base::mac::ScopedIOObject<io_service_t> platform_expert(
  148. IOServiceGetMatchingService(kIOMasterPortDefault,
  149. IOServiceMatching("IOPlatformExpertDevice")));
  150. if (!platform_expert)
  151. return absl::nullopt;
  152. // This is what libpmenergy is observed to do in order to retrieve the correct
  153. // coefficients file for the local computer.
  154. base::ScopedCFTypeRef<CFDataRef> board_id_data(
  155. base::mac::CFCast<CFDataRef>(IORegistryEntryCreateCFProperty(
  156. platform_expert, CFSTR("board-id"), kCFAllocatorDefault, 0)));
  157. if (!board_id_data)
  158. return absl::nullopt;
  159. return reinterpret_cast<const char*>(CFDataGetBytePtr(board_id_data));
  160. }
  161. } // namespace internal
  162. } // namespace power_metrics