| // 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 "chrome/browser/metrics/pressure/pressure_metrics_reporter.h" |
| |
| #include <vector> |
| |
| #include "base/task/thread_pool.h" |
| #include "base/trace_event/trace_event.h" |
| |
| namespace { |
| |
| // Histograms names for the pressure metrics. |
| const char kCPUPressureHistogramName[] = "System.Pressure.CPU"; |
| const char kIOPressureHistogramName[] = "System.Pressure.IO"; |
| const char kMemoryPressureHistogramName[] = "System.Pressure.Memory"; |
| |
| // Paths for the pressure metrics (Pressure Stall Information). |
| const base::FilePath::CharType kCPUPressureFilePath[] = |
| FILE_PATH_LITERAL("/proc/pressure/cpu"); |
| const base::FilePath::CharType kIOPressureFilePath[] = |
| FILE_PATH_LITERAL("/proc/pressure/io"); |
| const base::FilePath::CharType kMemoryPressureFilePath[] = |
| FILE_PATH_LITERAL("/proc/pressure/memory"); |
| |
| // The time to wait between UMA samples. |
| constexpr base::TimeDelta kDelayBetweenSamples = base::Minutes(10); |
| |
| // The time to wait between trace counters. |
| constexpr base::TimeDelta kDelayBetweenTracingSamples = base::Seconds(1); |
| |
| // Tracing caterogies to emit the memory pressure related trace events. |
| const char kTraceCategoryForPressureEvents[] = "resources"; |
| |
| } // anonymous namespace |
| |
| class PressureMetricsWorker { |
| public: |
| PressureMetricsWorker(); |
| ~PressureMetricsWorker(); |
| |
| void OnTracingEnabled(); |
| void OnTracingDisabled(); |
| |
| private: |
| void ReadAndEmitCounters(); |
| void ReadAndEmitUMA(); |
| |
| std::vector<PressureMetrics> pressure_metrics_; |
| base::RepeatingTimer metrics_sampling_timer_; |
| base::RepeatingTimer tracing_sampling_timer_; |
| }; |
| |
| PressureMetricsWorker::PressureMetricsWorker() { |
| pressure_metrics_.emplace_back(kCPUPressureHistogramName, |
| base::FilePath(kCPUPressureFilePath)); |
| pressure_metrics_.emplace_back(kIOPressureHistogramName, |
| base::FilePath(kIOPressureFilePath)); |
| pressure_metrics_.emplace_back(kMemoryPressureHistogramName, |
| base::FilePath(kMemoryPressureFilePath)); |
| |
| // It is safe to use base::Unretained(this) since the timer is own by this |
| // class. |
| metrics_sampling_timer_.Start( |
| FROM_HERE, kDelayBetweenSamples, |
| base::BindRepeating(&PressureMetricsWorker::ReadAndEmitUMA, |
| base::Unretained(this))); |
| } |
| |
| PressureMetricsWorker::~PressureMetricsWorker() = default; |
| |
| void PressureMetricsWorker::OnTracingEnabled() { |
| const uint8_t* category_enabled = TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED( |
| kTraceCategoryForPressureEvents); |
| if (*category_enabled) { |
| // It is safe to use base::Unretained(this) since the timer is own by this |
| // class. |
| tracing_sampling_timer_.Start( |
| FROM_HERE, kDelayBetweenTracingSamples, |
| base::BindRepeating(&PressureMetricsWorker::ReadAndEmitCounters, |
| base::Unretained(this))); |
| } |
| } |
| |
| void PressureMetricsWorker::OnTracingDisabled() { |
| tracing_sampling_timer_.Stop(); |
| } |
| |
| void PressureMetricsWorker::ReadAndEmitCounters() { |
| for (const auto& metric : pressure_metrics_) { |
| absl::optional<PressureMetrics::Sample> current_pressure = |
| metric.CollectCurrentPressure(); |
| if (current_pressure.has_value()) { |
| metric.EmitCounters(current_pressure.value()); |
| } |
| } |
| } |
| |
| void PressureMetricsWorker::ReadAndEmitUMA() { |
| for (const auto& metric : pressure_metrics_) { |
| absl::optional<PressureMetrics::Sample> current_pressure = |
| metric.CollectCurrentPressure(); |
| if (current_pressure.has_value()) { |
| metric.ReportToUMA(current_pressure.value()); |
| } |
| } |
| } |
| |
| PressureMetricsReporter::PressureMetricsReporter() |
| : worker_(base::ThreadPool::CreateSequencedTaskRunner( |
| {base::TaskPriority::BEST_EFFORT, base::MayBlock(), |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| base::trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this); |
| // It is possible with startup tracing that tracing was enabled before this |
| // class has register its observer. |
| base::trace_event::TraceLog* trace_log = |
| base::trace_event::TraceLog::GetInstance(); |
| if (trace_log->IsEnabled()) { |
| OnTraceLogEnabled(); |
| } |
| } |
| |
| PressureMetricsReporter::~PressureMetricsReporter() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(this); |
| } |
| |
| void PressureMetricsReporter::OnTraceLogEnabled() { |
| worker_.AsyncCall(&PressureMetricsWorker::OnTracingEnabled); |
| } |
| |
| void PressureMetricsReporter::OnTraceLogDisabled() { |
| worker_.AsyncCall(&PressureMetricsWorker::OnTracingDisabled); |
| } |