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