| // 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 |