0

[power_sampler] Add resource coalition sampler.

The resource coalition sampler produces resource usage stats for a
group of processes.

Bug: 1254332
Change-Id: I79dd6013fc7a46c81c01f407a0b2546cad2440bf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3298313
Commit-Queue: François Doray <fdoray@chromium.org>
Reviewed-by: Etienne Pierre-Doray <etiennep@chromium.org>
Cr-Commit-Position: refs/heads/main@{#945470}
This commit is contained in:
Francois Doray
2021-11-25 20:36:01 +00:00
committed by Chromium LUCI CQ
parent 4357c2ffb0
commit 7fd6b3bce5
10 changed files with 1003 additions and 7 deletions

@ -20,7 +20,10 @@ if (is_mac) {
source_set("unit_tests") {
testonly = true
sources = [ "energy_impact_mac_unittest.mm" ]
sources = [
"energy_impact_mac_unittest.mm",
"resource_coalition_mac_unittest.mm",
]
data = [ "test/data/" ]

@ -29,6 +29,12 @@ absl::optional<uint64_t> GetProcessCoalitionId(base::ProcessId pid);
std::unique_ptr<coalition_resource_usage> GetCoalitionResourceUsage(
int64_t coalition_id);
// Returns a `coalition_resource_usage` in which each member is the result of
// substracting the corresponding fields in `left` and `right`.
coalition_resource_usage GetCoalitionResourceUsageDifference(
const coalition_resource_usage& left,
const coalition_resource_usage& right);
} // namespace power_metrics
#endif // COMPONENTS_PPOWER_METRICS_RESOURCE_COALITION_MAC_H_

@ -36,4 +36,101 @@ std::unique_ptr<coalition_resource_usage> GetCoalitionResourceUsage(
return nullptr;
}
coalition_resource_usage GetCoalitionResourceUsageDifference(
const coalition_resource_usage& left,
const coalition_resource_usage& right) {
DCHECK_GE(left.tasks_started, right.tasks_started);
DCHECK_GE(left.tasks_exited, right.tasks_exited);
DCHECK_GE(left.time_nonempty, right.time_nonempty);
DCHECK_GE(left.cpu_time, right.cpu_time);
DCHECK_GE(left.interrupt_wakeups, right.interrupt_wakeups);
DCHECK_GE(left.platform_idle_wakeups, right.platform_idle_wakeups);
DCHECK_GE(left.bytesread, right.bytesread);
DCHECK_GE(left.byteswritten, right.byteswritten);
DCHECK_GE(left.gpu_time, right.gpu_time);
DCHECK_GE(left.cpu_time_billed_to_me, right.cpu_time_billed_to_me);
DCHECK_GE(left.cpu_time_billed_to_others, right.cpu_time_billed_to_others);
DCHECK_GE(left.energy, right.energy);
DCHECK_GE(left.logical_immediate_writes, right.logical_immediate_writes);
DCHECK_GE(left.logical_deferred_writes, right.logical_deferred_writes);
DCHECK_GE(left.logical_invalidated_writes, right.logical_invalidated_writes);
DCHECK_GE(left.logical_metadata_writes, right.logical_metadata_writes);
DCHECK_GE(left.logical_immediate_writes_to_external,
right.logical_immediate_writes_to_external);
DCHECK_GE(left.logical_deferred_writes_to_external,
right.logical_deferred_writes_to_external);
DCHECK_GE(left.logical_invalidated_writes_to_external,
right.logical_invalidated_writes_to_external);
DCHECK_GE(left.logical_metadata_writes_to_external,
right.logical_metadata_writes_to_external);
DCHECK_GE(left.energy_billed_to_me, right.energy_billed_to_me);
DCHECK_GE(left.energy_billed_to_others, right.energy_billed_to_others);
DCHECK_GE(left.cpu_ptime, right.cpu_ptime);
DCHECK_GE(left.cpu_instructions, right.cpu_instructions);
DCHECK_GE(left.cpu_cycles, right.cpu_cycles);
DCHECK_GE(left.fs_metadata_writes, right.fs_metadata_writes);
DCHECK_GE(left.pm_writes, right.pm_writes);
coalition_resource_usage ret;
ret.tasks_started = left.tasks_started - right.tasks_started;
ret.tasks_exited = left.tasks_exited - right.tasks_exited;
ret.time_nonempty = left.time_nonempty - right.time_nonempty;
ret.cpu_time = left.cpu_time - right.cpu_time;
ret.interrupt_wakeups = left.interrupt_wakeups - right.interrupt_wakeups;
ret.platform_idle_wakeups =
left.platform_idle_wakeups - right.platform_idle_wakeups;
ret.bytesread = left.bytesread - right.bytesread;
ret.byteswritten = left.byteswritten - right.byteswritten;
ret.gpu_time = left.gpu_time - right.gpu_time;
ret.cpu_time_billed_to_me =
left.cpu_time_billed_to_me - right.cpu_time_billed_to_me;
ret.cpu_time_billed_to_others =
left.cpu_time_billed_to_others - right.cpu_time_billed_to_others;
ret.energy = left.energy - right.energy;
ret.logical_immediate_writes =
left.logical_immediate_writes - right.logical_immediate_writes;
ret.logical_deferred_writes =
left.logical_deferred_writes - right.logical_deferred_writes;
ret.logical_invalidated_writes =
left.logical_invalidated_writes - right.logical_invalidated_writes;
ret.logical_metadata_writes =
left.logical_metadata_writes - right.logical_metadata_writes;
ret.logical_immediate_writes_to_external =
left.logical_immediate_writes_to_external -
right.logical_immediate_writes_to_external;
ret.logical_deferred_writes_to_external =
left.logical_deferred_writes_to_external -
right.logical_deferred_writes_to_external;
ret.logical_invalidated_writes_to_external =
left.logical_invalidated_writes_to_external -
right.logical_invalidated_writes_to_external;
ret.logical_metadata_writes_to_external =
left.logical_metadata_writes_to_external -
right.logical_metadata_writes_to_external;
ret.energy_billed_to_me =
left.energy_billed_to_me - right.energy_billed_to_me;
ret.energy_billed_to_others =
left.energy_billed_to_others - right.energy_billed_to_others;
ret.cpu_ptime = left.cpu_ptime - right.cpu_ptime;
DCHECK_EQ(left.cpu_time_eqos_len,
static_cast<uint64_t>(COALITION_NUM_THREAD_QOS_TYPES));
DCHECK_EQ(right.cpu_time_eqos_len,
static_cast<uint64_t>(COALITION_NUM_THREAD_QOS_TYPES));
ret.cpu_time_eqos_len = left.cpu_time_eqos_len;
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
DCHECK_GE(left.cpu_time_eqos[i], right.cpu_time_eqos[i]);
ret.cpu_time_eqos[i] = left.cpu_time_eqos[i] - right.cpu_time_eqos[i];
}
ret.cpu_instructions = left.cpu_instructions - right.cpu_instructions;
ret.cpu_cycles = left.cpu_cycles - right.cpu_cycles;
ret.fs_metadata_writes = left.fs_metadata_writes - right.fs_metadata_writes;
ret.pm_writes = left.pm_writes - right.pm_writes;
return ret;
}
} // power_metrics

