blob: ec0a92e2746dfdaad12cbfe8044fe132c712fa71 [file] [log] [blame]
// Copyright 2021 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/system_cpu/cpu_probe_linux.h"
#include <stdint.h>
#include <utility>
#include <vector>
#include "base/check_op.h"
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "components/system_cpu/core_times.h"
#include "components/system_cpu/cpu_sample.h"
#include "components/system_cpu/procfs_stat_cpu_parser.h"
namespace system_cpu {
// Helper class that performs the actual I/O. It must run on a
// SequencedTaskRunner that is properly configured for blocking I/O
// operations.
class CpuProbeLinux::BlockingTaskRunnerHelper final {
public:
explicit BlockingTaskRunnerHelper(base::FilePath procfs_stat_path);
~BlockingTaskRunnerHelper();
BlockingTaskRunnerHelper(const BlockingTaskRunnerHelper&) = delete;
BlockingTaskRunnerHelper& operator=(const BlockingTaskRunnerHelper&) = delete;
std::optional<CpuSample> Update();
private:
// Called when a core is seen the first time in /proc/stat.
//
// For most systems, the cores listed in /proc/stat are static. However, it
// is theoretically possible for cores to go online and offline.
void InitializeCore(size_t, const CoreTimes&);
SEQUENCE_CHECKER(sequence_checker_);
// /proc/stat parser. Used to derive CPU utilization.
ProcfsStatCpuParser stat_parser_ GUARDED_BY_CONTEXT(sequence_checker_);
// Most recent per-core times from /proc/stat.
std::vector<CoreTimes> last_per_core_times_
GUARDED_BY_CONTEXT(sequence_checker_);
};
CpuProbeLinux::BlockingTaskRunnerHelper::BlockingTaskRunnerHelper(
base::FilePath procfs_stat_path)
: stat_parser_(std::move(procfs_stat_path)) {}
CpuProbeLinux::BlockingTaskRunnerHelper::~BlockingTaskRunnerHelper() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
std::optional<CpuSample> CpuProbeLinux::BlockingTaskRunnerHelper::Update() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!stat_parser_.Update()) {
return std::nullopt;
}
const std::vector<CoreTimes>& per_core_times = stat_parser_.core_times();
double utilization_sum = 0.0;
int utilization_cores = 0;
for (size_t i = 0; i < per_core_times.size(); ++i) {
CHECK_GE(last_per_core_times_.size(), i);
const CoreTimes& core_times = per_core_times[i];
if (last_per_core_times_.size() == i) {
InitializeCore(i, core_times);
continue;
}
double core_utilization =
core_times.TimeUtilization(last_per_core_times_[i]);
if (core_utilization >= 0) {
// Only overwrite `last_per_core_times_` if the /proc/stat counters are
// monotonically increasing. Otherwise, discard the measurement.
last_per_core_times_[i] = core_times;
utilization_sum += core_utilization;
++utilization_cores;
}
}
if (utilization_cores > 0) {
return CpuSample{.cpu_utilization = utilization_sum / utilization_cores};
} else {
return std::nullopt;
}
}
void CpuProbeLinux::BlockingTaskRunnerHelper::InitializeCore(
size_t core_index,
const CoreTimes& initial_core_times) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK_EQ(last_per_core_times_.size(), core_index);
last_per_core_times_.push_back(initial_core_times);
}
// static
std::unique_ptr<CpuProbeLinux> CpuProbeLinux::Create() {
return base::WrapUnique(
new CpuProbeLinux(base::FilePath(ProcfsStatCpuParser::kProcfsStatPath)));
}
CpuProbeLinux::CpuProbeLinux(base::FilePath procfs_stat_path) {
helper_ = base::SequenceBound<BlockingTaskRunnerHelper>(
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}),
std::move(procfs_stat_path));
}
CpuProbeLinux::~CpuProbeLinux() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void CpuProbeLinux::Update(SampleCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
helper_.AsyncCall(&BlockingTaskRunnerHelper::Update)
.Then(std::move(callback));
}
base::WeakPtr<CpuProbe> CpuProbeLinux::GetWeakPtr() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return weak_factory_.GetWeakPtr();
}
} // namespace system_cpu