[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:

committed by
Chromium LUCI CQ

parent
4357c2ffb0
commit
7fd6b3bce5
@ -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
|
||||
|
101
components/power_metrics/resource_coalition_mac_unittest.mm
Normal file
101
components/power_metrics/resource_coalition_mac_unittest.mm
Normal file
@ -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",
|
||||
]
|
||||
|
3
tools/mac/power/power_sampler/DEPS
Normal file
3
tools/mac/power/power_sampler/DEPS
Normal file
@ -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));
|
||||
|
227
tools/mac/power/power_sampler/resource_coalition_sampler.cc
Normal file
227
tools/mac/power/power_sampler/resource_coalition_sampler.cc
Normal file
@ -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
|
75
tools/mac/power/power_sampler/resource_coalition_sampler.h
Normal file
75
tools/mac/power/power_sampler/resource_coalition_sampler.h
Normal file
@ -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
|
Reference in New Issue
Block a user