@ -0,0 +1,101 @@
// 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/resource_coalition_mac.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace power_metrics {
namespace {
coalition_resource_usage GetTestCoalitionResourceUsage(uint32_t increment) {
coalition_resource_usage ret{
.tasks_started = 1 + increment,
.tasks_exited = 2 + increment,
.time_nonempty = 3 + increment,
.cpu_time = 4 + increment,
.interrupt_wakeups = 5 + increment,
.platform_idle_wakeups = 6 + increment,
.bytesread = 7 + increment,
.byteswritten = 8 + increment,
.gpu_time = 9 + increment,
.cpu_time_billed_to_me = 10 + increment,
.cpu_time_billed_to_others = 11 + increment,
.energy = 12 + increment,
.logical_immediate_writes = 13 + increment,
.logical_deferred_writes = 14 + increment,
.logical_invalidated_writes = 15 + increment,
.logical_metadata_writes = 16 + increment,
.logical_immediate_writes_to_external = 17 + increment,
.logical_deferred_writes_to_external = 18 + increment,
.logical_invalidated_writes_to_external = 19 + increment,
.logical_metadata_writes_to_external = 20 + increment,
.energy_billed_to_me = 21 + increment,
.energy_billed_to_others = 22 + increment,
.cpu_ptime = 23 + increment,
.cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES,
.cpu_instructions = 31 + increment,
.cpu_cycles = 32 + increment,
.fs_metadata_writes = 33 + increment,
.pm_writes = 34 + increment,
};
ret.cpu_time_eqos[THREAD_QOS_DEFAULT] = 24 + increment;
ret.cpu_time_eqos[THREAD_QOS_MAINTENANCE] = 25 + increment;
ret.cpu_time_eqos[THREAD_QOS_BACKGROUND] = 26 + increment;
ret.cpu_time_eqos[THREAD_QOS_UTILITY] = 27 + increment;
ret.cpu_time_eqos[THREAD_QOS_LEGACY] = 28 + increment;
ret.cpu_time_eqos[THREAD_QOS_USER_INITIATED] = 29 + increment;
ret.cpu_time_eqos[THREAD_QOS_USER_INTERACTIVE] = 30 + increment;
return ret;
}
} // namespace
TEST(ResourceCoalitionMacTest, Difference) {
coalition_resource_usage left =
GetTestCoalitionResourceUsage(/* increment= */ 1);
coalition_resource_usage right =
GetTestCoalitionResourceUsage(/* increment= */ 0);
coalition_resource_usage diff =
GetCoalitionResourceUsageDifference(left, right);
EXPECT_EQ(diff.tasks_started, 1U);
EXPECT_EQ(diff.tasks_exited, 1U);
EXPECT_EQ(diff.time_nonempty, 1U);
EXPECT_EQ(diff.cpu_time, 1U);
EXPECT_EQ(diff.interrupt_wakeups, 1U);
EXPECT_EQ(diff.platform_idle_wakeups, 1U);
EXPECT_EQ(diff.bytesread, 1U);
EXPECT_EQ(diff.byteswritten, 1U);
EXPECT_EQ(diff.gpu_time, 1U);
EXPECT_EQ(diff.cpu_time_billed_to_me, 1U);
EXPECT_EQ(diff.cpu_time_billed_to_others, 1U);
EXPECT_EQ(diff.energy, 1U);
EXPECT_EQ(diff.logical_immediate_writes, 1U);
EXPECT_EQ(diff.logical_deferred_writes, 1U);
EXPECT_EQ(diff.logical_invalidated_writes, 1U);
EXPECT_EQ(diff.logical_metadata_writes, 1U);
EXPECT_EQ(diff.logical_immediate_writes_to_external, 1U);
EXPECT_EQ(diff.logical_deferred_writes_to_external, 1U);
EXPECT_EQ(diff.logical_invalidated_writes_to_external, 1U);
EXPECT_EQ(diff.logical_metadata_writes_to_external, 1U);
EXPECT_EQ(diff.energy_billed_to_me, 1U);
EXPECT_EQ(diff.energy_billed_to_others, 1U);
EXPECT_EQ(diff.cpu_ptime, 1U);
EXPECT_EQ(diff.cpu_time_eqos_len,
static_cast<uint64_t>(COALITION_NUM_THREAD_QOS_TYPES));
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i)
EXPECT_EQ(diff.cpu_time_eqos[i], 1U);
EXPECT_EQ(diff.cpu_instructions, 1U);
EXPECT_EQ(diff.cpu_cycles, 1U);
EXPECT_EQ(diff.fs_metadata_writes, 1U);
EXPECT_EQ(diff.pm_writes, 1U);
}
} // namespace power_metrics

