| // Copyright 2012 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/ash/memory_metrics.h" |
| |
| #include <stddef.h> |
| |
| #include <cinttypes> |
| #include <map> |
| #include <string> |
| #include <vector> |
| |
| #include "ash/constants/ash_switches.h" |
| #include "base/command_line.h" |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/metrics/histogram.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/statistics_recorder.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/task/thread_pool.h" |
| #include "chrome/browser/metrics/chromeos_metrics_provider.h" |
| #include "chromeos/ash/components/memory/memory.h" |
| #include "components/metrics/serialization/metric_sample.h" |
| #include "components/metrics/serialization/serialization_utils.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Name of the histogram that represents the success and various failure modes |
| // for parsing PSI memory data. |
| const char kPSIMemoryPressureSomeName[] = "ChromeOS.CWP.PSIMemPressure.Some"; |
| const char kPSIMemoryPressureFullName[] = "ChromeOS.CWP.PSIMemPressure.Full"; |
| |
| // File path that stores PSI Memory data. |
| const char kPSIMemoryPath[] = "/proc/pressure/memory"; |
| |
| } // namespace |
| |
| MemoryMetrics::MemoryMetrics(uint32_t period) |
| : memory_psi_file_(kPSIMemoryPath), parser_(period) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| DETACH_FROM_SEQUENCE(background_sequence_checker_); |
| |
| zram_metrics_ = base::MakeRefCounted<memory::ZramMetrics>(); |
| |
| collection_interval_ = base::Seconds(parser_.GetPeriod()); |
| runner_ = base::ThreadPool::CreateSequencedTaskRunner( |
| {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); |
| } |
| |
| MemoryMetrics::~MemoryMetrics() = default; |
| |
| void MemoryMetrics::Start() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| // Start the collection on the background sequence. |
| runner_->PostTask(FROM_HERE, |
| base::BindOnce(&MemoryMetrics::ScheduleCollector, this)); |
| } |
| |
| void MemoryMetrics::Stop() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| // Stop the collection on the background sequence. |
| runner_->PostTask(FROM_HERE, |
| base::BindOnce(&MemoryMetrics::CancelTimer, this)); |
| } |
| |
| void MemoryMetrics::CollectEvents() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(background_sequence_checker_); |
| |
| const bool zram_success = zram_metrics_->CollectEvents(); |
| |
| std::string content; |
| int metric_some; |
| int metric_full; |
| |
| if (!base::ReadFileToString(base::FilePath(memory_psi_file_), &content)) { |
| parser_.LogParseStatus(metrics::ParsePSIMemStatus::kReadFileFailed); |
| return; |
| } |
| |
| auto stat = parser_.ParseMetrics(content, &metric_some, &metric_full); |
| parser_.LogParseStatus(stat); // Log success and failure, for histograms. |
| if (stat != metrics::ParsePSIMemStatus::kSuccess) { |
| return; |
| } |
| |
| base::UmaHistogramCustomCounts( |
| kPSIMemoryPressureSomeName, metric_some, metrics::kMemPressureMin, |
| metrics::kMemPressureExclusiveMax, metrics::kMemPressureHistogramBuckets); |
| base::UmaHistogramCustomCounts( |
| kPSIMemoryPressureFullName, metric_full, metrics::kMemPressureMin, |
| metrics::kMemPressureExclusiveMax, metrics::kMemPressureHistogramBuckets); |
| |
| // Emit a composite metric consisting of the cross product of |
| // orig_data_size_mb_ and metric_some. |
| if (zram_success) { |
| // We use exactly 15 buckets for zram, each of size 1GB except for the last |
| // which is unbounded. This means we have buckets: [0, 1), [1, 2), ..., [14, |
| // infinity). |
| constexpr int kZramBucketCount = 15; |
| int zram_bucket = zram_metrics_->orig_data_size_mb() / 1024; |
| zram_bucket = std::min(kZramBucketCount - 1, zram_bucket); |
| |
| // We use exactly 20 buckets for metric_some of width 5 between 0 and 100. |
| constexpr int kPsiBucketWidth = 5; |
| constexpr int kPsiBucketCount = 100 / kPsiBucketWidth; |
| int psi_bucket = metric_some / kPsiBucketWidth; |
| psi_bucket = std::min(kPsiBucketCount - 1, psi_bucket); |
| |
| int composite_bucket = zram_bucket * kPsiBucketCount + psi_bucket; |
| |
| UMA_HISTOGRAM_ENUMERATION("ChromeOS.Zram.PSISomeOrigDataSizeMB", |
| composite_bucket, |
| kZramBucketCount * kPsiBucketCount); |
| } |
| } |
| |
| void MemoryMetrics::ScheduleCollector() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(background_sequence_checker_); |
| timer_ = std::make_unique<base::RepeatingTimer>(); |
| timer_->Start(FROM_HERE, collection_interval_, |
| base::BindRepeating(&MemoryMetrics::CollectEvents, this)); |
| } |
| |
| void MemoryMetrics::CancelTimer() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(background_sequence_checker_); |
| timer_.reset(); |
| } |
| |
| } // namespace ash |