Add SystemPowerMonitor in //components/power_metrics
It monitors system-wide power consumption. Specifically, it first obtains absolute energy data from power_metrics::EnergyMetricsProvider, then calculates the power consumption and calls TRACE_COUNTER to write power value into trace log. Bug: 1385251 Change-Id: I756755df3f6ec91688aef7dc4156b845246056ea Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4472031 Reviewed-by: Mark Pearson <mpearson@chromium.org> Commit-Queue: Francois Pierre Doray <fdoray@chromium.org> Reviewed-by: Francois Pierre Doray <fdoray@chromium.org> Cr-Commit-Position: refs/heads/main@{#1145923}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
4e10c9c110
commit
d2956aa221
base/trace_event
chrome/browser
components/power_metrics
@ -259,6 +259,7 @@
|
||||
X(TRACE_DISABLED_BY_DEFAULT("skia.gpu.cache")) \
|
||||
X(TRACE_DISABLED_BY_DEFAULT("skia.shaders")) \
|
||||
X(TRACE_DISABLED_BY_DEFAULT("SyncFileSystem")) \
|
||||
X(TRACE_DISABLED_BY_DEFAULT("system_power")) \
|
||||
X(TRACE_DISABLED_BY_DEFAULT("system_stats")) \
|
||||
X(TRACE_DISABLED_BY_DEFAULT("thread_pool_diagnostics")) \
|
||||
X(TRACE_DISABLED_BY_DEFAULT("toplevel.ipc")) \
|
||||
|
@ -6035,7 +6035,6 @@ static_library("browser") {
|
||||
"//components/crash/core/app",
|
||||
"//components/metal_util",
|
||||
"//components/policy/core/common:common_constants",
|
||||
"//components/power_metrics",
|
||||
"//components/remote_cocoa/browser:browser",
|
||||
"//sandbox/mac:seatbelt",
|
||||
"//sandbox/policy",
|
||||
@ -6213,6 +6212,7 @@ static_library("browser") {
|
||||
"//chrome/browser/enterprise/connectors/device_trust/key_management/core",
|
||||
"//chrome/browser/enterprise/connectors/device_trust/key_management/core/persistence",
|
||||
"//chrome/services/system_signals/public/cpp/browser",
|
||||
"//components/power_metrics",
|
||||
]
|
||||
public_deps +=
|
||||
[ "//chrome/browser/enterprise/connectors/analysis:sdk_manager" ]
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "base/threading/scoped_blocking_call.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/trace_event/trace_log.h"
|
||||
#include "build/build_config.h"
|
||||
#include "build/chromeos_buildflags.h"
|
||||
#include "build/config/compiler/compiler_buildflags.h"
|
||||
@ -110,6 +111,10 @@
|
||||
#include "components/user_manager/user_manager.h"
|
||||
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
|
||||
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
|
||||
#include "components/power_metrics/system_power_monitor.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
// The number of restarts to wait until removing the enable-benchmarking flag.
|
||||
@ -741,6 +746,11 @@ void ChromeBrowserMainExtraPartsMetrics::PostBrowserStart() {
|
||||
pressure_metrics_reporter_ = std::make_unique<PressureMetricsReporter>();
|
||||
#endif // BUILDFLAG(IS_LINUX)
|
||||
|
||||
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
|
||||
base::trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(
|
||||
power_metrics::SystemPowerMonitor::GetInstance());
|
||||
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX)
|
||||
|
||||
HandleEnableBenchmarkingCountdownAsync();
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,13 @@ static_library("power_metrics") {
|
||||
"energy_metrics_provider_linux.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (is_win || is_linux) {
|
||||
sources += [
|
||||
"system_power_monitor.cc",
|
||||
"system_power_monitor.h",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
source_set("unit_tests") {
|
||||
@ -57,6 +64,7 @@ source_set("unit_tests") {
|
||||
|
||||
deps = [
|
||||
":power_metrics",
|
||||
"//base",
|
||||
"//testing/gtest",
|
||||
]
|
||||
|
||||
@ -68,7 +76,11 @@ source_set("unit_tests") {
|
||||
configs += [ "//build/config/compiler:enable_arc" ]
|
||||
|
||||
data = [ "test/data/" ]
|
||||
}
|
||||
|
||||
deps += [ "//base" ]
|
||||
if (is_win || is_linux) {
|
||||
sources += [ "system_power_monitor_unittest.cc" ]
|
||||
|
||||
deps += [ "//base/test:test_support" ]
|
||||
}
|
||||
}
|
||||
|
228
components/power_metrics/system_power_monitor.cc
Normal file
228
components/power_metrics/system_power_monitor.cc
Normal file
@ -0,0 +1,228 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "components/power_metrics/system_power_monitor.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/task/task_traits.h"
|
||||
#include "base/task/thread_pool.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
|
||||
namespace power_metrics {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char kTraceCategory[] =
|
||||
TRACE_DISABLED_BY_DEFAULT("system_power");
|
||||
|
||||
constexpr const char kPackagePowerTraceCounterName[] = "Package Power (mW)";
|
||||
constexpr const char kCpuPowerTraceCounterName[] = "CPU Power (mW)";
|
||||
constexpr const char kIntegratedGpuPowerTraceCounterName[] = "iGPU Power (mW)";
|
||||
constexpr const char kDramPowerTraceCounterName[] = "DRAM Power (mW)";
|
||||
constexpr const char kPsysPowerTraceCounterName[] = "Psys Power (mW)";
|
||||
constexpr const char kVddcrVddTraceCounterName[] = "VDDCR VDD (mW)";
|
||||
constexpr const char kVddcrSocTraceCounterName[] = "VDDCR SOC (mW)";
|
||||
constexpr const char kCurrentSocketTraceCounterName[] = "Current Socket (mW)";
|
||||
constexpr const char kApuPowerTraceCounterName[] = "APU Power (mW)";
|
||||
|
||||
// Here we determine if the specified metric is valid according to whether its
|
||||
// corresponding value in the provided sample is greater than 0, since the
|
||||
// absolute energy must be greater than 0.
|
||||
bool GenerateValidMetrics(const EnergyMetricsProvider::EnergyMetrics& sample,
|
||||
std::vector<const char*>& valid_metrics) {
|
||||
if (sample.package_nanojoules > 0) {
|
||||
valid_metrics.push_back(kPackagePowerTraceCounterName);
|
||||
}
|
||||
if (sample.cpu_nanojoules > 0) {
|
||||
valid_metrics.push_back(kCpuPowerTraceCounterName);
|
||||
}
|
||||
if (sample.gpu_nanojoules > 0) {
|
||||
valid_metrics.push_back(kIntegratedGpuPowerTraceCounterName);
|
||||
}
|
||||
if (sample.dram_nanojoules > 0) {
|
||||
valid_metrics.push_back(kDramPowerTraceCounterName);
|
||||
}
|
||||
if (sample.psys_nanojoules > 0) {
|
||||
valid_metrics.push_back(kPsysPowerTraceCounterName);
|
||||
}
|
||||
if (sample.vdd_nanojoules > 0) {
|
||||
valid_metrics.push_back(kVddcrVddTraceCounterName);
|
||||
}
|
||||
if (sample.soc_nanojoules > 0) {
|
||||
valid_metrics.push_back(kVddcrSocTraceCounterName);
|
||||
}
|
||||
if (sample.socket_nanojoules > 0) {
|
||||
valid_metrics.push_back(kCurrentSocketTraceCounterName);
|
||||
}
|
||||
if (sample.apu_nanojoules > 0) {
|
||||
valid_metrics.push_back(kApuPowerTraceCounterName);
|
||||
}
|
||||
return !valid_metrics.empty();
|
||||
}
|
||||
|
||||
int64_t CalculateNanojoulesDeltaFromSamples(
|
||||
const EnergyMetricsProvider::EnergyMetrics& new_sample,
|
||||
const EnergyMetricsProvider::EnergyMetrics& old_sample,
|
||||
const char* metric) {
|
||||
if (std::strcmp(metric, kPackagePowerTraceCounterName) == 0) {
|
||||
return static_cast<int64_t>(new_sample.package_nanojoules -
|
||||
old_sample.package_nanojoules);
|
||||
} else if (std::strcmp(metric, kCpuPowerTraceCounterName) == 0) {
|
||||
return static_cast<int64_t>(new_sample.cpu_nanojoules -
|
||||
old_sample.cpu_nanojoules);
|
||||
} else if (std::strcmp(metric, kIntegratedGpuPowerTraceCounterName) == 0) {
|
||||
return static_cast<int64_t>(new_sample.gpu_nanojoules -
|
||||
old_sample.gpu_nanojoules);
|
||||
} else if (std::strcmp(metric, kDramPowerTraceCounterName) == 0) {
|
||||
return static_cast<int64_t>(new_sample.dram_nanojoules -
|
||||
old_sample.dram_nanojoules);
|
||||
} else if (std::strcmp(metric, kPsysPowerTraceCounterName) == 0) {
|
||||
return static_cast<int64_t>(new_sample.psys_nanojoules -
|
||||
old_sample.psys_nanojoules);
|
||||
} else if (std::strcmp(metric, kVddcrVddTraceCounterName) == 0) {
|
||||
return static_cast<int64_t>(new_sample.vdd_nanojoules -
|
||||
old_sample.vdd_nanojoules);
|
||||
} else if (std::strcmp(metric, kVddcrSocTraceCounterName) == 0) {
|
||||
return static_cast<int64_t>(new_sample.soc_nanojoules -
|
||||
old_sample.soc_nanojoules);
|
||||
} else if (std::strcmp(metric, kCurrentSocketTraceCounterName) == 0) {
|
||||
return static_cast<int64_t>(new_sample.socket_nanojoules -
|
||||
old_sample.socket_nanojoules);
|
||||
} else if (std::strcmp(metric, kApuPowerTraceCounterName) == 0) {
|
||||
return static_cast<int64_t>(new_sample.apu_nanojoules -
|
||||
old_sample.apu_nanojoules);
|
||||
}
|
||||
NOTREACHED_NORETURN() << "Unexpected metric: " << metric;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SystemPowerMonitorDelegate::SystemPowerMonitorDelegate() = default;
|
||||
SystemPowerMonitorDelegate::~SystemPowerMonitorDelegate() = default;
|
||||
|
||||
void SystemPowerMonitorDelegate::RecordSystemPower(const char* metric,
|
||||
base::TimeTicks timestamp,
|
||||
int64_t power) {
|
||||
TRACE_COUNTER_WITH_TIMESTAMP1(TRACE_DISABLED_BY_DEFAULT("system_power"),
|
||||
metric, timestamp, power);
|
||||
}
|
||||
|
||||
bool SystemPowerMonitorDelegate::IsTraceCategoryEnabled() const {
|
||||
bool enabled;
|
||||
TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled);
|
||||
return enabled;
|
||||
}
|
||||
|
||||
SystemPowerMonitorHelper::SystemPowerMonitorHelper(
|
||||
std::unique_ptr<EnergyMetricsProvider> provider,
|
||||
std::unique_ptr<SystemPowerMonitorDelegate> delegate)
|
||||
: provider_(std::move(provider)), delegate_(std::move(delegate)) {}
|
||||
|
||||
SystemPowerMonitorHelper::~SystemPowerMonitorHelper() = default;
|
||||
|
||||
void SystemPowerMonitorHelper::Start() {
|
||||
CHECK(provider_);
|
||||
CHECK(!timer_.IsRunning());
|
||||
if (!delegate_->IsTraceCategoryEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the provider fails to capture valid sample at the first time, we
|
||||
// determine that it is unable to provide valid data and give up starting the
|
||||
// timer.
|
||||
auto sample = provider_->CaptureMetrics();
|
||||
if (!sample.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// To avoid redundant loops on invalid metrics, we select the valid metrics
|
||||
// before start.
|
||||
CHECK(valid_metrics_.empty());
|
||||
if (!GenerateValidMetrics(sample.value(), valid_metrics_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
last_sample_ = sample.value();
|
||||
last_timestamp_ = base::TimeTicks::Now();
|
||||
|
||||
timer_.Start(FROM_HERE, kDefaultSampleInterval,
|
||||
base::BindRepeating(&SystemPowerMonitorHelper::Sample,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
void SystemPowerMonitorHelper::Stop() {
|
||||
timer_.Stop();
|
||||
valid_metrics_.clear();
|
||||
}
|
||||
|
||||
void SystemPowerMonitorHelper::Sample() {
|
||||
// If the provider fails to capture valid metrics after the timer started,
|
||||
// we leave the timer running.
|
||||
auto sample = provider_->CaptureMetrics();
|
||||
if (!sample.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
base::TimeTicks timestamp = base::TimeTicks::Now();
|
||||
base::TimeDelta interval = timestamp - last_timestamp_;
|
||||
CHECK(interval.is_positive());
|
||||
|
||||
for (auto const* metric : valid_metrics_) {
|
||||
int64_t nanojoules = CalculateNanojoulesDeltaFromSamples(
|
||||
sample.value(), last_sample_, metric);
|
||||
CHECK_GE(nanojoules, 0ll);
|
||||
|
||||
int64_t milliwatts = nanojoules / interval.InMicroseconds();
|
||||
delegate_->RecordSystemPower(metric, last_timestamp_, milliwatts);
|
||||
}
|
||||
|
||||
last_sample_ = sample.value();
|
||||
last_timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
bool SystemPowerMonitorHelper::IsTimerRunningForTesting() {
|
||||
return timer_.IsRunning();
|
||||
}
|
||||
|
||||
SystemPowerMonitor::SystemPowerMonitor()
|
||||
: SystemPowerMonitor(EnergyMetricsProvider::Create(),
|
||||
std::make_unique<SystemPowerMonitorDelegate>()) {}
|
||||
|
||||
SystemPowerMonitor::SystemPowerMonitor(
|
||||
std::unique_ptr<EnergyMetricsProvider> provider,
|
||||
std::unique_ptr<SystemPowerMonitorDelegate> delegate) {
|
||||
helper_ = base::SequenceBound<SystemPowerMonitorHelper>(
|
||||
base::ThreadPool::CreateSequencedTaskRunner(
|
||||
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
|
||||
base::TaskPriority::BEST_EFFORT}),
|
||||
std::move(provider), std::move(delegate));
|
||||
}
|
||||
|
||||
SystemPowerMonitor::~SystemPowerMonitor() = default;
|
||||
|
||||
// static
|
||||
SystemPowerMonitor* SystemPowerMonitor::GetInstance() {
|
||||
static base::NoDestructor<SystemPowerMonitor> instance;
|
||||
return instance.get();
|
||||
}
|
||||
|
||||
void SystemPowerMonitor::OnTraceLogEnabled() {
|
||||
helper_.AsyncCall(&SystemPowerMonitorHelper::Start);
|
||||
}
|
||||
|
||||
void SystemPowerMonitor::OnTraceLogDisabled() {
|
||||
helper_.AsyncCall(&SystemPowerMonitorHelper::Stop);
|
||||
}
|
||||
|
||||
base::SequenceBound<SystemPowerMonitorHelper>*
|
||||
SystemPowerMonitor::GetHelperForTesting() {
|
||||
return helper_ ? &helper_ : nullptr;
|
||||
}
|
||||
|
||||
} // namespace power_metrics
|
107
components/power_metrics/system_power_monitor.h
Normal file
107
components/power_metrics/system_power_monitor.h
Normal file
@ -0,0 +1,107 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef COMPONENTS_POWER_METRICS_SYSTEM_POWER_MONITOR_H_
|
||||
#define COMPONENTS_POWER_METRICS_SYSTEM_POWER_MONITOR_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/threading/sequence_bound.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/timer.h"
|
||||
#include "base/trace_event/trace_log.h"
|
||||
#include "components/power_metrics/energy_metrics_provider.h"
|
||||
|
||||
namespace power_metrics {
|
||||
|
||||
// A delegate to isolate System Power Monitor functionality mainly for
|
||||
// testing.
|
||||
class SystemPowerMonitorDelegate {
|
||||
public:
|
||||
SystemPowerMonitorDelegate();
|
||||
|
||||
SystemPowerMonitorDelegate(const SystemPowerMonitorDelegate&) = delete;
|
||||
SystemPowerMonitorDelegate& operator=(const SystemPowerMonitorDelegate&) =
|
||||
delete;
|
||||
|
||||
virtual ~SystemPowerMonitorDelegate();
|
||||
|
||||
// Emits trace counter. The metric string stands for trace counter name,
|
||||
// timestamp is the counter timestamp, power is the corresponding counter
|
||||
// value of system power consumption in units of milliwatts.
|
||||
virtual void RecordSystemPower(const char* metric,
|
||||
base::TimeTicks timestamp,
|
||||
int64_t power);
|
||||
|
||||
// Returns whether the tracing category is enabled to determine if we should
|
||||
// record.
|
||||
virtual bool IsTraceCategoryEnabled() const;
|
||||
};
|
||||
|
||||
// Manages a timer to regularly sample and emit trace events, whose start and
|
||||
// stop are controlled by System Power Monitor.
|
||||
class SystemPowerMonitorHelper {
|
||||
public:
|
||||
// Default sampling interval, which should be set to larger or equal to 50 ms.
|
||||
static constexpr base::TimeDelta kDefaultSampleInterval =
|
||||
base::Milliseconds(50);
|
||||
|
||||
SystemPowerMonitorHelper(
|
||||
std::unique_ptr<EnergyMetricsProvider> provider,
|
||||
std::unique_ptr<SystemPowerMonitorDelegate> delegate);
|
||||
|
||||
SystemPowerMonitorHelper(const SystemPowerMonitorHelper&) = delete;
|
||||
SystemPowerMonitorHelper& operator=(const SystemPowerMonitorHelper&) = delete;
|
||||
|
||||
~SystemPowerMonitorHelper();
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void Sample();
|
||||
|
||||
bool IsTimerRunningForTesting();
|
||||
|
||||
private:
|
||||
std::vector<const char*> valid_metrics_;
|
||||
EnergyMetricsProvider::EnergyMetrics last_sample_;
|
||||
base::TimeTicks last_timestamp_;
|
||||
base::RepeatingTimer timer_;
|
||||
|
||||
// Used to derive instant system energy metrics.
|
||||
std::unique_ptr<EnergyMetricsProvider> provider_;
|
||||
std::unique_ptr<SystemPowerMonitorDelegate> delegate_;
|
||||
};
|
||||
|
||||
// Monitors system-wide power consumption. Gets data from EnergyMetricsProvider.
|
||||
class SystemPowerMonitor
|
||||
: public base::trace_event::TraceLog::EnabledStateObserver {
|
||||
public:
|
||||
SystemPowerMonitor();
|
||||
|
||||
SystemPowerMonitor(const SystemPowerMonitor&) = delete;
|
||||
SystemPowerMonitor& operator=(const SystemPowerMonitor&) = delete;
|
||||
|
||||
~SystemPowerMonitor() override;
|
||||
|
||||
static SystemPowerMonitor* GetInstance();
|
||||
|
||||
// TraceLog::EnabledStateObserver.
|
||||
void OnTraceLogEnabled() override;
|
||||
void OnTraceLogDisabled() override;
|
||||
|
||||
private:
|
||||
friend class SystemPowerMonitorTest;
|
||||
|
||||
SystemPowerMonitor(std::unique_ptr<EnergyMetricsProvider> provider,
|
||||
std::unique_ptr<SystemPowerMonitorDelegate> delegate);
|
||||
|
||||
base::SequenceBound<SystemPowerMonitorHelper>* GetHelperForTesting();
|
||||
|
||||
base::SequenceBound<SystemPowerMonitorHelper> helper_;
|
||||
};
|
||||
|
||||
} // namespace power_metrics
|
||||
|
||||
#endif // COMPONENTS_POWER_METRICS_SYSTEM_POWER_MONITOR_H_
|
253
components/power_metrics/system_power_monitor_unittest.cc
Normal file
253
components/power_metrics/system_power_monitor_unittest.cc
Normal file
@ -0,0 +1,253 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "components/power_metrics/system_power_monitor.h"
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "base/test/test_future.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace power_metrics {
|
||||
|
||||
class FakeProvider : public EnergyMetricsProvider {
|
||||
public:
|
||||
void set_metrics(EnergyMetrics metrics) { metrics_ = metrics; }
|
||||
|
||||
absl::optional<EnergyMetrics> CaptureMetrics() override { return metrics_; }
|
||||
|
||||
private:
|
||||
absl::optional<EnergyMetrics> metrics_;
|
||||
};
|
||||
|
||||
class FakeDelegate : public SystemPowerMonitorDelegate {
|
||||
public:
|
||||
void set_trace_category_enabled(bool enabled) {
|
||||
trace_category_enabled_ = enabled;
|
||||
}
|
||||
|
||||
void RecordSystemPower(const char* category,
|
||||
base::TimeTicks timestamp,
|
||||
int64_t power) override {
|
||||
timestamp_ = timestamp;
|
||||
if (strcmp(category, "Package Power (mW)") == 0) {
|
||||
system_power_.package_nanojoules = static_cast<uint64_t>(power);
|
||||
} else if (strcmp(category, "CPU Power (mW)") == 0) {
|
||||
system_power_.cpu_nanojoules = static_cast<uint64_t>(power);
|
||||
} else if (strcmp(category, "iGPU Power (mW)") == 0) {
|
||||
system_power_.gpu_nanojoules = static_cast<uint64_t>(power);
|
||||
} else if (strcmp(category, "DRAM Power (mW)") == 0) {
|
||||
system_power_.dram_nanojoules = static_cast<uint64_t>(power);
|
||||
} else if (strcmp(category, "Psys Power (mW)") == 0) {
|
||||
system_power_.psys_nanojoules = static_cast<uint64_t>(power);
|
||||
} else if (strcmp(category, "VDDCR VDD (mW)") == 0) {
|
||||
system_power_.vdd_nanojoules = static_cast<uint64_t>(power);
|
||||
} else if (strcmp(category, "VDDCR SOC (mW)") == 0) {
|
||||
system_power_.soc_nanojoules = static_cast<uint64_t>(power);
|
||||
} else if (strcmp(category, "Current Socket (mW)") == 0) {
|
||||
system_power_.socket_nanojoules = static_cast<uint64_t>(power);
|
||||
} else if (strcmp(category, "APU Power (mW)") == 0) {
|
||||
system_power_.apu_nanojoules = static_cast<uint64_t>(power);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsTraceCategoryEnabled() const override {
|
||||
return trace_category_enabled_;
|
||||
}
|
||||
|
||||
EnergyMetricsProvider::EnergyMetrics& SystemPower() { return system_power_; }
|
||||
|
||||
base::TimeTicks timestamp() { return timestamp_; }
|
||||
|
||||
private:
|
||||
// We use EnergyMetrics to save recorded power data in milliwatts for
|
||||
// simplicity.
|
||||
EnergyMetricsProvider::EnergyMetrics system_power_;
|
||||
base::TimeTicks timestamp_;
|
||||
bool trace_category_enabled_{true};
|
||||
};
|
||||
|
||||
class SystemPowerMonitorHelperTest : public testing::Test {
|
||||
public:
|
||||
SystemPowerMonitorHelperTest()
|
||||
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
|
||||
|
||||
void SetUp() override {
|
||||
auto provider = std::make_unique<FakeProvider>();
|
||||
provider_ = provider.get();
|
||||
auto delegate = std::make_unique<FakeDelegate>();
|
||||
delegate_ = delegate.get();
|
||||
helper_ = std::make_unique<SystemPowerMonitorHelper>(std::move(provider),
|
||||
std::move(delegate));
|
||||
}
|
||||
|
||||
void TearDown() override { helper_.reset(); }
|
||||
|
||||
base::test::TaskEnvironment& task_environment() { return task_environment_; }
|
||||
SystemPowerMonitorHelper* helper() { return helper_.get(); }
|
||||
FakeDelegate* delegate() { return delegate_.get(); }
|
||||
FakeProvider* provider() { return provider_.get(); }
|
||||
|
||||
protected:
|
||||
base::test::TaskEnvironment task_environment_;
|
||||
|
||||
private:
|
||||
std::unique_ptr<SystemPowerMonitorHelper> helper_;
|
||||
raw_ptr<FakeDelegate> delegate_;
|
||||
raw_ptr<FakeProvider> provider_;
|
||||
};
|
||||
|
||||
class SystemPowerMonitorTest : public testing::Test {
|
||||
public:
|
||||
SystemPowerMonitorTest() : task_environment_() {}
|
||||
|
||||
void SetUp() override {
|
||||
auto provider = std::make_unique<FakeProvider>();
|
||||
|
||||
// Assign a valid metric to provider, so the timer can start successfully.
|
||||
provider->set_metrics({1llu});
|
||||
monitor_.reset(new SystemPowerMonitor(std::move(provider),
|
||||
std::make_unique<FakeDelegate>()));
|
||||
}
|
||||
|
||||
void TearDown() override { monitor_.reset(); }
|
||||
|
||||
SystemPowerMonitor* monitor() { return monitor_.get(); }
|
||||
base::SequenceBound<SystemPowerMonitorHelper>* helper() {
|
||||
return monitor_->GetHelperForTesting();
|
||||
}
|
||||
|
||||
protected:
|
||||
base::test::TaskEnvironment task_environment_;
|
||||
|
||||
private:
|
||||
std::unique_ptr<SystemPowerMonitor> monitor_;
|
||||
};
|
||||
|
||||
TEST_F(SystemPowerMonitorHelperTest, MonitorHelperStartStop) {
|
||||
provider()->set_metrics({1llu});
|
||||
|
||||
helper()->Start();
|
||||
ASSERT_TRUE(helper()->IsTimerRunningForTesting());
|
||||
helper()->Stop();
|
||||
ASSERT_FALSE(helper()->IsTimerRunningForTesting());
|
||||
helper()->Start();
|
||||
ASSERT_TRUE(helper()->IsTimerRunningForTesting());
|
||||
helper()->Stop();
|
||||
ASSERT_FALSE(helper()->IsTimerRunningForTesting());
|
||||
}
|
||||
|
||||
TEST_F(SystemPowerMonitorHelperTest, TimerStartFailed_InvalidSample) {
|
||||
// We haven't set metrics for provider, so monitor gets an
|
||||
// absl::nullopt sample at the beginning and it will not start.
|
||||
helper()->Start();
|
||||
ASSERT_FALSE(helper()->IsTimerRunningForTesting());
|
||||
}
|
||||
|
||||
TEST_F(SystemPowerMonitorHelperTest, TimerStartFailed_MetricsAllZero) {
|
||||
// If the metrics are all 0, we determine that there is no valid metric
|
||||
// provided, so monitor will not start.
|
||||
provider()->set_metrics({});
|
||||
helper()->Start();
|
||||
ASSERT_FALSE(helper()->IsTimerRunningForTesting());
|
||||
}
|
||||
|
||||
TEST_F(SystemPowerMonitorHelperTest, TraceCategoryEnableDisable) {
|
||||
provider()->set_metrics({1llu});
|
||||
|
||||
delegate()->set_trace_category_enabled(false);
|
||||
ASSERT_FALSE(delegate()->IsTraceCategoryEnabled());
|
||||
helper()->Start();
|
||||
ASSERT_FALSE(helper()->IsTimerRunningForTesting());
|
||||
|
||||
delegate()->set_trace_category_enabled(true);
|
||||
ASSERT_TRUE(delegate()->IsTraceCategoryEnabled());
|
||||
helper()->Start();
|
||||
ASSERT_TRUE(helper()->IsTimerRunningForTesting());
|
||||
}
|
||||
|
||||
TEST_F(SystemPowerMonitorHelperTest, TestSample) {
|
||||
EnergyMetricsProvider::EnergyMetrics sample1 = {
|
||||
100000llu, 100000llu, 100000llu, 100000llu, 100000llu,
|
||||
100000llu, 100000llu, 100000llu, 100000llu};
|
||||
EnergyMetricsProvider::EnergyMetrics sample2 = {
|
||||
200000llu, 300000llu, 400000llu, 500000llu, 600000llu,
|
||||
700000llu, 800000llu, 900000llu, 1000000llu};
|
||||
|
||||
provider()->set_metrics(sample1);
|
||||
helper()->Start();
|
||||
ASSERT_TRUE(helper()->IsTimerRunningForTesting());
|
||||
|
||||
provider()->set_metrics(sample2);
|
||||
task_environment().FastForwardBy(
|
||||
SystemPowerMonitorHelper::kDefaultSampleInterval);
|
||||
auto power = delegate()->SystemPower();
|
||||
EXPECT_EQ(delegate()->timestamp() +
|
||||
SystemPowerMonitorHelper::kDefaultSampleInterval,
|
||||
task_environment().NowTicks());
|
||||
EXPECT_EQ(
|
||||
power.package_nanojoules,
|
||||
(sample2.package_nanojoules - sample1.package_nanojoules) /
|
||||
SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
|
||||
EXPECT_EQ(
|
||||
power.cpu_nanojoules,
|
||||
(sample2.cpu_nanojoules - sample1.cpu_nanojoules) /
|
||||
SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
|
||||
EXPECT_EQ(
|
||||
power.gpu_nanojoules,
|
||||
(sample2.gpu_nanojoules - sample1.gpu_nanojoules) /
|
||||
SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
|
||||
EXPECT_EQ(
|
||||
power.dram_nanojoules,
|
||||
(sample2.dram_nanojoules - sample1.dram_nanojoules) /
|
||||
SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
|
||||
EXPECT_EQ(
|
||||
power.psys_nanojoules,
|
||||
(sample2.psys_nanojoules - sample1.psys_nanojoules) /
|
||||
SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
|
||||
EXPECT_EQ(
|
||||
power.vdd_nanojoules,
|
||||
(sample2.vdd_nanojoules - sample1.vdd_nanojoules) /
|
||||
SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
|
||||
EXPECT_EQ(
|
||||
power.soc_nanojoules,
|
||||
(sample2.soc_nanojoules - sample1.soc_nanojoules) /
|
||||
SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
|
||||
EXPECT_EQ(
|
||||
power.socket_nanojoules,
|
||||
(sample2.socket_nanojoules - sample1.socket_nanojoules) /
|
||||
SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
|
||||
EXPECT_EQ(
|
||||
power.apu_nanojoules,
|
||||
(sample2.apu_nanojoules - sample1.apu_nanojoules) /
|
||||
SystemPowerMonitorHelper::kDefaultSampleInterval.InMicroseconds());
|
||||
}
|
||||
|
||||
TEST_F(SystemPowerMonitorTest, TraceLogEnableDisable) {
|
||||
ASSERT_NE(helper(), nullptr);
|
||||
|
||||
base::test::TestFuture<bool> future_enable;
|
||||
monitor()->OnTraceLogEnabled();
|
||||
helper()
|
||||
->AsyncCall(&SystemPowerMonitorHelper::IsTimerRunningForTesting)
|
||||
.Then(base::BindOnce(
|
||||
[](base::OnceCallback<void(bool)> callback, bool is_running) {
|
||||
std::move(callback).Run(is_running);
|
||||
},
|
||||
future_enable.GetCallback()));
|
||||
EXPECT_TRUE(future_enable.Get());
|
||||
|
||||
base::test::TestFuture<bool> future_disable;
|
||||
monitor()->OnTraceLogDisabled();
|
||||
helper()
|
||||
->AsyncCall(&SystemPowerMonitorHelper::IsTimerRunningForTesting)
|
||||
.Then(base::BindOnce(
|
||||
[](base::OnceCallback<void(bool)> callback, bool is_running) {
|
||||
std::move(callback).Run(is_running);
|
||||
},
|
||||
future_disable.GetCallback()));
|
||||
EXPECT_FALSE(future_disable.Get());
|
||||
}
|
||||
|
||||
} // namespace power_metrics
|
Reference in New Issue
Block a user