@ -37,6 +37,8 @@ static_library("power_sampler_lib") {
"power_sampler/main_display_sampler.h",
"power_sampler/monitor.cc",
"power_sampler/monitor.h",
"power_sampler/resource_coalition_sampler.cc",
"power_sampler/resource_coalition_sampler.h",
"power_sampler/sample_counter.cc",
"power_sampler/sample_counter.h",
"power_sampler/sampler.cc",
@ -50,7 +52,10 @@ static_library("power_sampler_lib") {
"power_sampler/user_idle_level_sampler.cc",
"power_sampler/user_idle_level_sampler.h",
]
deps = [ "//base" ]
deps = [
"//base",
"//components/power_metrics",
]
frameworks = [
"CoreGraphics.framework",
"Foundation.framework",
@ -77,6 +82,7 @@ test("power_sampler_unittests") {
"power_sampler/csv_exporter_unittest.cc",
"power_sampler/json_exporter_unittest.cc",
"power_sampler/main_display_sampler_unittest.cc",
"power_sampler/resource_coalition_sampler_unittest.cc",
"power_sampler/sampling_controller_unittest.cc",
"power_sampler/timer_sampling_event_source_unittest.cc",
"power_sampler/user_idle_level_sampler_unittest.cc",
@ -87,6 +93,7 @@ test("power_sampler_unittests") {
"//base",
"//base/test:run_all_unittests",
"//base/test:test_support",
"//components/power_metrics",
"//testing/gmock",
"//testing/gtest",
]

@ -0,0 +1,3 @@
include_rules = [
"+components/power_metrics",
]

@ -9,6 +9,7 @@
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/process/process_handle.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/single_thread_task_executor.h"
@ -18,6 +19,7 @@
#include "tools/mac/power/power_sampler/iopm_power_source_sampling_event_source.h"
#include "tools/mac/power/power_sampler/json_exporter.h"
#include "tools/mac/power/power_sampler/main_display_sampler.h"
#include "tools/mac/power/power_sampler/resource_coalition_sampler.h"
#include "tools/mac/power/power_sampler/sample_counter.h"
#include "tools/mac/power/power_sampler/sampler.h"
#include "tools/mac/power/power_sampler/sampling_controller.h"
@ -42,18 +44,21 @@ constexpr char kSwitchSampleInterval[] = "sample-interval";
constexpr char kSwitchSampleCount[] = "sample-count";
constexpr char kSwitchJsonOutputFile[] = "json-output-file";
constexpr char kSwitchSampleOnNotification[] = "sample-on-notification";
constexpr char kSwitchResourceCoalitionPid[] = "resource-coalition-pid";
constexpr char kUsageString[] = R"(Usage: power_sampler [options]
A tool that samples power-related metrics and states. The tool outputs samples
in CSV or JSON format.
Options:
--sample-interval=<num> Sample on a <num> second interval.
--sample-on-notification Sample on power manager notifications.
--sample-interval=<num> Sample on a <num> second interval.
--sample-on-notification Sample on power manager notifications.
Note that interval and event notifications are mutually exclusive.
--sample-count=<num> Collect <num> samples before exiting.
--json-output-file=<path> Produce JSON output to <path> before exit.
--sample-count=<num> Collect <num> samples before exiting.
--json-output-file=<path> Produce JSON output to <path> before exit.
By default output is in CSV format on STDOUT.
--resource-coalition-pid=<pid> The pid of a process that is part of a
resource coalition for which to sample resource usage.
)";
// Prints main usage text.
@ -138,6 +143,8 @@ int main(int argc, char** argv) {
base::SingleThreadTaskExecutor executor(base::MessagePumpType::NS_RUNLOOP);
power_sampler::SamplingController controller;
const base::TimeTicks start_time = base::TimeTicks::Now();
std::unique_ptr<power_sampler::Sampler> sampler =
power_sampler::MainDisplaySampler::Create();
if (sampler)
@ -151,7 +158,24 @@ int main(int argc, char** argv) {
if (sampler)
controller.AddSampler(std::move(sampler));
base::TimeTicks start_time = base::TimeTicks::Now();
if (command_line.HasSwitch(kSwitchResourceCoalitionPid)) {
std::string resource_coalition_pid_switch =
command_line.GetSwitchValueASCII(kSwitchResourceCoalitionPid);
base::ProcessId pid;
if (!base::StringToInt(resource_coalition_pid_switch, &pid) || pid < 0) {
PrintUsage("resource-coalition-pid must be numeric and positive.");
return kStatusInvalidParam;
}
sampler = power_sampler::ResourceCoalitionSampler::Create(pid, start_time);
if (!sampler) {
PrintUsage(
"Could not create a resource coalition sampler. Is the pid passed to "
"--resource-coalition-pid valid?");
return kStatusRuntimeError;
}
controller.AddSampler(std::move(sampler));
}
if (!json_output_file_path.empty()) {
controller.AddMonitor(power_sampler::JsonExporter::Create(
std::move(json_output_file_path), start_time));

@ -0,0 +1,227 @@
// 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 "tools/mac/power/power_sampler/resource_coalition_sampler.h"
#include "base/memory/ptr_util.h"
#include "components/power_metrics/mach_time_mac.h"
namespace power_sampler {
namespace {
double RatePerSecond(int64_t quantity, base::TimeDelta duration) {
return static_cast<double>(quantity) / duration.InSecondsF();
}
double RatePerSecondFromMachTime(int64_t mach_time,
const mach_timebase_info_data_t& timebase,
base::TimeDelta duration) {
return RatePerSecond(power_metrics::MachTimeToNs(mach_time, timebase),
duration);
}
} // namespace
ResourceCoalitionSampler::~ResourceCoalitionSampler() = default;
// static
std::unique_ptr<ResourceCoalitionSampler> ResourceCoalitionSampler::Create(
base::ProcessId pid,
base::TimeTicks start_time) {
return Create(pid, start_time, &power_metrics::GetProcessCoalitionId,
&power_metrics::GetCoalitionResourceUsage,
power_metrics::GetSystemMachTimeBase());
}
std::string ResourceCoalitionSampler::GetName() {
return "resource_coalition";
}
Sampler::DatumNameUnits ResourceCoalitionSampler::GetDatumNameUnits() {
DatumNameUnits ret{{"tasks_started", "tasks/s"},
{"tasks_exited", "tasks/s"},
{"time_nonempty", "ns/s"},
{"cpu_time", "ns/s"},
{"interrupt_wakeups", "wakeups/s"},
{"platform_idle_wakeups", "wakeups/s"},
{"bytesread", "bytes/s"},
{"byteswritten", "bytes/s"},
{"gpu_time", "ns/s"},
{"cpu_time_billed_to_me", "ns/s"},
{"cpu_time_billed_to_others", "ns/s"},
{"energy", "nw"},
{"logical_immediate_writes", "writes/s"},
{"logical_deferred_writes", "writes/s"},
{"logical_invalidated_writes", "writes/s"},
{"logical_metadata_writes", "writes/s"},
{"logical_immediate_writes_to_external", "writes/s"},
{"logical_deferred_writes_to_external", "writes/s"},
{"logical_invalidated_writes_to_external", "writes/s"},
{"logical_metadata_writes_to_external", "writes/s"},
{"energy_billed_to_me", "nw"},
{"energy_billed_to_others", "nw"},
{"cpu_ptime", "ns/s"},
{"cpu_time_qos_background", "ns/s"},
{"cpu_time_qos_default", "ns/s"},
{"cpu_time_qos_legacy", "ns/s"},
{"cpu_time_qos_maintenance", "ns/s"},
{"cpu_time_qos_user_initiated", "ns/s"},
{"cpu_time_qos_user_interactive", "ns/s"},
{"cpu_time_qos_utility", "ns/s"},
{"cpu_instructions", "instructions/s"},
{"cpu_cycles", "cycles/s"},
{"fs_metadata_writes", "writes/s"},
{"pm_writes", "writes/s"}};
return ret;
}
Sampler::Sample ResourceCoalitionSampler::GetSample(
base::TimeTicks sample_time) {
std::unique_ptr<coalition_resource_usage> current_stats =
get_coalition_resource_usage_fn_(coalition_id_);
// Current stats are not available: discard previous stats so that they aren't
// used to compute a difference in the future.
if (!current_stats) {
previous_time_ = base::TimeTicks();
previous_stats_.reset();
return Sample();
}
// Previous stats are not available: store the current stats so that they can
// be used to compute a difference in the future.
if (!previous_stats_) {
previous_time_ = sample_time;
previous_stats_ = std::move(current_stats);
return Sample();
}
// Previous and current stats are available: compute the difference and output
// a sample.
coalition_resource_usage diff =
power_metrics::GetCoalitionResourceUsageDifference(*current_stats,
*previous_stats_);
base::TimeDelta duration = sample_time - previous_time_;
previous_time_ = sample_time;
previous_stats_ = std::move(current_stats);
DCHECK_GE(duration, base::TimeDelta());
if (duration.is_zero())
return Sample();
Sample sample;
sample.emplace("tasks_started", RatePerSecond(diff.tasks_started, duration));
sample.emplace("tasks_exited", RatePerSecond(diff.tasks_exited, duration));
sample.emplace("time_nonempty", RatePerSecond(diff.time_nonempty, duration));
sample.emplace("cpu_time",
RatePerSecondFromMachTime(diff.cpu_time, timebase_, duration));
sample.emplace("interrupt_wakeups",
RatePerSecond(diff.interrupt_wakeups, duration));
sample.emplace("platform_idle_wakeups",
RatePerSecond(diff.platform_idle_wakeups, duration));
sample.emplace("bytesread", RatePerSecond(diff.bytesread, duration));
sample.emplace("byteswritten", RatePerSecond(diff.byteswritten, duration));
sample.emplace("gpu_time",
RatePerSecondFromMachTime(diff.gpu_time, timebase_, duration));
sample.emplace("cpu_time_billed_to_me",
RatePerSecondFromMachTime(diff.cpu_time_billed_to_me,
timebase_, duration));
sample.emplace("cpu_time_billed_to_others",
RatePerSecondFromMachTime(diff.cpu_time_billed_to_others,
timebase_, duration));
sample.emplace("energy", RatePerSecond(diff.energy, duration));
sample.emplace("logical_immediate_writes",
RatePerSecond(diff.logical_immediate_writes, duration));
sample.emplace("logical_deferred_writes",
RatePerSecond(diff.logical_deferred_writes, duration));
sample.emplace("logical_invalidated_writes",
RatePerSecond(diff.logical_invalidated_writes, duration));
sample.emplace("logical_metadata_writes",
RatePerSecond(diff.logical_metadata_writes, duration));
sample.emplace(
"logical_immediate_writes_to_external",
RatePerSecond(diff.logical_immediate_writes_to_external, duration));
sample.emplace(
"logical_deferred_writes_to_external",
RatePerSecond(diff.logical_deferred_writes_to_external, duration));
sample.emplace(
"logical_invalidated_writes_to_external",
RatePerSecond(diff.logical_invalidated_writes_to_external, duration));
sample.emplace(
"logical_metadata_writes_to_external",
RatePerSecond(diff.logical_metadata_writes_to_external, duration));
sample.emplace("energy_billed_to_me",
RatePerSecond(diff.energy_billed_to_me, duration));
sample.emplace("energy_billed_to_others",
RatePerSecond(diff.energy_billed_to_others, duration));
sample.emplace("cpu_ptime", RatePerSecondFromMachTime(diff.cpu_ptime,
timebase_, duration));
sample.emplace(
"cpu_time_qos_background",
RatePerSecondFromMachTime(diff.cpu_time_eqos[THREAD_QOS_BACKGROUND],
timebase_, duration));
sample.emplace(
"cpu_time_qos_default",
RatePerSecondFromMachTime(diff.cpu_time_eqos[THREAD_QOS_DEFAULT],
timebase_, duration));
sample.emplace(
"cpu_time_qos_legacy",
RatePerSecondFromMachTime(diff.cpu_time_eqos[THREAD_QOS_LEGACY],
timebase_, duration));
sample.emplace(
"cpu_time_qos_maintenance",
RatePerSecondFromMachTime(diff.cpu_time_eqos[THREAD_QOS_MAINTENANCE],
timebase_, duration));
sample.emplace(
"cpu_time_qos_user_initiated",
RatePerSecondFromMachTime(diff.cpu_time_eqos[THREAD_QOS_USER_INITIATED],
timebase_, duration));
sample.emplace(
"cpu_time_qos_user_interactive",
RatePerSecondFromMachTime(diff.cpu_time_eqos[THREAD_QOS_USER_INTERACTIVE],
timebase_, duration));
sample.emplace(
"cpu_time_qos_utility",
RatePerSecondFromMachTime(diff.cpu_time_eqos[THREAD_QOS_UTILITY],
timebase_, duration));
sample.emplace("cpu_instructions",
RatePerSecond(diff.cpu_instructions, duration));
sample.emplace("cpu_cycles", RatePerSecond(diff.cpu_cycles, duration));
sample.emplace("fs_metadata_writes",
RatePerSecond(diff.fs_metadata_writes, duration));
sample.emplace("pm_writes", RatePerSecond(diff.pm_writes, duration));
return sample;
}
// static
std::unique_ptr<ResourceCoalitionSampler> ResourceCoalitionSampler::Create(
base::ProcessId pid,
base::TimeTicks now,
GetProcessCoalitionIdFn get_process_coalition_id_fn,
GetCoalitionResourceUsageFn get_coalition_resource_usage_fn,
mach_timebase_info_data_t timebase) {
absl::optional<uint64_t> coalition_id = get_process_coalition_id_fn(pid);
if (!coalition_id.has_value()) {
return nullptr;
}
return base::WrapUnique(new ResourceCoalitionSampler(
coalition_id.value(), now, get_coalition_resource_usage_fn, timebase));
}
ResourceCoalitionSampler::ResourceCoalitionSampler(
uint64_t coalition_id,
base::TimeTicks now,
GetCoalitionResourceUsageFn get_coalition_resource_usage_fn,
mach_timebase_info_data_t timebase)
: coalition_id_(coalition_id),
get_coalition_resource_usage_fn_(get_coalition_resource_usage_fn),
timebase_(timebase),
previous_time_(now),
previous_stats_(get_coalition_resource_usage_fn_(coalition_id_)) {}
} // namespace power_sampler

@ -0,0 +1,75 @@
// 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 TOOLS_MAC_POWER_POWER_SAMPLER_RESOURCE_COALITION_SAMPLER_H_
#define TOOLS_MAC_POWER_POWER_SAMPLER_RESOURCE_COALITION_SAMPLER_H_
#include <mach/mach_time.h>
#include <stdint.h>
#include <memory>
#include "base/process/process_handle.h"
#include "components/power_metrics/resource_coalition_mac.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "tools/mac/power/power_sampler/sampler.h"
namespace power_sampler {
class ResourceCoalitionSamplerTest;
// The resource coalition sampler provides resource usage data for a group of
// tasks that are part of a "resource coalition", including those that have
// died. Typically, a "resource coalition" includes a root process and its
// descendants. "Resource coalition" is an undocumented mechanism available in
// macOS. Some information is available in the source
// (https://github.com/apple/darwin-xnu/blob/main/osfmk/kern/coalition.c).
class ResourceCoalitionSampler : public Sampler {
public:
~ResourceCoalitionSampler() override;
// Creates and initializes a new sampler. |pid| is the pid of any process in
// the "resource coalition" to sample. |start_time| is the time at which this
// is invoked. Returns nullptr on failure.
static std::unique_ptr<ResourceCoalitionSampler> Create(
base::ProcessId pid,
base::TimeTicks start_time);
// Sampler implementation.
std::string GetName() override;
DatumNameUnits GetDatumNameUnits() override;
Sample GetSample(base::TimeTicks sample_time) override;
private:
friend class power_sampler::ResourceCoalitionSamplerTest;
// Exposed for testing only.
using GetProcessCoalitionIdFn =
absl::optional<uint64_t> (*)(base::ProcessId pid);
using GetCoalitionResourceUsageFn =
std::unique_ptr<coalition_resource_usage> (*)(int64_t coalition_id);
static std::unique_ptr<ResourceCoalitionSampler> Create(
base::ProcessId pid,
base::TimeTicks start_time,
GetProcessCoalitionIdFn get_process_coalition_id_fn,
GetCoalitionResourceUsageFn get_coalition_resource_usage_fn,
mach_timebase_info_data_t timebase);
ResourceCoalitionSampler(
uint64_t coalition_id,
base::TimeTicks now,
GetCoalitionResourceUsageFn get_coalition_resource_usage_fn,
mach_timebase_info_data_t timebase);
const uint64_t coalition_id_;
const GetCoalitionResourceUsageFn get_coalition_resource_usage_fn_;
const mach_timebase_info_data_t timebase_;
base::TimeTicks previous_time_;
std::unique_ptr<coalition_resource_usage> previous_stats_;
};
} // namespace power_sampler
#endif // TOOLS_MAC_POWER_POWER_SAMPLER_RESOURCE_COALITION_SAMPLER_H_

@ -0,0 +1,453 @@
// 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 "tools/mac/power/power_sampler/resource_coalition_sampler.h"
#include <stdint.h>
#include <memory>
#include "base/process/process_handle.h"
#include "components/power_metrics/mach_time_mac.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace power_sampler {
namespace {
using testing::UnorderedElementsAre;
constexpr base::ProcessId kTestPid = 42;
constexpr base::ProcessId kTestCoalitionId = 123;
constexpr mach_timebase_info_data_t kIntelTimebase = {1, 1};
mach_timebase_info_data_t kM1Timebase = {125, 3};
coalition_resource_usage GetTestCoalitionResourceUsage(uint32_t multiplier) {
coalition_resource_usage ret{
.tasks_started = 1 * multiplier,
.tasks_exited = 2 * multiplier,
.time_nonempty = 3 * multiplier,
.cpu_time = 4 * multiplier,
.interrupt_wakeups = 5 * multiplier,
.platform_idle_wakeups = 6 * multiplier,
.bytesread = 7 * multiplier,
.byteswritten = 8 * multiplier,
.gpu_time = 9 * multiplier,
.cpu_time_billed_to_me = 10 * multiplier,
.cpu_time_billed_to_others = 11 * multiplier,
.energy = 12 * multiplier,
.logical_immediate_writes = 13 * multiplier,
.logical_deferred_writes = 14 * multiplier,
.logical_invalidated_writes = 15 * multiplier,
.logical_metadata_writes = 16 * multiplier,
.logical_immediate_writes_to_external = 17 * multiplier,
.logical_deferred_writes_to_external = 18 * multiplier,
.logical_invalidated_writes_to_external = 19 * multiplier,
.logical_metadata_writes_to_external = 20 * multiplier,
.energy_billed_to_me = 21 * multiplier,
.energy_billed_to_others = 22 * multiplier,
.cpu_ptime = 23 * multiplier,
.cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES,
.cpu_instructions = 31 * multiplier,
.cpu_cycles = 32 * multiplier,
.fs_metadata_writes = 33 * multiplier,
.pm_writes = 34 * multiplier,
};
ret.cpu_time_eqos[THREAD_QOS_DEFAULT] = 24 * multiplier;
ret.cpu_time_eqos[THREAD_QOS_MAINTENANCE] = 25 * multiplier;
ret.cpu_time_eqos[THREAD_QOS_BACKGROUND] = 26 * multiplier;
ret.cpu_time_eqos[THREAD_QOS_UTILITY] = 27 * multiplier;
ret.cpu_time_eqos[THREAD_QOS_LEGACY] = 28 * multiplier;
ret.cpu_time_eqos[THREAD_QOS_USER_INITIATED] = 29 * multiplier;
ret.cpu_time_eqos[THREAD_QOS_USER_INTERACTIVE] = 30 * multiplier;
return ret;
}
double NsToM1Timebase(int64_t ns) {
return static_cast<double>(ns * kM1Timebase.numer) / kM1Timebase.denom;
}
} // namespace
class ResourceCoalitionSamplerTest : public testing::Test {
protected:
void SetUp() override {
set_expected_process_id(-1);
set_coalition_id(absl::nullopt);
set_resource_usage(absl::nullopt);
}
static void set_expected_process_id(base::ProcessId expected_process_id) {
expected_process_id_ = expected_process_id;
}
static void set_coalition_id(absl::optional<uint64_t> coalition_id) {
coalition_id_ = coalition_id;
}
static void set_resource_usage(
absl::optional<coalition_resource_usage> resource_usage) {
resource_usage_ = resource_usage;
}
// The gmock *ElementsAre* matchers are too exacting for the double values
// in our samples, but this poor man's substitute will do for our needs.
template <size_t N>
void ExpectSampleMatchesArray(
const Sampler::Sample& sample,
const std::pair<std::string, double> (&datums)[N]) {
EXPECT_EQ(N, sample.size());
for (size_t i = 0; i < N; ++i) {
const auto& name = datums[i].first;
const double value = datums[i].second;
auto it = sample.find(name);
EXPECT_TRUE(it != sample.end()) << " for " << name;
if (it != sample.end())
EXPECT_DOUBLE_EQ(it->second, value) << " for " << name;
}
}
std::unique_ptr<ResourceCoalitionSampler> CreateSampler(
base::ProcessId pid,
base::TimeTicks now,
mach_timebase_info_data_t timebase) {
return ResourceCoalitionSampler::Create(
pid, now, &GetStaticProcessCoalitionId,
&GetStaticCoalitionResourceUsage, timebase);
}
private:
static absl::optional<uint64_t> GetStaticProcessCoalitionId(
base::ProcessId pid) {
EXPECT_EQ(pid, expected_process_id_);
return coalition_id_;
}
static std::unique_ptr<coalition_resource_usage>
GetStaticCoalitionResourceUsage(int64_t coalition_id) {
EXPECT_EQ(coalition_id, coalition_id_);
if (!resource_usage_.has_value())
return nullptr;
return std::make_unique<coalition_resource_usage>(resource_usage_.value());
}
static base::ProcessId expected_process_id_;
static absl::optional<int64_t> coalition_id_;
static absl::optional<coalition_resource_usage> resource_usage_;
};
// static
base::ProcessId ResourceCoalitionSamplerTest::expected_process_id_ = -1;
absl::optional<int64_t> ResourceCoalitionSamplerTest::coalition_id_;
absl::optional<coalition_resource_usage>
ResourceCoalitionSamplerTest::resource_usage_;
TEST_F(ResourceCoalitionSamplerTest, CreateFailsWhenNoCoalitionId) {
set_expected_process_id(kTestPid);
EXPECT_EQ(nullptr,
CreateSampler(kTestPid, base::TimeTicks(), kIntelTimebase));
}
TEST_F(ResourceCoalitionSamplerTest, CreateSucceedsWithCoalitonId) {
set_expected_process_id(kTestPid);
set_coalition_id(kTestCoalitionId);
EXPECT_NE(nullptr,
CreateSampler(kTestPid, base::TimeTicks(), kIntelTimebase));
}
TEST_F(ResourceCoalitionSamplerTest, NameAndGetDatumNameUnits) {
set_expected_process_id(kTestPid);
set_coalition_id(kTestCoalitionId);
std::unique_ptr<ResourceCoalitionSampler> sampler(
CreateSampler(kTestPid, base::TimeTicks(), kIntelTimebase));
ASSERT_NE(nullptr, sampler);
EXPECT_EQ("resource_coalition", sampler->GetName());
auto datum_name_units = sampler->GetDatumNameUnits();
EXPECT_THAT(
datum_name_units,
UnorderedElementsAre(
std::make_pair("tasks_started", "tasks/s"),
std::make_pair("tasks_exited", "tasks/s"),
std::make_pair("time_nonempty", "ns/s"),
std::make_pair("cpu_time", "ns/s"),
std::make_pair("interrupt_wakeups", "wakeups/s"),
std::make_pair("platform_idle_wakeups", "wakeups/s"),
std::make_pair("bytesread", "bytes/s"),
std::make_pair("byteswritten", "bytes/s"),
std::make_pair("gpu_time", "ns/s"),
std::make_pair("cpu_time_billed_to_me", "ns/s"),
std::make_pair("cpu_time_billed_to_others", "ns/s"),
std::make_pair("energy", "nw"),
std::make_pair("logical_immediate_writes", "writes/s"),
std::make_pair("logical_deferred_writes", "writes/s"),
std::make_pair("logical_invalidated_writes", "writes/s"),
std::make_pair("logical_metadata_writes", "writes/s"),
std::make_pair("logical_immediate_writes_to_external", "writes/s"),
std::make_pair("logical_deferred_writes_to_external", "writes/s"),
std::make_pair("logical_invalidated_writes_to_external", "writes/s"),
std::make_pair("logical_metadata_writes_to_external", "writes/s"),
std::make_pair("energy_billed_to_me", "nw"),
std::make_pair("energy_billed_to_others", "nw"),
std::make_pair("cpu_ptime", "ns/s"),
std::make_pair("cpu_time_qos_default", "ns/s"),
std::make_pair("cpu_time_qos_background", "ns/s"),
std::make_pair("cpu_time_qos_utility", "ns/s"),
std::make_pair("cpu_time_qos_legacy", "ns/s"),
std::make_pair("cpu_time_qos_maintenance", "ns/s"),
std::make_pair("cpu_time_qos_user_initiated", "ns/s"),
std::make_pair("cpu_time_qos_user_interactive", "ns/s"),
std::make_pair("cpu_instructions", "instructions/s"),
std::make_pair("cpu_cycles", "cycles/s"),
std::make_pair("fs_metadata_writes", "writes/s"),
std::make_pair("pm_writes", "writes/s")));
}
TEST_F(ResourceCoalitionSamplerTest, GetSample_Available_IntelTimebase) {
set_expected_process_id(kTestPid);
set_coalition_id(kTestCoalitionId);
set_resource_usage(GetTestCoalitionResourceUsage(
/* multiplier=*/1 * base::Time::kSecondsPerMinute));
std::unique_ptr<ResourceCoalitionSampler> sampler(
CreateSampler(kTestPid, base::TimeTicks(), kIntelTimebase));
set_resource_usage(GetTestCoalitionResourceUsage(
/* multiplier=*/2 * base::Time::kSecondsPerMinute));
Sampler::Sample sample =
sampler->GetSample(base::TimeTicks() + base::Minutes(1));
ExpectSampleMatchesArray(
sample, {std::make_pair("tasks_started", 1),
std::make_pair("tasks_exited", 2),
std::make_pair("time_nonempty", 3),
std::make_pair("cpu_time", 4),
std::make_pair("interrupt_wakeups", 5),
std::make_pair("platform_idle_wakeups", 6),
std::make_pair("bytesread", 7),
std::make_pair("byteswritten", 8),
std::make_pair("gpu_time", 9),
std::make_pair("cpu_time_billed_to_me", 10),
std::make_pair("cpu_time_billed_to_others", 11),
std::make_pair("energy", 12),
std::make_pair("logical_immediate_writes", 13),
std::make_pair("logical_deferred_writes", 14),
std::make_pair("logical_invalidated_writes", 15),
std::make_pair("logical_metadata_writes", 16),
std::make_pair("logical_immediate_writes_to_external", 17),
std::make_pair("logical_deferred_writes_to_external", 18),
std::make_pair("logical_invalidated_writes_to_external", 19),
std::make_pair("logical_metadata_writes_to_external", 20),
std::make_pair("energy_billed_to_me", 21),
std::make_pair("energy_billed_to_others", 22),
std::make_pair("cpu_ptime", 23),
std::make_pair("cpu_time_qos_default", 24),
std::make_pair("cpu_time_qos_maintenance", 25),
std::make_pair("cpu_time_qos_background", 26),
std::make_pair("cpu_time_qos_utility", 27),
std::make_pair("cpu_time_qos_legacy", 28),
std::make_pair("cpu_time_qos_user_initiated", 29),
std::make_pair("cpu_time_qos_user_interactive", 30),
std::make_pair("cpu_instructions", 31),
std::make_pair("cpu_cycles", 32),
std::make_pair("fs_metadata_writes", 33),
std::make_pair("pm_writes", 34)});
set_resource_usage(GetTestCoalitionResourceUsage(
/* multiplier=*/4 * base::Time::kSecondsPerMinute));
sample = sampler->GetSample(base::TimeTicks() + base::Minutes(2));
ExpectSampleMatchesArray(
sample, {std::make_pair("tasks_started", 2),
std::make_pair("tasks_exited", 4),
std::make_pair("time_nonempty", 6),
std::make_pair("cpu_time", 8),
std::make_pair("interrupt_wakeups", 10),
std::make_pair("platform_idle_wakeups", 12),
std::make_pair("bytesread", 14),
std::make_pair("byteswritten", 16),
std::make_pair("gpu_time", 18),
std::make_pair("cpu_time_billed_to_me", 20),
std::make_pair("cpu_time_billed_to_others", 22),
std::make_pair("energy", 24),
std::make_pair("logical_immediate_writes", 26),
std::make_pair("logical_deferred_writes", 28),
std::make_pair("logical_invalidated_writes", 30),
std::make_pair("logical_metadata_writes", 32),
std::make_pair("logical_immediate_writes_to_external", 34),
std::make_pair("logical_deferred_writes_to_external", 36),
std::make_pair("logical_invalidated_writes_to_external", 38),
std::make_pair("logical_metadata_writes_to_external", 40),
std::make_pair("energy_billed_to_me", 42),
std::make_pair("energy_billed_to_others", 44),
std::make_pair("cpu_ptime", 46),
std::make_pair("cpu_time_qos_default", 48),
std::make_pair("cpu_time_qos_maintenance", 50),
std::make_pair("cpu_time_qos_background", 52),
std::make_pair("cpu_time_qos_utility", 54),
std::make_pair("cpu_time_qos_legacy", 56),
std::make_pair("cpu_time_qos_user_initiated", 58),
std::make_pair("cpu_time_qos_user_interactive", 60),
std::make_pair("cpu_instructions", 62),
std::make_pair("cpu_cycles", 64),
std::make_pair("fs_metadata_writes", 66),
std::make_pair("pm_writes", 68)});
}
TEST_F(ResourceCoalitionSamplerTest, GetSample_Available_M1Timebase) {
set_expected_process_id(kTestPid);
set_coalition_id(kTestCoalitionId);
set_resource_usage(GetTestCoalitionResourceUsage(
/* multiplier=*/1 * base::Time::kSecondsPerMinute));
std::unique_ptr<ResourceCoalitionSampler> sampler(
CreateSampler(kTestPid, base::TimeTicks(), kM1Timebase));
set_resource_usage(GetTestCoalitionResourceUsage(
/* multiplier=*/2 * base::Time::kSecondsPerMinute));
Sampler::Sample sample =
sampler->GetSample(base::TimeTicks() + base::Minutes(1));
ExpectSampleMatchesArray(
sample,
{std::make_pair("tasks_started", 1),
std::make_pair("tasks_exited", 2),
std::make_pair("time_nonempty", 3),
std::make_pair("cpu_time", NsToM1Timebase(4)),
std::make_pair("interrupt_wakeups", 5),
std::make_pair("platform_idle_wakeups", 6),
std::make_pair("bytesread", 7),
std::make_pair("byteswritten", 8),
std::make_pair("gpu_time", NsToM1Timebase(9)),
std::make_pair("cpu_time_billed_to_me", NsToM1Timebase(10)),
std::make_pair("cpu_time_billed_to_others", NsToM1Timebase(11)),
std::make_pair("energy", 12),
std::make_pair("logical_immediate_writes", 13),
std::make_pair("logical_deferred_writes", 14),
std::make_pair("logical_invalidated_writes", 15),
std::make_pair("logical_metadata_writes", 16),
std::make_pair("logical_immediate_writes_to_external", 17),
std::make_pair("logical_deferred_writes_to_external", 18),
std::make_pair("logical_invalidated_writes_to_external", 19),
std::make_pair("logical_metadata_writes_to_external", 20),
std::make_pair("energy_billed_to_me", 21),
std::make_pair("energy_billed_to_others", 22),
std::make_pair("cpu_ptime", NsToM1Timebase(23)),
std::make_pair("cpu_time_qos_default", NsToM1Timebase(24)),
std::make_pair("cpu_time_qos_maintenance", NsToM1Timebase(25)),
std::make_pair("cpu_time_qos_background", NsToM1Timebase(26)),
std::make_pair("cpu_time_qos_utility", NsToM1Timebase(27)),
std::make_pair("cpu_time_qos_legacy", NsToM1Timebase(28)),
std::make_pair("cpu_time_qos_user_initiated", NsToM1Timebase(29)),
std::make_pair("cpu_time_qos_user_interactive", NsToM1Timebase(30)),
std::make_pair("cpu_instructions", 31),
std::make_pair("cpu_cycles", 32),
std::make_pair("fs_metadata_writes", 33),
std::make_pair("pm_writes", 34)});
set_resource_usage(GetTestCoalitionResourceUsage(
/* multiplier=*/4 * base::Time::kSecondsPerMinute));
sample = sampler->GetSample(base::TimeTicks() + base::Minutes(2));
ExpectSampleMatchesArray(
sample,
{std::make_pair("tasks_started", 2),
std::make_pair("tasks_exited", 4),
std::make_pair("time_nonempty", 6),
std::make_pair("cpu_time", NsToM1Timebase(8)),
std::make_pair("interrupt_wakeups", 10),
std::make_pair("platform_idle_wakeups", 12),
std::make_pair("bytesread", 14),
std::make_pair("byteswritten", 16),
std::make_pair("gpu_time", NsToM1Timebase(18)),
std::make_pair("cpu_time_billed_to_me", NsToM1Timebase(20)),
std::make_pair("cpu_time_billed_to_others", NsToM1Timebase(22)),
std::make_pair("energy", 24),
std::make_pair("logical_immediate_writes", 26),
std::make_pair("logical_deferred_writes", 28),
std::make_pair("logical_invalidated_writes", 30),
std::make_pair("logical_metadata_writes", 32),
std::make_pair("logical_immediate_writes_to_external", 34),
std::make_pair("logical_deferred_writes_to_external", 36),
std::make_pair("logical_invalidated_writes_to_external", 38),
std::make_pair("logical_metadata_writes_to_external", 40),
std::make_pair("energy_billed_to_me", 42),
std::make_pair("energy_billed_to_others", 44),
std::make_pair("cpu_ptime", NsToM1Timebase(46)),
std::make_pair("cpu_time_qos_default", NsToM1Timebase(48)),
std::make_pair("cpu_time_qos_maintenance", NsToM1Timebase(50)),
std::make_pair("cpu_time_qos_background", NsToM1Timebase(52)),
std::make_pair("cpu_time_qos_utility", NsToM1Timebase(54)),
std::make_pair("cpu_time_qos_legacy", NsToM1Timebase(56)),
std::make_pair("cpu_time_qos_user_initiated", NsToM1Timebase(58)),
std::make_pair("cpu_time_qos_user_interactive", NsToM1Timebase(60)),
std::make_pair("cpu_instructions", 62),
std::make_pair("cpu_cycles", 64),
std::make_pair("fs_metadata_writes", 66),
std::make_pair("pm_writes", 68)});
}
TEST_F(ResourceCoalitionSamplerTest, GetSample_NotAvailable) {
set_expected_process_id(kTestPid);
set_coalition_id(kTestCoalitionId);
set_resource_usage(GetTestCoalitionResourceUsage(/* multiplier=*/1));
std::unique_ptr<ResourceCoalitionSampler> sampler(
CreateSampler(kTestPid, base::TimeTicks(), kIntelTimebase));
// Previous `coalition_resource_usage` is available but not the current one.
set_resource_usage(absl::nullopt);
Sampler::Sample sample =
sampler->GetSample(base::TimeTicks() + base::Minutes(1));
EXPECT_TRUE(sample.empty());
// Current `coalition_resource_usage` is available but not the previous one.
set_resource_usage(GetTestCoalitionResourceUsage(
/* multiplier=*/2 * base::Time::kSecondsPerMinute));
sample = sampler->GetSample(base::TimeTicks() + base::Minutes(2));
EXPECT_TRUE(sample.empty());
// Both current and previous `coalition_resource_usage` are available.
set_resource_usage(GetTestCoalitionResourceUsage(
/* multiplier=*/3 * base::Time::kSecondsPerMinute));
sample = sampler->GetSample(base::TimeTicks() + base::Minutes(3));
ExpectSampleMatchesArray(
sample, {std::make_pair("tasks_started", 1),
std::make_pair("tasks_exited", 2),
std::make_pair("time_nonempty", 3),
std::make_pair("cpu_time", 4),
std::make_pair("interrupt_wakeups", 5),
std::make_pair("platform_idle_wakeups", 6),
std::make_pair("bytesread", 7),
std::make_pair("byteswritten", 8),
std::make_pair("gpu_time", 9),
std::make_pair("cpu_time_billed_to_me", 10),
std::make_pair("cpu_time_billed_to_others", 11),
std::make_pair("energy", 12),
std::make_pair("logical_immediate_writes", 13),
std::make_pair("logical_deferred_writes", 14),
std::make_pair("logical_invalidated_writes", 15),
std::make_pair("logical_metadata_writes", 16),
std::make_pair("logical_immediate_writes_to_external", 17),
std::make_pair("logical_deferred_writes_to_external", 18),
std::make_pair("logical_invalidated_writes_to_external", 19),
std::make_pair("logical_metadata_writes_to_external", 20),
std::make_pair("energy_billed_to_me", 21),
std::make_pair("energy_billed_to_others", 22),
std::make_pair("cpu_ptime", 23),
std::make_pair("cpu_time_qos_default", 24),
std::make_pair("cpu_time_qos_maintenance", 25),
std::make_pair("cpu_time_qos_background", 26),
std::make_pair("cpu_time_qos_utility", 27),
std::make_pair("cpu_time_qos_legacy", 28),
std::make_pair("cpu_time_qos_user_initiated", 29),
std::make_pair("cpu_time_qos_user_interactive", 30),
std::make_pair("cpu_instructions", 31),
std::make_pair("cpu_cycles", 32),
std::make_pair("fs_metadata_writes", 33),
std::make_pair("pm_writes", 34)});
}
} // namespace power_sampler