blob: db37bef4644735836403d4a4904e16d0e6b63914 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 "src/cobalt/bin/system-metrics/cpu_stats_fetcher_impl.h"
#include <fcntl.h>
#include <fuchsia/cobalt/cpp/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/fdio/fdio.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/trace/event.h>
#include <lib/zx/resource.h>
#include <zircon/status.h>
namespace cobalt {
CpuStatsFetcherImpl::CpuStatsFetcherImpl() { InitializeKernelStats(); }
FetchCpuResult CpuStatsFetcherImpl::FetchCpuPercentage(double *cpu_percentage) {
TRACE_DURATION("system_metrics", "CpuStatsFetcherImpl::FetchCpuPercentage");
if (FetchCpuStats() == false) {
return FetchCpuResult::Error;
}
bool success = CalculateCpuPercentage(cpu_percentage);
last_cpu_stats_buffer_.swap(cpu_stats_buffer_);
last_cpu_stats_ = cpu_stats_;
last_cpu_fetch_time_ = cpu_fetch_time_;
return success ? FetchCpuResult::Ok : FetchCpuResult::FirstDataPoint;
}
bool CpuStatsFetcherImpl::FetchCpuStats() {
if (stats_service_ == nullptr) {
FX_LOGS(ERROR) << "CpuStatsFetcherImpl: No kernel stats service "
<< "present. Reconnecting...";
InitializeKernelStats();
return false;
}
cpu_fetch_time_ = std::chrono::high_resolution_clock::now();
auto result = stats_service_->GetCpuStats(cpu_stats_buffer_->view());
if (result.status() != ZX_OK) {
FX_LOGS(ERROR) << "CpuStatsFetcherImpl: Fetching "
<< "CpuStats through fuchsia.kernel.Stats returns "
<< zx_status_get_string(result.status());
return false;
}
cpu_stats_ = &result->stats;
if (cpu_stats_->actual_num_cpus < cpu_stats_->per_cpu_stats.count()) {
FX_LOGS(WARNING) << "CpuStatsFetcherImpl: actual CPUs reported " << cpu_stats_->actual_num_cpus
<< " is less than available CPUs " << cpu_stats_->per_cpu_stats.count();
return false;
}
if (num_cpu_cores_ == 0) {
num_cpu_cores_ = cpu_stats_->actual_num_cpus;
}
return true;
}
bool CpuStatsFetcherImpl::CalculateCpuPercentage(double *cpu_percentage) {
if (last_cpu_stats_ == nullptr) {
return false;
}
auto elapsed_time =
std::chrono::duration_cast<std::chrono::nanoseconds>(cpu_fetch_time_ - last_cpu_fetch_time_)
.count();
double cpu_percentage_sum = 0;
for (size_t i = 0; i < num_cpu_cores_; i++) {
zx_duration_t delta_idle_time = zx_duration_sub_duration(
cpu_stats_->per_cpu_stats[i].idle_time(), last_cpu_stats_->per_cpu_stats[i].idle_time());
zx_duration_t delta_busy_time =
(delta_idle_time > elapsed_time ? 0 : elapsed_time - delta_idle_time);
cpu_percentage_sum +=
static_cast<double>(delta_busy_time) * 100 / static_cast<double>(elapsed_time);
}
*cpu_percentage = cpu_percentage_sum / static_cast<double>(num_cpu_cores_);
TRACE_COUNTER("system_metrics", "cpu_usage", 0, "average_cpu_percentage", *cpu_percentage);
return true;
}
// TODO(fxbug.dev/4571) When Component Stats (CS) supports cpu metrics,
// switch to Component Stats / iquery, by creating a new class with the
// interface CpuStatsFetcher.
void CpuStatsFetcherImpl::InitializeKernelStats() {
zx::channel local, remote;
zx_status_t status = zx::channel::create(0, &local, &remote);
if (status != ZX_OK) {
return;
}
static const char kKernelStatsSvc[] = "/svc/fuchsia.kernel.Stats";
status = fdio_service_connect(kKernelStatsSvc, remote.release());
if (status != ZX_OK) {
FX_LOGS(ERROR) << "Cobalt SystemMetricsDaemon: Error getting kernel stats. "
<< "Cannot open fuchsia.kernel.Stats: " << zx_status_get_string(status);
return;
}
cpu_stats_buffer_ =
std::make_unique<fidl::Buffer<llcpp::fuchsia::kernel::Stats::GetCpuStatsResponse>>();
last_cpu_stats_buffer_ =
std::make_unique<fidl::Buffer<llcpp::fuchsia::kernel::Stats::GetCpuStatsResponse>>();
stats_service_ = std::make_unique<llcpp::fuchsia::kernel::Stats::SyncClient>(std::move(local));
}
} // namespace cobalt