/** * * \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 #include #include #include 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(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); }