You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
srsRAN_4G/lib/src/system/sys_metrics_processor.cc

263 lines
7.8 KiB
C++

/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsran/system/sys_metrics_processor.h"
#include <fstream>
#include <sstream>
#include <sys/sysinfo.h>
#include <unistd.h>
using namespace srsran;
static const uint32_t cpu_count = ::sysconf(_SC_NPROCESSORS_CONF);
static const float ticks_per_second = ::sysconf(_SC_CLK_TCK);
sys_metrics_processor::sys_metrics_processor(srslog::basic_logger& logger) : logger(logger)
{
if (cpu_count > metrics_max_supported_cpu) {
logger.warning("Number of cpu is greater than supported. CPU metrics will be disabled.");
}
}
sys_metrics_processor::proc_stats_info::proc_stats_info()
{
std::string line;
{
std::ifstream file("/proc/self/stat");
if (!file) {
return;
}
std::getline(file, line);
}
std::istringstream reader(line);
reader >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr >> tpgid >> flags >> minflt >> cminflt >>
majflt >> cmajflt >> utime >> stime >> cutime >> cstime >> priority >> nice >> num_threads >> itrealvalue >>
starttime >> vsize >> rss >> rsslim >> startcode >> endcode >> startstack >> kstkeip >> signal >> blocked >>
sigignore >> sigcatch >> wchan >> nswap >> cnswap >> exit_signal >> processor >> rt_priority >> policy >>
delaycct_blkio_ticks >> guest_time >> cguest_time >> start_data >> end_data >> start_brk >> arg_start >>
arg_end >> env_start >> env_end >> exit_code;
}
/// Returns a null sys_metrics_t with the cpu count field filled.
static sys_metrics_t create_null_metrics()
{
sys_metrics_t metrics;
if (cpu_count > metrics_max_supported_cpu) {
return metrics;
}
metrics.cpu_count = cpu_count;
metrics.cpu_load.fill(0.f);
return metrics;
}
sys_metrics_t sys_metrics_processor::get_metrics()
{
auto current_time = std::chrono::steady_clock::now();
uint32_t measure_interval_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(current_time - last_query_time).count();
// The time elapsed between 2 measures must be greater that 10 milliseconds.
if (measure_interval_ms < 10u) {
logger.info("Interval less than 10ms, skipping measurement.");
return create_null_metrics();
}
sys_metrics_t metrics;
// Get the memory metrics.
calculate_mem_usage(metrics);
// Calculate cpu metrics.
calculate_cpu_metrics(metrics, measure_interval_ms / 1000.f);
// Get the stats from the proc.
proc_stats_info current_query;
metrics.thread_count = current_query.num_threads;
metrics.process_cpu_usage = calculate_cpu_usage(current_query, measure_interval_ms / 1000.f);
// Update the last values.
last_query_time = current_time;
last_query = std::move(current_query);
return metrics;
}
float sys_metrics_processor::calculate_cpu_usage(const proc_stats_info& current_query,
float delta_time_in_seconds) const
{
// Error current value has to be greater than last value.
if (current_query.stime < last_query.stime || current_query.utime < last_query.utime) {
return -1.f;
}
// If current and last tick counter equals, means that the process didn't used CPU.
if (current_query.stime == last_query.stime && current_query.utime == last_query.utime) {
return 0.f;
}
return ((current_query.stime + current_query.utime) - (last_query.stime + last_query.utime)) * 100.f /
(cpu_count * ticks_per_second * delta_time_in_seconds);
}
sys_metrics_processor::cpu_metrics_t sys_metrics_processor::read_cpu_idle_from_line(const std::string& line) const
{
std::istringstream reader(line);
cpu_metrics_t m;
reader >> m.name >> m.user >> m.nice >> m.system >> m.idle >> m.iowait >> m.irq >> m.softirq;
return m;
}
void sys_metrics_processor::calculate_cpu_metrics(sys_metrics_t& metrics, float delta_time_in_seconds)
{
// When the number of cpu is higher than system_metrics_t supports, skip the cpu metrics.
if (cpu_count > metrics_max_supported_cpu) {
return;
}
metrics.cpu_count = cpu_count;
std::ifstream file("/proc/stat");
std::string line;
if (!file) {
return;
}
int count = -1;
while (std::getline(file, line)) {
// First line is the CPU field that contains all the cores and thread. For now, we skip this one.
if (count < 0) {
++count;
continue;
}
// Parse all the cpus.
if (line.find("cpu") != std::string::npos) {
auto tmp = read_cpu_idle_from_line(line);
auto index = count++;
if (tmp.idle < last_cpu_thread[index].idle) {
metrics.cpu_load[index] = 0.f;
continue;
}
metrics.cpu_load[index] = std::max(
(1.f - (tmp.idle - last_cpu_thread[index].idle) / (ticks_per_second * delta_time_in_seconds)) * 100.f, 0.f);
last_cpu_thread[index] = std::move(tmp);
}
}
}
/// Sets the memory parameters of the given metrics to zero.
static void set_mem_to_zero(sys_metrics_t& metrics)
{
metrics.process_realmem_kB = 0;
metrics.process_virtualmem_kB = 0;
metrics.process_realmem = 0;
metrics.system_mem = 0;
}
/// Extracts and returns the memory size from the given line.
static int32_t read_memory_value_from_line(const std::string& line)
{
std::istringstream reader(line);
std::string label, unit;
int32_t value;
reader >> label >> value >> unit;
return value;
}
static void calculate_percentage_memory(sys_metrics_t& metrics)
{
std::ifstream file("/proc/meminfo");
std::string line;
if (!file) {
set_mem_to_zero(metrics);
return;
}
struct meminfo_t {
uint32_t total_kB = 0;
uint32_t free_kB = 0;
uint32_t buffers_kB = 0;
uint32_t cached_kB = 0;
uint32_t slab_kB = 0;
};
// Retrieve the data
meminfo_t m_info;
while (std::getline(file, line)) {
// Looks for Virtual memory.
if (line.find("MemTotal:") != std::string::npos) {
m_info.total_kB = std::max(read_memory_value_from_line(line), 0);
}
if (line.find("MemFree:") != std::string::npos) {
m_info.free_kB = std::max(read_memory_value_from_line(line), 0);
}
if (line.find("Buffers:") != std::string::npos) {
m_info.buffers_kB = std::max(read_memory_value_from_line(line), 0);
}
if (line.find("Cached:") != std::string::npos) {
m_info.cached_kB = std::max(read_memory_value_from_line(line), 0);
}
if (line.find("Slab:") != std::string::npos) {
m_info.slab_kB = std::max(read_memory_value_from_line(line), 0);
}
}
// Calculate the metrics.
metrics.process_realmem = (metrics.process_realmem_kB <= m_info.total_kB)
? 100.f * (float(metrics.process_realmem_kB) / m_info.total_kB)
: 0;
metrics.system_mem =
(1.f - float(m_info.buffers_kB + m_info.cached_kB + m_info.free_kB + m_info.slab_kB) / float(m_info.total_kB)) *
100.f;
}
void sys_metrics_processor::calculate_mem_usage(sys_metrics_t& metrics) const
{
std::ifstream file("/proc/self/status");
std::string line;
if (!file) {
set_mem_to_zero(metrics);
return;
}
while (std::getline(file, line)) {
// Looks for Virtual memory.
if (line.find("VmSize:") != std::string::npos) {
// NOTE: std::max will clamp negative values to 0.
metrics.process_virtualmem_kB = std::max(read_memory_value_from_line(line), 0);
continue;
}
// Looks for physical memory.
if (line.find("VmRSS:") != std::string::npos) {
// NOTE: std::max will clamp negative values to 0.
metrics.process_realmem_kB = std::max(read_memory_value_from_line(line), 0);
continue;
}
}
// Now calculate the memory usage in percentage.
calculate_percentage_memory(metrics);
}