Bug 1774844 - Record per process power use on Apple Silicon, r=gerald.
authorFlorian Quèze <florian@queze.net>
Wed, 29 Jun 2022 21:24:12 +0000
changeset 622610 0839b2f7ae02263470ed9edd63ea415ad872557c
parent 622609 185ab81e5f51a52139387f5f536100d423d59a2a
child 622611 fbb1a17e83a23e5750948cc97c8e4f68ca7bcff9
push id39918
push userbszekely@mozilla.com
push dateThu, 30 Jun 2022 09:55:19 +0000
treeherdermozilla-central@65e579f52525 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1774844
milestone104.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1774844 - Record per process power use on Apple Silicon, r=gerald. Differential Revision: https://phabricator.services.mozilla.com/D150511
devtools/client/performance-new/utils.js
mozglue/baseprofiler/public/BaseProfilerState.h
tools/profiler/core/PowerCounters-mac.cpp
tools/profiler/core/PowerCounters.h
tools/profiler/moz.build
tools/profiler/public/ProfilerState.h
--- a/devtools/client/performance-new/utils.js
+++ b/devtools/client/performance-new/utils.js
@@ -537,17 +537,17 @@ const featureDescriptions = [
       "experimental.enableProcessCPUTracks()",
     experimental: true,
   },
   {
     name: "Power Use",
     value: "power",
     title:
       "Record the value of every energy meter available on the system with " +
-      "each sample. Only available on Windows 11.",
+      "each sample. Only available on Windows 11 and Apple Silicon.",
     experimental: true,
   },
 ];
 
 module.exports = {
   formatFileSize,
   makeLinear10Scale,
   makePowerOf2Scale,
--- a/mozglue/baseprofiler/public/BaseProfilerState.h
+++ b/mozglue/baseprofiler/public/BaseProfilerState.h
@@ -216,17 +216,19 @@ class MOZ_RAII AutoProfilerStats {
           "Record markers from all registered threads")                        \
                                                                                \
     MACRO(22, "unregisteredthreads", UnregisteredThreads,                      \
           "Discover and profile unregistered threads -- beware: expensive!")   \
                                                                                \
     MACRO(23, "processcpu", ProcessCPU,                                        \
           "Sample the CPU utilization of each process")                        \
                                                                                \
-    MACRO(24, "power", Power, "Sample energy meters. (Windows 11 only)")
+    MACRO(24, "power", Power,                                                  \
+          "Sample energy meters on Windows 11 and per process power use on "   \
+          "Apple Silicon")
 // *** Synchronize with lists in ProfilerState.h and geckoProfiler.json ***
 
 struct ProfilerFeature {
 #  define DECLARE(n_, str_, Name_, desc_)                                \
     static constexpr uint32_t Name_ = (1u << n_);                        \
     [[nodiscard]] static constexpr bool Has##Name_(uint32_t aFeatures) { \
       return aFeatures & Name_;                                          \
     }                                                                    \
new file mode 100644
--- /dev/null
+++ b/tools/profiler/core/PowerCounters-mac.cpp
@@ -0,0 +1,47 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PowerCounters.h"
+
+#include <mach/mach.h>
+
+class ProcessPower final : public BaseProfilerCount {
+ public:
+  ProcessPower()
+      : BaseProfilerCount("task_energy", nullptr, nullptr, "power",
+                          "Power utilization") {}
+
+  CountSample Sample() override {
+    CountSample result;
+    result.count = GetTaskEnergy();
+    result.number = 0;
+    result.isSampleNew = true;
+    return result;
+  }
+
+ private:
+  int64_t GetTaskEnergy() {
+    task_power_info_v2_data_t task_power_info;
+    mach_msg_type_number_t count = TASK_POWER_INFO_V2_COUNT;
+    kern_return_t kr = task_info(mach_task_self(), TASK_POWER_INFO_V2,
+                                 (task_info_t)&task_power_info, &count);
+    if (kr != KERN_SUCCESS) {
+      return 0;
+    }
+
+    // task_energy is in nanojoules. To be consistent with the Windows EMI
+    // API, return values in picowatt-hour.
+    return task_power_info.task_energy / 3.6;
+  }
+};
+
+PowerCounters::PowerCounters() : mProcessPower(new ProcessPower()) {
+  if (mProcessPower) {
+    (void)mCounters.append(mProcessPower.get());
+  }
+}
+
+PowerCounters::~PowerCounters() { mCounters.clear(); }
+
+void PowerCounters::Sample() {}
--- a/tools/profiler/core/PowerCounters.h
+++ b/tools/profiler/core/PowerCounters.h
@@ -8,20 +8,23 @@
 #include "PlatformMacros.h"
 #include "mozilla/ProfilerCounts.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Vector.h"
 
 #if defined(_MSC_VER)
 class PowerMeterDevice;
 #endif
+#if defined(GP_PLAT_arm64_darwin)
+class ProcessPower;
+#endif
 
 class PowerCounters {
  public:
-#if defined(_MSC_VER)
+#if defined(_MSC_VER) || defined(GP_PLAT_arm64_darwin)
   explicit PowerCounters();
   ~PowerCounters();
   void Sample();
 #else
   explicit PowerCounters(){};
   ~PowerCounters(){};
   void Sample(){};
 #endif
@@ -30,11 +33,14 @@ class PowerCounters {
   const CountVector& GetCounters() { return mCounters; }
 
  private:
   CountVector mCounters;
 
 #if defined(_MSC_VER)
   mozilla::Vector<mozilla::UniquePtr<PowerMeterDevice>> mPowerMeterDevices;
 #endif
+#if defined(GP_PLAT_arm64_darwin)
+  mozilla::UniquePtr<ProcessPower> mProcessPower;
+#endif
 };
 
 #endif /* ndef TOOLS_POWERCOUNTERS_H_ */
--- a/tools/profiler/moz.build
+++ b/tools/profiler/moz.build
@@ -81,16 +81,20 @@ if CONFIG["MOZ_GECKO_PROFILER"]:
         if CONFIG["CPU_ARCH"] == "arm" and CONFIG["OS_TARGET"] != "FreeBSD":
             SOURCES += [
                 "core/EHABIStackWalk.cpp",
             ]
     elif CONFIG["OS_TARGET"] == "Darwin":
         UNIFIED_SOURCES += [
             "core/shared-libraries-macos.cc",
         ]
+        if CONFIG["CPU_ARCH"] == "aarch64":
+            UNIFIED_SOURCES += [
+                "core/PowerCounters-mac.cpp",
+            ]
     elif CONFIG["OS_TARGET"] == "WINNT":
         if CONFIG["CC_TYPE"] == "clang-cl":
             UNIFIED_SOURCES += [
                 "core/PowerCounters-win.cpp",
             ]
         SOURCES += [
             "core/shared-libraries-win32.cc",
         ]
--- a/tools/profiler/public/ProfilerState.h
+++ b/tools/profiler/public/ProfilerState.h
@@ -95,17 +95,19 @@
         "Record markers from all registered threads")                        \
                                                                              \
   MACRO(22, "unregisteredthreads", UnregisteredThreads,                      \
         "Discover and profile unregistered threads -- beware: expensive!")   \
                                                                              \
   MACRO(23, "processcpu", ProcessCPU,                                        \
         "Sample the CPU utilization of each process")                        \
                                                                              \
-  MACRO(24, "power", Power, "Sample energy meters. (Windows 11 only)")
+  MACRO(24, "power", Power,                                                  \
+        "Sample energy meters on Windows 11 and per process power use on "   \
+        "Apple Silicon")
 // *** Synchronize with lists in BaseProfilerState.h and geckoProfiler.json ***
 
 struct ProfilerFeature {
 #define DECLARE(n_, str_, Name_, desc_)                                \
   static constexpr uint32_t Name_ = (1u << n_);                        \
   [[nodiscard]] static constexpr bool Has##Name_(uint32_t aFeatures) { \
     return aFeatures & Name_;                                          \
   }                                                                    \