[battery] Move computation of coalition resource usage rate to //components/power_metrics/.
In an upcoming CL, ProcessMonitor will no longer be involved in sampling coalition resource usage. In preparation for that change, the code that computes resource usage rate from 2 coalition resource usage samples is moved out of //chrome/browser/performance_monitor/ to //components/power_metrics/. Moving the code to a standalone function also facilitates testing. This CL does not change behavior, except that it use an optional field for "energy impact" to make it possible not to report the value if it couldn't be evaluated (previously, "0" was reported). This new behavior is tested by PowerMetricsReporterUnitTest.ReportResourceCoalitionHistograms_NoEnergyImpact. Bug: 1293465 Change-Id: Ib478603a82d8c23e526336b3c44add66e2faeab3 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3433768 Reviewed-by: Patrick Monette <pmonette@chromium.org> Commit-Queue: Francois Pierre Doray <fdoray@chromium.org> Cr-Commit-Position: refs/heads/main@{#966959}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
db07987317
commit
dcdce27b4c
chrome/browser
metrics
performance_monitor
components/power_metrics
@ -371,12 +371,14 @@ void PowerMetricsReporter::ReportResourceCoalitionHistograms(
|
||||
|
||||
// EnergyImpact is reported in centi-EI, so scaled up by a factor of 100
|
||||
// for the histogram recording.
|
||||
constexpr double kEnergyImpactScalingFactor = 100.0;
|
||||
base::UmaHistogramCounts100000(
|
||||
base::StrCat({"PerformanceMonitor.ResourceCoalition.EnergyImpact",
|
||||
scenario_suffix}),
|
||||
std::roundl(metrics.coalition_data->energy_impact_per_second *
|
||||
kEnergyImpactScalingFactor));
|
||||
if (metrics.coalition_data->energy_impact_per_second.has_value()) {
|
||||
constexpr double kEnergyImpactScalingFactor = 100.0;
|
||||
base::UmaHistogramCounts100000(
|
||||
base::StrCat({"PerformanceMonitor.ResourceCoalition.EnergyImpact",
|
||||
scenario_suffix}),
|
||||
std::roundl(metrics.coalition_data->energy_impact_per_second.value() *
|
||||
kEnergyImpactScalingFactor));
|
||||
}
|
||||
|
||||
constexpr int kNanoWattToMilliWatt = 1000 * 1000;
|
||||
// Use a maximum of 100 watts, or 100 * 1000 milliwatts.
|
||||
|
@ -49,6 +49,25 @@ performance_monitor::ProcessMonitor::Metrics GetFakeProcessMetrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
power_metrics::CoalitionResourceUsageRate GetFakeResourceUsageRate() {
|
||||
power_metrics::CoalitionResourceUsageRate rate;
|
||||
rate.cpu_time_per_second = 0.1;
|
||||
rate.interrupt_wakeups_per_second = 0.3;
|
||||
rate.platform_idle_wakeups_per_second = 2;
|
||||
rate.bytesread_per_second = 10;
|
||||
rate.byteswritten_per_second = 0.1;
|
||||
rate.gpu_time_per_second = 0.8;
|
||||
rate.energy_impact_per_second = 3.0;
|
||||
rate.power_nw = 1000;
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i)
|
||||
rate.qos_time_per_second[i] = i * 0.1;
|
||||
|
||||
return rate;
|
||||
}
|
||||
#endif // BUILDFLAG(IS_MAC)
|
||||
|
||||
using UkmEntry = ukm::builders::PowerUsageScenariosIntervalData;
|
||||
|
||||
class PowerMetricsReporterAccess : public PowerMetricsReporter {
|
||||
@ -1187,20 +1206,8 @@ TEST_F(PowerMetricsReporterUnitTest, MainScreenBrightnessHistogram) {
|
||||
TEST_F(PowerMetricsReporterUnitTest, ReportResourceCoalitionHistograms) {
|
||||
base::HistogramTester histogram_tester;
|
||||
performance_monitor::ProcessMonitor::Metrics metrics;
|
||||
performance_monitor::ResourceCoalition::DataRate coalition_data = {};
|
||||
|
||||
coalition_data.cpu_time_per_second = 0.1;
|
||||
coalition_data.interrupt_wakeups_per_second = 0.3;
|
||||
coalition_data.platform_idle_wakeups_per_second = 2;
|
||||
coalition_data.bytesread_per_second = 10;
|
||||
coalition_data.byteswritten_per_second = 0.1;
|
||||
coalition_data.gpu_time_per_second = 0.8;
|
||||
coalition_data.energy_impact_per_second = 3.0;
|
||||
coalition_data.power_nw = 1000;
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i)
|
||||
coalition_data.qos_time_per_second[i] = i * 0.1;
|
||||
|
||||
power_metrics::CoalitionResourceUsageRate coalition_data =
|
||||
GetFakeResourceUsageRate();
|
||||
metrics.coalition_data = coalition_data;
|
||||
|
||||
std::vector<const char*> suffixes = {"", ".Foo", ".Bar"};
|
||||
@ -1243,7 +1250,7 @@ TEST_F(PowerMetricsReporterUnitTest, ReportResourceCoalitionHistograms) {
|
||||
histogram_tester.ExpectUniqueSample(
|
||||
base::StrCat({"PerformanceMonitor.ResourceCoalition.EnergyImpact",
|
||||
scenario_suffix}),
|
||||
coalition_data.energy_impact_per_second * 100.0, 1);
|
||||
coalition_data.energy_impact_per_second.value() * 100.0, 1);
|
||||
|
||||
// Power is reported in milliwatts (mj/s), the data is in nj/s so it has to
|
||||
// be divided by 1000000.
|
||||
@ -1288,4 +1295,27 @@ TEST_F(PowerMetricsReporterUnitTest, ReportResourceCoalitionHistograms) {
|
||||
coalition_data.qos_time_per_second[6] * 10000, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that no energy impact histogram is reported when
|
||||
// `CoalitionResourceUsageRate::energy_impact_per_second` is nullopt.
|
||||
TEST_F(PowerMetricsReporterUnitTest,
|
||||
ReportResourceCoalitionHistograms_NoEnergyImpact) {
|
||||
base::HistogramTester histogram_tester;
|
||||
performance_monitor::ProcessMonitor::Metrics metrics;
|
||||
power_metrics::CoalitionResourceUsageRate coalition_data =
|
||||
GetFakeResourceUsageRate();
|
||||
|
||||
coalition_data.energy_impact_per_second.reset();
|
||||
|
||||
metrics.coalition_data = coalition_data;
|
||||
|
||||
std::vector<const char*> suffixes = {"", ".Foo"};
|
||||
PowerMetricsReporterAccess::ReportResourceCoalitionHistograms(metrics,
|
||||
suffixes);
|
||||
|
||||
histogram_tester.ExpectTotalCount(
|
||||
"PerformanceMonitor.ResourceCoalition.EnergyImpact", 0);
|
||||
histogram_tester.ExpectTotalCount(
|
||||
"PerformanceMonitor.ResourceCoalition.EnergyImpact.Foo", 0);
|
||||
}
|
||||
#endif // BUILDFLAG(IS_MAC)
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#if BUILDFLAG(IS_MAC)
|
||||
#include "chrome/browser/performance_monitor/resource_coalition_mac.h"
|
||||
#include "components/power_metrics/resource_coalition_mac.h"
|
||||
#endif
|
||||
|
||||
namespace performance_monitor {
|
||||
@ -75,7 +76,7 @@ class ProcessMonitor {
|
||||
// Process coalition data. Only available for aggregated metrics (not
|
||||
// individual processes), on some Mac devices. absl::nullopt if not
|
||||
// available.
|
||||
absl::optional<ResourceCoalition::DataRate> coalition_data;
|
||||
absl::optional<power_metrics::CoalitionResourceUsageRate> coalition_data;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -35,29 +35,6 @@ namespace performance_monitor {
|
||||
// data retrieved by this class is experimental only.
|
||||
class ResourceCoalition {
|
||||
public:
|
||||
// The data tracked by the coalition.
|
||||
// TODO(sebmarchand): This is only a subset of the available data, we should
|
||||
// probably record more data.
|
||||
struct DataRate {
|
||||
DataRate();
|
||||
DataRate(const DataRate& other);
|
||||
DataRate& operator=(const DataRate& other);
|
||||
~DataRate();
|
||||
|
||||
double cpu_time_per_second;
|
||||
double interrupt_wakeups_per_second;
|
||||
double platform_idle_wakeups_per_second;
|
||||
double bytesread_per_second;
|
||||
double byteswritten_per_second;
|
||||
double gpu_time_per_second;
|
||||
// Only makes sense on Intel macs, not computed on M1 macs.
|
||||
double energy_impact_per_second;
|
||||
// Only available on M1 macs as of September 2021.
|
||||
double power_nw;
|
||||
|
||||
double qos_time_per_second[COALITION_NUM_THREAD_QOS_TYPES];
|
||||
};
|
||||
|
||||
// Note: The constructor will record whether or not coalition data are
|
||||
// available to UMA.
|
||||
ResourceCoalition();
|
||||
@ -72,14 +49,15 @@ class ResourceCoalition {
|
||||
// or since the creation of this object, returns nullopt if not available
|
||||
// or if one of the data counter has overflowed.
|
||||
// This should only be called if |IsAvailable| returns true.
|
||||
absl::optional<DataRate> GetDataRate();
|
||||
absl::optional<power_metrics::CoalitionResourceUsageRate> GetDataRate();
|
||||
|
||||
protected:
|
||||
void SetCoalitionIDToCurrentProcessIdForTesting();
|
||||
|
||||
// Compute the data change rate between |old_data_sample| and
|
||||
// |recent_data_sample| over an interval of length |interval_length|.
|
||||
absl::optional<DataRate> GetDataRateFromFakeDataForTesting(
|
||||
absl::optional<power_metrics::CoalitionResourceUsageRate>
|
||||
GetDataRateFromFakeDataForTesting(
|
||||
std::unique_ptr<coalition_resource_usage> old_data_sample,
|
||||
std::unique_ptr<coalition_resource_usage> recent_data_sample,
|
||||
base::TimeDelta interval_length);
|
||||
@ -96,17 +74,8 @@ class ResourceCoalition {
|
||||
private:
|
||||
void SetCoalitionId(absl::optional<uint64_t> coalition_id);
|
||||
|
||||
// Computes the diff between two coalition_resource_usage objects and stores
|
||||
// the per-second change rate for each field in a ResourceCoalition::Data
|
||||
// object that will then be returned. Returns nullopt if any of the samples
|
||||
// has overflowed.
|
||||
absl::optional<DataRate> GetCoalitionDataDiff(
|
||||
const coalition_resource_usage& new_sample,
|
||||
const coalition_resource_usage& old_sample,
|
||||
base::TimeDelta interval_length);
|
||||
|
||||
// Implementation details for GetDataRate.
|
||||
absl::optional<DataRate> GetDataRateImpl(
|
||||
absl::optional<power_metrics::CoalitionResourceUsageRate> GetDataRateImpl(
|
||||
std::unique_ptr<coalition_resource_usage> new_data_sample,
|
||||
base::TimeTicks now);
|
||||
|
||||
|
@ -80,12 +80,6 @@ absl::optional<uint64_t> GetCurrentCoalitionId(
|
||||
|
||||
} // namespace
|
||||
|
||||
ResourceCoalition::DataRate::DataRate() = default;
|
||||
ResourceCoalition::DataRate::DataRate(const DataRate& other) = default;
|
||||
ResourceCoalition::DataRate& ResourceCoalition::DataRate::operator=(
|
||||
const DataRate& other) = default;
|
||||
ResourceCoalition::DataRate::~DataRate() = default;
|
||||
|
||||
ResourceCoalition::ResourceCoalition()
|
||||
: mach_timebase_(power_metrics::GetSystemMachTimeBase()),
|
||||
energy_impact_coefficients_(
|
||||
@ -98,7 +92,8 @@ ResourceCoalition::ResourceCoalition()
|
||||
|
||||
ResourceCoalition::~ResourceCoalition() = default;
|
||||
|
||||
absl::optional<ResourceCoalition::DataRate> ResourceCoalition::GetDataRate() {
|
||||
absl::optional<power_metrics::CoalitionResourceUsageRate>
|
||||
ResourceCoalition::GetDataRate() {
|
||||
DCHECK(IsAvailable());
|
||||
DCHECK_EQ(
|
||||
power_metrics::GetProcessCoalitionId(base::GetCurrentProcId()).value(),
|
||||
@ -109,7 +104,7 @@ absl::optional<ResourceCoalition::DataRate> ResourceCoalition::GetDataRate() {
|
||||
base::TimeTicks::Now());
|
||||
}
|
||||
|
||||
absl::optional<ResourceCoalition::DataRate>
|
||||
absl::optional<power_metrics::CoalitionResourceUsageRate>
|
||||
ResourceCoalition::GetDataRateFromFakeDataForTesting(
|
||||
std::unique_ptr<coalition_resource_usage> old_data_sample,
|
||||
std::unique_ptr<coalition_resource_usage> recent_data_sample,
|
||||
@ -145,93 +140,14 @@ void ResourceCoalition::SetCoalitionId(absl::optional<uint64_t> coalition_id) {
|
||||
}
|
||||
}
|
||||
|
||||
absl::optional<ResourceCoalition::DataRate>
|
||||
ResourceCoalition::GetCoalitionDataDiff(
|
||||
const coalition_resource_usage& new_sample,
|
||||
const coalition_resource_usage& old_sample,
|
||||
base::TimeDelta interval_length) {
|
||||
bool new_samples_exceeds_or_equals_old_ones =
|
||||
std::tie(new_sample.cpu_time, new_sample.interrupt_wakeups,
|
||||
new_sample.platform_idle_wakeups, new_sample.bytesread,
|
||||
new_sample.byteswritten, new_sample.gpu_time,
|
||||
new_sample.energy) >=
|
||||
std::tie(old_sample.cpu_time, old_sample.interrupt_wakeups,
|
||||
old_sample.platform_idle_wakeups, old_sample.bytesread,
|
||||
old_sample.byteswritten, old_sample.gpu_time, old_sample.energy);
|
||||
DCHECK_EQ(new_sample.cpu_time_eqos_len,
|
||||
static_cast<uint64_t>(COALITION_NUM_THREAD_QOS_TYPES));
|
||||
// Check for an overflow in the QoS data.
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
if (new_sample.cpu_time_eqos[i] < old_sample.cpu_time_eqos[i]) {
|
||||
new_samples_exceeds_or_equals_old_ones = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!new_samples_exceeds_or_equals_old_ones)
|
||||
return absl::nullopt;
|
||||
|
||||
ResourceCoalition::DataRate ret;
|
||||
|
||||
auto get_rate_per_second = [&interval_length](uint64_t new_sample,
|
||||
uint64_t old_sample) -> double {
|
||||
DCHECK_GE(new_sample, old_sample);
|
||||
uint64_t diff = new_sample - old_sample;
|
||||
return diff / interval_length.InSecondsF();
|
||||
};
|
||||
|
||||
auto get_timedelta_rate_per_second = [&interval_length, self = this](
|
||||
uint64_t new_sample,
|
||||
uint64_t old_sample) -> double {
|
||||
DCHECK_GE(new_sample, old_sample);
|
||||
// Compute the delta in s, being careful to avoid truncation due to integral
|
||||
// division.
|
||||
double delta_sample_s =
|
||||
power_metrics::MachTimeToNs(new_sample - old_sample,
|
||||
self->mach_timebase_) /
|
||||
static_cast<double>(base::Time::kNanosecondsPerSecond);
|
||||
return delta_sample_s / interval_length.InSecondsF();
|
||||
};
|
||||
|
||||
ret.cpu_time_per_second =
|
||||
get_timedelta_rate_per_second(new_sample.cpu_time, old_sample.cpu_time);
|
||||
ret.interrupt_wakeups_per_second = get_rate_per_second(
|
||||
new_sample.interrupt_wakeups, old_sample.interrupt_wakeups);
|
||||
ret.platform_idle_wakeups_per_second = get_rate_per_second(
|
||||
new_sample.platform_idle_wakeups, old_sample.platform_idle_wakeups);
|
||||
ret.bytesread_per_second =
|
||||
get_rate_per_second(new_sample.bytesread, old_sample.bytesread);
|
||||
ret.byteswritten_per_second =
|
||||
get_rate_per_second(new_sample.byteswritten, old_sample.byteswritten);
|
||||
ret.gpu_time_per_second =
|
||||
get_timedelta_rate_per_second(new_sample.gpu_time, old_sample.gpu_time);
|
||||
ret.power_nw = get_rate_per_second(new_sample.energy, old_sample.energy);
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
ret.qos_time_per_second[i] = get_timedelta_rate_per_second(
|
||||
new_sample.cpu_time_eqos[i], old_sample.cpu_time_eqos[i]);
|
||||
}
|
||||
|
||||
if (energy_impact_coefficients_.has_value()) {
|
||||
ret.energy_impact_per_second =
|
||||
(power_metrics::ComputeEnergyImpactForResourceUsage(
|
||||
new_sample, energy_impact_coefficients_.value(), mach_timebase_) -
|
||||
power_metrics::ComputeEnergyImpactForResourceUsage(
|
||||
old_sample, energy_impact_coefficients_.value(), mach_timebase_)) /
|
||||
interval_length.InSecondsF();
|
||||
} else {
|
||||
// TODO(siggi): Use something else here as sentinel?
|
||||
ret.energy_impact_per_second = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
absl::optional<ResourceCoalition::DataRate> ResourceCoalition::GetDataRateImpl(
|
||||
absl::optional<power_metrics::CoalitionResourceUsageRate>
|
||||
ResourceCoalition::GetDataRateImpl(
|
||||
std::unique_ptr<coalition_resource_usage> new_data_sample,
|
||||
base::TimeTicks now) {
|
||||
auto ret =
|
||||
GetCoalitionDataDiff(*new_data_sample.get(), *last_data_sample_.get(),
|
||||
now - last_data_sample_timestamp_);
|
||||
auto ret = power_metrics::GetCoalitionResourceUsageRate(
|
||||
*last_data_sample_, *new_data_sample, now - last_data_sample_timestamp_,
|
||||
mach_timebase_, energy_impact_coefficients_);
|
||||
|
||||
last_data_sample_.swap(new_data_sample);
|
||||
last_data_sample_timestamp_ = now;
|
||||
|
||||
|
@ -27,11 +27,6 @@ namespace {
|
||||
|
||||
constexpr mach_timebase_info_data_t kIntelTimebase = {1, 1};
|
||||
|
||||
// A sample (not definitive) timebase for M1.
|
||||
constexpr mach_timebase_info_data_t kM1Timebase = {125, 3};
|
||||
|
||||
using EnergyImpactCoefficients = power_metrics::EnergyImpactCoefficients;
|
||||
|
||||
// Initializes to no EI constants and the Intel timebase.
|
||||
class TestResourceCoalition : public ResourceCoalition {
|
||||
public:
|
||||
@ -46,22 +41,7 @@ class TestResourceCoalition : public ResourceCoalition {
|
||||
using ResourceCoalition::SetMachTimebaseForTesting;
|
||||
};
|
||||
|
||||
EnergyImpactCoefficients GetEnergyImpactTestCoefficients() {
|
||||
constexpr EnergyImpactCoefficients coefficients{
|
||||
.kcpu_wakeups = base::Microseconds(200).InSecondsF(),
|
||||
.kqos_default = 1.0,
|
||||
.kqos_background = 0.8,
|
||||
.kqos_utility = 1.0,
|
||||
.kqos_legacy = 1.0,
|
||||
.kqos_user_initiated = 1.0,
|
||||
.kqos_user_interactive = 1.0,
|
||||
.kgpu_time = 2.5,
|
||||
};
|
||||
|
||||
return coefficients;
|
||||
}
|
||||
|
||||
constexpr base::TimeDelta kIntervalLength = base::Seconds(2.5);
|
||||
} // namespace
|
||||
|
||||
TEST(ResourceCoalitionTests, Basics) {
|
||||
base::HistogramTester histogram_tester;
|
||||
@ -91,330 +71,4 @@ TEST(ResourceCoalitionTests, Basics) {
|
||||
EXPECT_NE(sample->cpu_time_per_second, 0);
|
||||
}
|
||||
|
||||
constexpr double kExpectedCPUUsagePerSecondPercent = 0.7;
|
||||
constexpr double kExpectedGPUUsagePerSecondPercent = 0.3;
|
||||
// Note: The following counters must have an integral value once multiplied by
|
||||
// the interval length in seconds (2.5).
|
||||
constexpr double kExpectedInterruptWakeUpPerSecond = 0.4;
|
||||
constexpr double kExpectedPlatformIdleWakeUpPerSecond = 10;
|
||||
constexpr double kExpectedBytesReadPerSecond = 0.8;
|
||||
constexpr double kExpectedBytesWrittenPerSecond = 1.6;
|
||||
constexpr double kExpectedPowerNW = 10000.0;
|
||||
// This number will be multiplied by the int value associated with a QoS level
|
||||
// to compute the expected time spent in this QoS level. E.g.
|
||||
// |QoSLevels::kUtility == 3| so the time spent in the utility QoS state will
|
||||
// be set to 3 * 0.1 = 30%.
|
||||
constexpr double kExpectedQoSTimeBucketIdMultiplier = 0.1;
|
||||
|
||||
// Scales a time given in ns to mach_time in |timebase|.
|
||||
uint64_t NsScaleToTimebase(const mach_timebase_info_data_t& timebase,
|
||||
int64_t time_ns) {
|
||||
return time_ns * timebase.denom / timebase.numer;
|
||||
}
|
||||
|
||||
// Returns test data with all time quantities scaled to the given time base.
|
||||
std::unique_ptr<coalition_resource_usage> GetCoalitionResourceUsageTestData(
|
||||
const mach_timebase_info_data_t& timebase) {
|
||||
std::unique_ptr<coalition_resource_usage> test_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
|
||||
// Scales a time given in ns to mach_time in |timebase|.
|
||||
auto scale_to_timebase = [&timebase](double time_ns) -> int64_t {
|
||||
return NsScaleToTimebase(timebase, time_ns);
|
||||
};
|
||||
|
||||
test_data->cpu_time = scale_to_timebase(kExpectedCPUUsagePerSecondPercent *
|
||||
kIntervalLength.InNanoseconds());
|
||||
test_data->interrupt_wakeups =
|
||||
kExpectedInterruptWakeUpPerSecond * kIntervalLength.InSecondsF();
|
||||
test_data->platform_idle_wakeups =
|
||||
kExpectedPlatformIdleWakeUpPerSecond * kIntervalLength.InSecondsF();
|
||||
test_data->bytesread =
|
||||
kExpectedBytesReadPerSecond * kIntervalLength.InSecondsF();
|
||||
test_data->byteswritten =
|
||||
kExpectedBytesWrittenPerSecond * kIntervalLength.InSecondsF();
|
||||
test_data->gpu_time = scale_to_timebase(kExpectedGPUUsagePerSecondPercent *
|
||||
kIntervalLength.InNanoseconds());
|
||||
test_data->energy = kExpectedPowerNW * kIntervalLength.InSecondsF();
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
test_data->cpu_time_eqos[i] =
|
||||
scale_to_timebase(i * kExpectedQoSTimeBucketIdMultiplier *
|
||||
kIntervalLength.InNanoseconds());
|
||||
}
|
||||
test_data->cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES;
|
||||
|
||||
return test_data;
|
||||
}
|
||||
|
||||
TEST(ResourceCoalitionTests, GetDataRate_NoEnergyImpact_Intel) {
|
||||
TestResourceCoalition coalition;
|
||||
coalition.SetCoalitionIDToCurrentProcessIdForTesting();
|
||||
|
||||
EXPECT_TRUE(coalition.IsAvailable());
|
||||
|
||||
// Keep the initial data zero initialized.
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
GetCoalitionResourceUsageTestData(kIntelTimebase);
|
||||
|
||||
auto data_rate = coalition.GetDataRateFromFakeDataForTesting(
|
||||
std::move(t0_data), std::move(t1_data), kIntervalLength);
|
||||
ASSERT_TRUE(data_rate);
|
||||
EXPECT_EQ(kExpectedCPUUsagePerSecondPercent, data_rate->cpu_time_per_second);
|
||||
EXPECT_EQ(kExpectedInterruptWakeUpPerSecond,
|
||||
data_rate->interrupt_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedPlatformIdleWakeUpPerSecond,
|
||||
data_rate->platform_idle_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedBytesReadPerSecond, data_rate->bytesread_per_second);
|
||||
EXPECT_EQ(kExpectedBytesWrittenPerSecond, data_rate->byteswritten_per_second);
|
||||
EXPECT_EQ(kExpectedGPUUsagePerSecondPercent, data_rate->gpu_time_per_second);
|
||||
EXPECT_EQ(0, data_rate->energy_impact_per_second);
|
||||
EXPECT_EQ(kExpectedPowerNW, data_rate->power_nw);
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
EXPECT_DOUBLE_EQ(i * kExpectedQoSTimeBucketIdMultiplier,
|
||||
data_rate->qos_time_per_second[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ResourceCoalitionTests, GetDataRate_NoEnergyImpact_M1) {
|
||||
TestResourceCoalition coalition;
|
||||
coalition.SetCoalitionIDToCurrentProcessIdForTesting();
|
||||
coalition.SetMachTimebaseForTesting(kM1Timebase);
|
||||
|
||||
EXPECT_TRUE(coalition.IsAvailable());
|
||||
|
||||
// Keep the initial data zero initialized.
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
GetCoalitionResourceUsageTestData(kM1Timebase);
|
||||
|
||||
auto data_rate = coalition.GetDataRateFromFakeDataForTesting(
|
||||
std::move(t0_data), std::move(t1_data), kIntervalLength);
|
||||
ASSERT_TRUE(data_rate);
|
||||
EXPECT_DOUBLE_EQ(kExpectedCPUUsagePerSecondPercent,
|
||||
data_rate->cpu_time_per_second);
|
||||
EXPECT_DOUBLE_EQ(kExpectedInterruptWakeUpPerSecond,
|
||||
data_rate->interrupt_wakeups_per_second);
|
||||
EXPECT_DOUBLE_EQ(kExpectedPlatformIdleWakeUpPerSecond,
|
||||
data_rate->platform_idle_wakeups_per_second);
|
||||
EXPECT_DOUBLE_EQ(kExpectedBytesReadPerSecond,
|
||||
data_rate->bytesread_per_second);
|
||||
EXPECT_DOUBLE_EQ(kExpectedBytesWrittenPerSecond,
|
||||
data_rate->byteswritten_per_second);
|
||||
EXPECT_DOUBLE_EQ(kExpectedGPUUsagePerSecondPercent,
|
||||
data_rate->gpu_time_per_second);
|
||||
EXPECT_DOUBLE_EQ(0, data_rate->energy_impact_per_second);
|
||||
EXPECT_DOUBLE_EQ(kExpectedPowerNW, data_rate->power_nw);
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
EXPECT_DOUBLE_EQ(i * kExpectedQoSTimeBucketIdMultiplier,
|
||||
data_rate->qos_time_per_second[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ResourceCoalitionTests, GetDataRate_WithEnergyImpact_Intel) {
|
||||
TestResourceCoalition coalition;
|
||||
coalition.SetCoalitionIDToCurrentProcessIdForTesting();
|
||||
coalition.SetEnergyImpactCoefficientsForTesting(
|
||||
GetEnergyImpactTestCoefficients());
|
||||
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
GetCoalitionResourceUsageTestData(kIntelTimebase);
|
||||
|
||||
auto ei_data_rate = coalition.GetDataRateFromFakeDataForTesting(
|
||||
std::move(t0_data), std::move(t1_data), kIntervalLength);
|
||||
ASSERT_TRUE(ei_data_rate);
|
||||
EXPECT_EQ(kExpectedCPUUsagePerSecondPercent,
|
||||
ei_data_rate->cpu_time_per_second);
|
||||
EXPECT_EQ(kExpectedInterruptWakeUpPerSecond,
|
||||
ei_data_rate->interrupt_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedPlatformIdleWakeUpPerSecond,
|
||||
ei_data_rate->platform_idle_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedBytesReadPerSecond, ei_data_rate->bytesread_per_second);
|
||||
EXPECT_EQ(kExpectedBytesWrittenPerSecond,
|
||||
ei_data_rate->byteswritten_per_second);
|
||||
EXPECT_EQ(kExpectedGPUUsagePerSecondPercent,
|
||||
ei_data_rate->gpu_time_per_second);
|
||||
EXPECT_EQ(271.2, ei_data_rate->energy_impact_per_second);
|
||||
EXPECT_FLOAT_EQ(kExpectedPowerNW, ei_data_rate->power_nw);
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
EXPECT_DOUBLE_EQ(i * kExpectedQoSTimeBucketIdMultiplier,
|
||||
ei_data_rate->qos_time_per_second[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ResourceCoalitionTests, GetDataRate_WithEnergyImpact_M1) {
|
||||
TestResourceCoalition coalition;
|
||||
coalition.SetCoalitionIDToCurrentProcessIdForTesting();
|
||||
coalition.SetEnergyImpactCoefficientsForTesting(
|
||||
GetEnergyImpactTestCoefficients());
|
||||
coalition.SetMachTimebaseForTesting(kM1Timebase);
|
||||
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
GetCoalitionResourceUsageTestData(kM1Timebase);
|
||||
|
||||
auto ei_data_rate = coalition.GetDataRateFromFakeDataForTesting(
|
||||
std::move(t0_data), std::move(t1_data), kIntervalLength);
|
||||
ASSERT_TRUE(ei_data_rate);
|
||||
EXPECT_EQ(kExpectedCPUUsagePerSecondPercent,
|
||||
ei_data_rate->cpu_time_per_second);
|
||||
EXPECT_EQ(kExpectedInterruptWakeUpPerSecond,
|
||||
ei_data_rate->interrupt_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedPlatformIdleWakeUpPerSecond,
|
||||
ei_data_rate->platform_idle_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedBytesReadPerSecond, ei_data_rate->bytesread_per_second);
|
||||
EXPECT_EQ(kExpectedBytesWrittenPerSecond,
|
||||
ei_data_rate->byteswritten_per_second);
|
||||
EXPECT_EQ(kExpectedGPUUsagePerSecondPercent,
|
||||
ei_data_rate->gpu_time_per_second);
|
||||
EXPECT_EQ(271.2, ei_data_rate->energy_impact_per_second);
|
||||
EXPECT_FLOAT_EQ(kExpectedPowerNW, ei_data_rate->power_nw);
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
EXPECT_DOUBLE_EQ(i * kExpectedQoSTimeBucketIdMultiplier,
|
||||
ei_data_rate->qos_time_per_second[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool DataOverflowInvalidatesDiffImpl(
|
||||
TestResourceCoalition& coalition,
|
||||
std::unique_ptr<coalition_resource_usage> t0,
|
||||
std::unique_ptr<coalition_resource_usage> t1,
|
||||
uint64_t* field_to_overflow) {
|
||||
// Initialize all fields to a non zero value.
|
||||
::memset(t0.get(), 1000, sizeof(coalition_resource_usage));
|
||||
::memset(t1.get(), 1000, sizeof(coalition_resource_usage));
|
||||
*field_to_overflow = 0;
|
||||
|
||||
t1->cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES;
|
||||
return !coalition
|
||||
.GetDataRateFromFakeDataForTesting(std::move(t0), std::move(t1),
|
||||
kIntervalLength)
|
||||
.has_value();
|
||||
}
|
||||
|
||||
bool DataOverflowInvalidatesDiff(
|
||||
TestResourceCoalition& coalition,
|
||||
uint64_t coalition_resource_usage::*member_ptr) {
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
auto* ptr = &(t1_data.get()->*member_ptr);
|
||||
return DataOverflowInvalidatesDiffImpl(coalition, std::move(t0_data),
|
||||
std::move(t1_data), ptr);
|
||||
}
|
||||
|
||||
bool DataOverflowInvalidatesDiff(
|
||||
TestResourceCoalition& coalition,
|
||||
uint64_t (
|
||||
coalition_resource_usage::*member_ptr)[COALITION_NUM_THREAD_QOS_TYPES],
|
||||
int index_to_check) {
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
auto* ptr = &(t1_data.get()->*member_ptr)[index_to_check];
|
||||
return DataOverflowInvalidatesDiffImpl(coalition, std::move(t0_data),
|
||||
std::move(t1_data), ptr);
|
||||
}
|
||||
|
||||
TEST(ResourceCoalitionTests, Overflows) {
|
||||
TestResourceCoalition coalition;
|
||||
coalition.SetCoalitionIDToCurrentProcessIdForTesting();
|
||||
|
||||
EXPECT_TRUE(coalition.IsAvailable());
|
||||
|
||||
// Keep the initial data zero initialized.
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
|
||||
{
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
t1_data->cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES;
|
||||
auto data_rate = coalition.GetDataRateFromFakeDataForTesting(
|
||||
std::move(t0_data), std::move(t1_data), kIntervalLength);
|
||||
// It's valid for the data from the 2 samples to be equal to one another.
|
||||
EXPECT_TRUE(data_rate);
|
||||
}
|
||||
|
||||
// If one of these tests fails then it means that overflows on a newly tracked
|
||||
// coalition field aren't tracked properly in GetCoalitionDataDiff.
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::energy_billed_to_me));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::tasks_started));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::tasks_exited));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::time_nonempty));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(coalition,
|
||||
&coalition_resource_usage::cpu_time));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::interrupt_wakeups));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::platform_idle_wakeups));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::bytesread));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::byteswritten));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(coalition,
|
||||
&coalition_resource_usage::gpu_time));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::cpu_time_billed_to_me));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::cpu_time_billed_to_others));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(coalition,
|
||||
&coalition_resource_usage::energy));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::logical_immediate_writes));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::logical_deferred_writes));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::logical_invalidated_writes));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::logical_metadata_writes));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition,
|
||||
&coalition_resource_usage::logical_immediate_writes_to_external));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition,
|
||||
&coalition_resource_usage::logical_deferred_writes_to_external));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition,
|
||||
&coalition_resource_usage::logical_invalidated_writes_to_external));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition,
|
||||
&coalition_resource_usage::logical_metadata_writes_to_external));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::energy_billed_to_me));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::energy_billed_to_others));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::cpu_ptime));
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::cpu_time_eqos, i));
|
||||
}
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::cpu_instructions));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::cpu_cycles));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::fs_metadata_writes));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
coalition, &coalition_resource_usage::pm_writes));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace performance_monitor
|
||||
|
@ -14,12 +14,17 @@
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
#include "base/process/process_handle.h"
|
||||
#include "base/time/time.h"
|
||||
#include "components/power_metrics/resource_coalition_internal_types_mac.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
namespace power_metrics {
|
||||
|
||||
struct EnergyImpactCoefficients;
|
||||
|
||||
// Returns the coalition id for the process identified by |pid| or nullopt if
|
||||
// not available.
|
||||
absl::optional<uint64_t> GetProcessCoalitionId(base::ProcessId pid);
|
||||
@ -35,6 +40,31 @@ coalition_resource_usage GetCoalitionResourceUsageDifference(
|
||||
const coalition_resource_usage& left,
|
||||
const coalition_resource_usage& right);
|
||||
|
||||
// Struct that contains the rate of resource usage for a coalition.
|
||||
struct CoalitionResourceUsageRate {
|
||||
double cpu_time_per_second;
|
||||
double interrupt_wakeups_per_second;
|
||||
double platform_idle_wakeups_per_second;
|
||||
double bytesread_per_second;
|
||||
double byteswritten_per_second;
|
||||
double gpu_time_per_second;
|
||||
// Only makes sense on Intel macs, not computed on M1 macs.
|
||||
absl::optional<double> energy_impact_per_second;
|
||||
// Only available on M1 macs as of September 2021.
|
||||
double power_nw;
|
||||
|
||||
double qos_time_per_second[THREAD_QOS_LAST];
|
||||
};
|
||||
|
||||
// Returns rate of resource usage for a coalition, given the usage at
|
||||
// the beginning and end of an interval and the duration of the interval.
|
||||
absl::optional<CoalitionResourceUsageRate> GetCoalitionResourceUsageRate(
|
||||
const coalition_resource_usage& begin,
|
||||
const coalition_resource_usage& end,
|
||||
base::TimeDelta interval_duration,
|
||||
mach_timebase_info_data_t timebase,
|
||||
absl::optional<EnergyImpactCoefficients> energy_impact_coefficients);
|
||||
|
||||
} // namespace power_metrics
|
||||
|
||||
#endif // COMPONENTS_PPOWER_METRICS_RESOURCE_COALITION_MAC_H_
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include <libproc.h>
|
||||
|
||||
#include "base/check_op.h"
|
||||
#include "components/power_metrics/energy_impact_mac.h"
|
||||
#include "components/power_metrics/mach_time_mac.h"
|
||||
|
||||
extern "C" int coalition_info_resource_usage(
|
||||
uint64_t cid,
|
||||
@ -133,4 +135,75 @@ coalition_resource_usage GetCoalitionResourceUsageDifference(
|
||||
return ret;
|
||||
}
|
||||
|
||||
absl::optional<CoalitionResourceUsageRate> GetCoalitionResourceUsageRate(
|
||||
const coalition_resource_usage& begin,
|
||||
const coalition_resource_usage& end,
|
||||
base::TimeDelta interval_duration,
|
||||
mach_timebase_info_data_t timebase,
|
||||
absl::optional<EnergyImpactCoefficients> energy_impact_coefficients) {
|
||||
// Validate that |end| >= |begin|.
|
||||
bool end_greater_or_equal_begin =
|
||||
std::tie(end.cpu_time, end.interrupt_wakeups, end.platform_idle_wakeups,
|
||||
end.bytesread, end.byteswritten, end.gpu_time, end.energy) >=
|
||||
std::tie(begin.cpu_time, begin.interrupt_wakeups,
|
||||
begin.platform_idle_wakeups, begin.bytesread, begin.byteswritten,
|
||||
begin.gpu_time, begin.energy);
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
if (end.cpu_time_eqos[i] < begin.cpu_time_eqos[i])
|
||||
end_greater_or_equal_begin = false;
|
||||
}
|
||||
if (!end_greater_or_equal_begin)
|
||||
return absl::nullopt;
|
||||
|
||||
auto get_rate_per_second = [&interval_duration](uint64_t begin,
|
||||
uint64_t end) -> double {
|
||||
DCHECK_GE(end, begin);
|
||||
uint64_t diff = end - begin;
|
||||
return diff / interval_duration.InSecondsF();
|
||||
};
|
||||
|
||||
auto get_time_rate_per_second = [&interval_duration, &timebase](
|
||||
uint64_t begin, uint64_t end) -> double {
|
||||
DCHECK_GE(end, begin);
|
||||
// Compute the delta in s, being careful to avoid truncation due to integral
|
||||
// division.
|
||||
double delta_sample_s =
|
||||
power_metrics::MachTimeToNs(end - begin, timebase) /
|
||||
static_cast<double>(base::Time::kNanosecondsPerSecond);
|
||||
return delta_sample_s / interval_duration.InSecondsF();
|
||||
};
|
||||
|
||||
CoalitionResourceUsageRate result;
|
||||
|
||||
result.cpu_time_per_second =
|
||||
get_time_rate_per_second(begin.cpu_time, end.cpu_time);
|
||||
result.interrupt_wakeups_per_second =
|
||||
get_rate_per_second(begin.interrupt_wakeups, end.interrupt_wakeups);
|
||||
result.platform_idle_wakeups_per_second = get_rate_per_second(
|
||||
begin.platform_idle_wakeups, end.platform_idle_wakeups);
|
||||
result.bytesread_per_second =
|
||||
get_rate_per_second(begin.bytesread, end.bytesread);
|
||||
result.byteswritten_per_second =
|
||||
get_rate_per_second(begin.byteswritten, end.byteswritten);
|
||||
result.gpu_time_per_second =
|
||||
get_time_rate_per_second(begin.gpu_time, end.gpu_time);
|
||||
result.power_nw = get_rate_per_second(begin.energy, end.energy);
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
result.qos_time_per_second[i] =
|
||||
get_time_rate_per_second(begin.cpu_time_eqos[i], end.cpu_time_eqos[i]);
|
||||
}
|
||||
|
||||
if (energy_impact_coefficients.has_value()) {
|
||||
result.energy_impact_per_second =
|
||||
(ComputeEnergyImpactForResourceUsage(
|
||||
end, energy_impact_coefficients.value(), timebase) -
|
||||
ComputeEnergyImpactForResourceUsage(
|
||||
begin, energy_impact_coefficients.value(), timebase)) /
|
||||
interval_duration.InSecondsF();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // power_metrics
|
||||
|
@ -4,12 +4,28 @@
|
||||
|
||||
#include "components/power_metrics/resource_coalition_mac.h"
|
||||
|
||||
#include "components/power_metrics/energy_impact_mac.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
|
||||
namespace power_metrics {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr mach_timebase_info_data_t kIntelTimebase = {1, 1};
|
||||
constexpr mach_timebase_info_data_t kM1Timebase = {125, 3};
|
||||
|
||||
constexpr EnergyImpactCoefficients kEnergyImpactCoefficients{
|
||||
.kcpu_wakeups = base::Microseconds(200).InSecondsF(),
|
||||
.kqos_default = 1.0,
|
||||
.kqos_background = 0.8,
|
||||
.kqos_utility = 1.0,
|
||||
.kqos_legacy = 1.0,
|
||||
.kqos_user_initiated = 1.0,
|
||||
.kqos_user_interactive = 1.0,
|
||||
.kgpu_time = 2.5,
|
||||
};
|
||||
|
||||
coalition_resource_usage GetTestCoalitionResourceUsage(uint32_t increment) {
|
||||
coalition_resource_usage ret{
|
||||
.tasks_started = 1 + increment,
|
||||
@ -98,4 +114,282 @@ TEST(ResourceCoalitionMacTest, Difference) {
|
||||
EXPECT_EQ(diff.pm_writes, 1U);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr base::TimeDelta kIntervalDuration = base::Seconds(2.5);
|
||||
|
||||
constexpr double kExpectedCPUUsagePerSecondPercent = 0.7;
|
||||
constexpr double kExpectedGPUUsagePerSecondPercent = 0.3;
|
||||
// Note: The following counters must have an integral value once multiplied by
|
||||
// the interval length in seconds (2.5).
|
||||
constexpr double kExpectedInterruptWakeUpPerSecond = 0.4;
|
||||
constexpr double kExpectedPlatformIdleWakeUpPerSecond = 10;
|
||||
constexpr double kExpectedBytesReadPerSecond = 0.8;
|
||||
constexpr double kExpectedBytesWrittenPerSecond = 1.6;
|
||||
constexpr double kExpectedPowerNW = 10000.0;
|
||||
// This number will be multiplied by the int value associated with a QoS level
|
||||
// to compute the expected time spent in this QoS level. E.g.
|
||||
// |QoSLevels::kUtility == 3| so the time spent in the utility QoS state will
|
||||
// be set to 3 * 0.1 = 30%.
|
||||
constexpr double kExpectedQoSTimeBucketIdMultiplier = 0.1;
|
||||
|
||||
// Scales a time given in ns to mach_time in |timebase|.
|
||||
uint64_t NsScaleToTimebase(const mach_timebase_info_data_t& timebase,
|
||||
int64_t time_ns) {
|
||||
return time_ns * timebase.denom / timebase.numer;
|
||||
}
|
||||
|
||||
// Returns test data with all time quantities scaled to the given time base.
|
||||
std::unique_ptr<coalition_resource_usage> GetCoalitionResourceUsageRateTestData(
|
||||
const mach_timebase_info_data_t& timebase) {
|
||||
std::unique_ptr<coalition_resource_usage> test_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
|
||||
// Scales a time given in ns to mach_time in |timebase|.
|
||||
auto scale_to_timebase = [&timebase](double time_ns) -> int64_t {
|
||||
return NsScaleToTimebase(timebase, time_ns);
|
||||
};
|
||||
|
||||
test_data->cpu_time = scale_to_timebase(kExpectedCPUUsagePerSecondPercent *
|
||||
kIntervalDuration.InNanoseconds());
|
||||
test_data->interrupt_wakeups =
|
||||
kExpectedInterruptWakeUpPerSecond * kIntervalDuration.InSecondsF();
|
||||
test_data->platform_idle_wakeups =
|
||||
kExpectedPlatformIdleWakeUpPerSecond * kIntervalDuration.InSecondsF();
|
||||
test_data->bytesread =
|
||||
kExpectedBytesReadPerSecond * kIntervalDuration.InSecondsF();
|
||||
test_data->byteswritten =
|
||||
kExpectedBytesWrittenPerSecond * kIntervalDuration.InSecondsF();
|
||||
test_data->gpu_time = scale_to_timebase(kExpectedGPUUsagePerSecondPercent *
|
||||
kIntervalDuration.InNanoseconds());
|
||||
test_data->energy = kExpectedPowerNW * kIntervalDuration.InSecondsF();
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
test_data->cpu_time_eqos[i] =
|
||||
scale_to_timebase(i * kExpectedQoSTimeBucketIdMultiplier *
|
||||
kIntervalDuration.InNanoseconds());
|
||||
}
|
||||
test_data->cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES;
|
||||
|
||||
return test_data;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(ResourceCoalitionMacTest, GetDataRate_NoEnergyImpact_Intel) {
|
||||
// Keep the initial data zero initialized.
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
GetCoalitionResourceUsageRateTestData(kIntelTimebase);
|
||||
|
||||
auto rate = GetCoalitionResourceUsageRate(
|
||||
*t0_data, *t1_data, kIntervalDuration, kIntelTimebase, absl::nullopt);
|
||||
ASSERT_TRUE(rate);
|
||||
EXPECT_EQ(kExpectedCPUUsagePerSecondPercent, rate->cpu_time_per_second);
|
||||
EXPECT_EQ(kExpectedInterruptWakeUpPerSecond,
|
||||
rate->interrupt_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedPlatformIdleWakeUpPerSecond,
|
||||
rate->platform_idle_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedBytesReadPerSecond, rate->bytesread_per_second);
|
||||
EXPECT_EQ(kExpectedBytesWrittenPerSecond, rate->byteswritten_per_second);
|
||||
EXPECT_EQ(kExpectedGPUUsagePerSecondPercent, rate->gpu_time_per_second);
|
||||
EXPECT_FALSE(rate->energy_impact_per_second.has_value());
|
||||
EXPECT_EQ(kExpectedPowerNW, rate->power_nw);
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
EXPECT_DOUBLE_EQ(i * kExpectedQoSTimeBucketIdMultiplier,
|
||||
rate->qos_time_per_second[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ResourceCoalitionMacTest, GetDataRate_NoEnergyImpact_M1) {
|
||||
// Keep the initial data zero initialized.
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
GetCoalitionResourceUsageRateTestData(kM1Timebase);
|
||||
|
||||
auto rate = GetCoalitionResourceUsageRate(
|
||||
*t0_data, *t1_data, kIntervalDuration, kM1Timebase, absl::nullopt);
|
||||
ASSERT_TRUE(rate);
|
||||
EXPECT_DOUBLE_EQ(kExpectedCPUUsagePerSecondPercent,
|
||||
rate->cpu_time_per_second);
|
||||
EXPECT_DOUBLE_EQ(kExpectedInterruptWakeUpPerSecond,
|
||||
rate->interrupt_wakeups_per_second);
|
||||
EXPECT_DOUBLE_EQ(kExpectedPlatformIdleWakeUpPerSecond,
|
||||
rate->platform_idle_wakeups_per_second);
|
||||
EXPECT_DOUBLE_EQ(kExpectedBytesReadPerSecond, rate->bytesread_per_second);
|
||||
EXPECT_DOUBLE_EQ(kExpectedBytesWrittenPerSecond,
|
||||
rate->byteswritten_per_second);
|
||||
EXPECT_DOUBLE_EQ(kExpectedGPUUsagePerSecondPercent,
|
||||
rate->gpu_time_per_second);
|
||||
EXPECT_FALSE(rate->energy_impact_per_second.has_value());
|
||||
EXPECT_DOUBLE_EQ(kExpectedPowerNW, rate->power_nw);
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
EXPECT_DOUBLE_EQ(i * kExpectedQoSTimeBucketIdMultiplier,
|
||||
rate->qos_time_per_second[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ResourceCoalitionMacTest, GetDataRate_WithEnergyImpact_Intel) {
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
GetCoalitionResourceUsageRateTestData(kIntelTimebase);
|
||||
|
||||
auto rate =
|
||||
GetCoalitionResourceUsageRate(*t0_data, *t1_data, kIntervalDuration,
|
||||
kIntelTimebase, kEnergyImpactCoefficients);
|
||||
ASSERT_TRUE(rate);
|
||||
EXPECT_EQ(kExpectedCPUUsagePerSecondPercent, rate->cpu_time_per_second);
|
||||
EXPECT_EQ(kExpectedInterruptWakeUpPerSecond,
|
||||
rate->interrupt_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedPlatformIdleWakeUpPerSecond,
|
||||
rate->platform_idle_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedBytesReadPerSecond, rate->bytesread_per_second);
|
||||
EXPECT_EQ(kExpectedBytesWrittenPerSecond, rate->byteswritten_per_second);
|
||||
EXPECT_EQ(kExpectedGPUUsagePerSecondPercent, rate->gpu_time_per_second);
|
||||
ASSERT_TRUE(rate->energy_impact_per_second.has_value());
|
||||
EXPECT_EQ(271.2, rate->energy_impact_per_second.value());
|
||||
EXPECT_FLOAT_EQ(kExpectedPowerNW, rate->power_nw);
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
EXPECT_DOUBLE_EQ(i * kExpectedQoSTimeBucketIdMultiplier,
|
||||
rate->qos_time_per_second[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ResourceCoalitionMacTest, GetDataRate_WithEnergyImpact_M1) {
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
GetCoalitionResourceUsageRateTestData(kM1Timebase);
|
||||
|
||||
auto rate =
|
||||
GetCoalitionResourceUsageRate(*t0_data, *t1_data, kIntervalDuration,
|
||||
kM1Timebase, kEnergyImpactCoefficients);
|
||||
ASSERT_TRUE(rate);
|
||||
EXPECT_EQ(kExpectedCPUUsagePerSecondPercent, rate->cpu_time_per_second);
|
||||
EXPECT_EQ(kExpectedInterruptWakeUpPerSecond,
|
||||
rate->interrupt_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedPlatformIdleWakeUpPerSecond,
|
||||
rate->platform_idle_wakeups_per_second);
|
||||
EXPECT_EQ(kExpectedBytesReadPerSecond, rate->bytesread_per_second);
|
||||
EXPECT_EQ(kExpectedBytesWrittenPerSecond, rate->byteswritten_per_second);
|
||||
EXPECT_EQ(kExpectedGPUUsagePerSecondPercent, rate->gpu_time_per_second);
|
||||
ASSERT_TRUE(rate->energy_impact_per_second.has_value());
|
||||
EXPECT_EQ(271.2, rate->energy_impact_per_second.value());
|
||||
EXPECT_FLOAT_EQ(kExpectedPowerNW, rate->power_nw);
|
||||
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
EXPECT_DOUBLE_EQ(i * kExpectedQoSTimeBucketIdMultiplier,
|
||||
rate->qos_time_per_second[i]);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool DataOverflowInvalidatesDiffImpl(
|
||||
std::unique_ptr<coalition_resource_usage> t0,
|
||||
std::unique_ptr<coalition_resource_usage> t1,
|
||||
uint64_t* field_to_overflow) {
|
||||
// Initialize all fields to a non zero value.
|
||||
::memset(t0.get(), 1000, sizeof(coalition_resource_usage));
|
||||
::memset(t1.get(), 1000, sizeof(coalition_resource_usage));
|
||||
*field_to_overflow = 0;
|
||||
t1->cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES;
|
||||
return !GetCoalitionResourceUsageRate(*t0, *t1, kIntervalDuration,
|
||||
kIntelTimebase, absl::nullopt)
|
||||
.has_value();
|
||||
}
|
||||
|
||||
bool DataOverflowInvalidatesDiff(
|
||||
uint64_t coalition_resource_usage::*member_ptr) {
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
auto* ptr = &(t1_data.get()->*member_ptr);
|
||||
return DataOverflowInvalidatesDiffImpl(std::move(t0_data), std::move(t1_data),
|
||||
ptr);
|
||||
}
|
||||
|
||||
bool DataOverflowInvalidatesDiff(
|
||||
uint64_t (
|
||||
coalition_resource_usage::*member_ptr)[COALITION_NUM_THREAD_QOS_TYPES],
|
||||
int index_to_check) {
|
||||
std::unique_ptr<coalition_resource_usage> t0_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
std::unique_ptr<coalition_resource_usage> t1_data =
|
||||
std::make_unique<coalition_resource_usage>();
|
||||
auto* ptr = &(t1_data.get()->*member_ptr)[index_to_check];
|
||||
return DataOverflowInvalidatesDiffImpl(std::move(t0_data), std::move(t1_data),
|
||||
ptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// If one of these tests fails then it means that overflows on a newly tracked
|
||||
// coalition field aren't tracked properly in GetCoalitionResourceUsageRate().
|
||||
TEST(ResourceCoalitionTests, Overflows) {
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::energy_billed_to_me));
|
||||
EXPECT_FALSE(
|
||||
DataOverflowInvalidatesDiff(&coalition_resource_usage::tasks_started));
|
||||
EXPECT_FALSE(
|
||||
DataOverflowInvalidatesDiff(&coalition_resource_usage::tasks_exited));
|
||||
EXPECT_FALSE(
|
||||
DataOverflowInvalidatesDiff(&coalition_resource_usage::time_nonempty));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(&coalition_resource_usage::cpu_time));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::interrupt_wakeups));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::platform_idle_wakeups));
|
||||
EXPECT_TRUE(
|
||||
DataOverflowInvalidatesDiff(&coalition_resource_usage::bytesread));
|
||||
EXPECT_TRUE(
|
||||
DataOverflowInvalidatesDiff(&coalition_resource_usage::byteswritten));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(&coalition_resource_usage::gpu_time));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::cpu_time_billed_to_me));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::cpu_time_billed_to_others));
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(&coalition_resource_usage::energy));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::logical_immediate_writes));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::logical_deferred_writes));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::logical_invalidated_writes));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::logical_metadata_writes));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::logical_immediate_writes_to_external));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::logical_deferred_writes_to_external));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::logical_invalidated_writes_to_external));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::logical_metadata_writes_to_external));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::energy_billed_to_me));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::energy_billed_to_others));
|
||||
EXPECT_FALSE(
|
||||
DataOverflowInvalidatesDiff(&coalition_resource_usage::cpu_ptime));
|
||||
for (int i = 0; i < COALITION_NUM_THREAD_QOS_TYPES; ++i) {
|
||||
EXPECT_TRUE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::cpu_time_eqos, i));
|
||||
}
|
||||
EXPECT_FALSE(
|
||||
DataOverflowInvalidatesDiff(&coalition_resource_usage::cpu_instructions));
|
||||
EXPECT_FALSE(
|
||||
DataOverflowInvalidatesDiff(&coalition_resource_usage::cpu_cycles));
|
||||
EXPECT_FALSE(DataOverflowInvalidatesDiff(
|
||||
&coalition_resource_usage::fs_metadata_writes));
|
||||
EXPECT_FALSE(
|
||||
DataOverflowInvalidatesDiff(&coalition_resource_usage::pm_writes));
|
||||
}
|
||||
|
||||
} // namespace power_metrics
|
||||
|
Reference in New Issue
Block a user