blob: 3f636623d15374c9cf33bc237370554fb5abea23 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/crostini/crosvm_metrics.h"
#include <unistd.h>
#include <cmath>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/ash/crostini/crosvm_process_list.h"
namespace crostini {
namespace {
constexpr char kCrosvmProcessesHistogram[] = "Crostini.Crosvm.Processes.Count";
constexpr char kCrosvmCpuPercentageHistogram[] =
"Crostini.Crosvm.CpuPercentage";
constexpr char kCrosvmRssPercentageHistogram[] =
"Crostini.Crosvm.RssPercentage";
constexpr base::TimeDelta kCrosvmMetricsInterval =
base::TimeDelta::FromMinutes(10);
} // namespace
CrosvmMetrics::CrosvmMetrics()
: task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
page_size_(sysconf(_SC_PAGESIZE)) {}
CrosvmMetrics::~CrosvmMetrics() = default;
void CrosvmMetrics::Start() {
task_runner_->PostNonNestableTask(
FROM_HERE, base::BindOnce(&CrosvmMetrics::CollectCycleStartData,
weak_ptr_factory_.GetWeakPtr()));
timer_.Start(FROM_HERE, kCrosvmMetricsInterval, this,
&CrosvmMetrics::MetricsCycleCallback);
}
void CrosvmMetrics::CollectCycleStartData() {
previous_pid_stat_map_ = GetCrosvmPidStatMap();
absl::optional<int64_t> total_cpu_time = ash::system::GetCpuTimeJiffies();
if (!total_cpu_time.has_value()) {
cycle_start_data_collected_ = false;
return;
}
previous_total_cpu_time_ = total_cpu_time.value();
cycle_start_data_collected_ = true;
}
// static
int CrosvmMetrics::CalculateCrosvmRssPercentage(const PidStatMap& pid_stat_map,
int64_t mem_used,
int64_t page_size) {
int64_t total_rss = 0;
for (const auto& pair : pid_stat_map) {
total_rss += pair.second.rss;
}
return std::lround(static_cast<double>(total_rss) /
(mem_used * 1024 / page_size) * 100);
}
// static
int CrosvmMetrics::CalculateCrosvmCpuPercentage(
const PidStatMap& pid_stat_map,
const PidStatMap& previous_pid_stat_map,
int64_t cycle_cpu_time) {
int64_t total_cpu_time = 0;
for (const auto& pair : pid_stat_map) {
auto it = previous_pid_stat_map.find(pair.first);
if (it == previous_pid_stat_map.end()) {
total_cpu_time += pair.second.utime + pair.second.stime;
} else {
total_cpu_time += (pair.second.utime + pair.second.stime) -
(it->second.utime + it->second.stime);
}
}
return std::lround(static_cast<double>(total_cpu_time) / cycle_cpu_time *
100);
}
void CrosvmMetrics::MetricsCycleCallback() {
task_runner_->PostNonNestableTask(
FROM_HERE, base::BindOnce(&CrosvmMetrics::MetricsCycle,
weak_ptr_factory_.GetWeakPtr()));
}
void CrosvmMetrics::MetricsCycle() {
if (!cycle_start_data_collected_) {
CollectCycleStartData();
return;
}
PidStatMap pid_stat_map = GetCrosvmPidStatMap();
absl::optional<int64_t> total_cpu_time = ash::system::GetCpuTimeJiffies();
if (!total_cpu_time.has_value()) {
cycle_start_data_collected_ = false;
return;
}
int64_t cycle_cpu_time = total_cpu_time.value() - previous_total_cpu_time_;
absl::optional<int64_t> mem_used = ash::system::GetUsedMemTotalKB();
if (!mem_used.has_value()) {
cycle_start_data_collected_ = false;
return;
}
if (pid_stat_map.empty()) {
previous_pid_stat_map_ = pid_stat_map;
previous_total_cpu_time_ = total_cpu_time.value();
return;
}
int rss_percentage =
CalculateCrosvmRssPercentage(pid_stat_map, mem_used.value(), page_size_);
int cpu_percentage = CalculateCrosvmCpuPercentage(
pid_stat_map, previous_pid_stat_map_, cycle_cpu_time);
UMA_HISTOGRAM_COUNTS_100(kCrosvmProcessesHistogram, pid_stat_map.size());
UMA_HISTOGRAM_PERCENTAGE(kCrosvmCpuPercentageHistogram, cpu_percentage);
UMA_HISTOGRAM_PERCENTAGE(kCrosvmRssPercentageHistogram, rss_percentage);
previous_pid_stat_map_ = pid_stat_map;
previous_total_cpu_time_ = total_cpu_time.value();
}
} // namespace crostini