[go: nahoru, domu]

blob: b7f5d024dd57d162a726e60b25d94e6313af40ed [file] [log] [blame]
// 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