diff --git a/lib/include/srslte/system/sys_metrics.h b/lib/include/srslte/system/sys_metrics.h new file mode 100644 index 000000000..57fd7aa7a --- /dev/null +++ b/lib/include/srslte/system/sys_metrics.h @@ -0,0 +1,40 @@ +/** + * Copyright 2013-2021 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSLTE_SYS_METRICS_H +#define SRSLTE_SYS_METRICS_H + +namespace srslte { + +/// Metrics that will be returned in the system. +struct sys_metrics_t { + unsigned process_realmem_kB = 0; + unsigned process_virtualmem_kB = 0; + float process_realmem = -1.f; + float process_virtualmem = -1.f; + unsigned thread_count = 0; + float process_cpu_usage = -1.f; + float system_mem = -1.f; +}; + +} // namespace srslte + +#endif // SRSLTE_SYS_METRICS_H diff --git a/lib/include/srslte/system/sys_metrics_processor.h b/lib/include/srslte/system/sys_metrics_processor.h new file mode 100644 index 000000000..bdf502b54 --- /dev/null +++ b/lib/include/srslte/system/sys_metrics_processor.h @@ -0,0 +1,70 @@ +/** + * Copyright 2013-2021 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSLTE_SYS_METRICS_PROCESSOR_H +#define SRSLTE_SYS_METRICS_PROCESSOR_H + +#include "srslte/system/sys_metrics.h" +#include +#include + +namespace srslte { + +/// Process the data from the proc_stats_info. +class sys_metrics_processor +{ + /// Contains the information of a process. + struct proc_stats_info { + proc_stats_info(); + + /// Parsed every field in /proc/[pid]/stats file. + int pid, ppid, pgrp, session, tty_nr, tpgid, exit_signal, processor, exit_code; + unsigned flags, rt_priority, policy; + unsigned long minflt, cminflt, majflt, cmajflt, utime, stime, vsize, rsslim, startcode, endcode, startstack, + kstkesp, kstkeip, signal, blocked, sigignore, sigcatch, wchan, nswap, cnswap, guest_time, start_data, end_data, + start_brk, arg_start, arg_end, env_start, env_end; + int long cutime, cstime, priority, nice, num_threads, itrealvalue, rss, cguest_time; + unsigned long long starttime, delaycct_blkio_ticks; + char state; + std::string comm; + }; + +public: + /// Performs a new measure and returns the values. + sys_metrics_t get_metrics(); + +private: + /// Returns the cpu usage in %. current_query is the most recent proc_stats_info, and delta_time_in_seconds is the + /// elapsed time between the last measure and current in seconds. + /// NOTE: Returns -1.0f on error. + float cpu_usage(const proc_stats_info& current_query, float delta_time_in_seconds) const; + + /// Returns the memory usage in kB. First element is the real mem whereas the second is the virtual mem. + void mem_usage(sys_metrics_t& metrics) const; + +private: + proc_stats_info last_query; + std::chrono::time_point last_query_time = std::chrono::steady_clock::now(); +}; + +} // namespace srslte + +#endif // SRSLTE_SYS_METRICS_PROCESSOR_H diff --git a/lib/src/CMakeLists.txt b/lib/src/CMakeLists.txt index 840617222..1f47129c9 100644 --- a/lib/src/CMakeLists.txt +++ b/lib/src/CMakeLists.txt @@ -12,4 +12,5 @@ add_subdirectory(mac) add_subdirectory(phy) add_subdirectory(radio) add_subdirectory(srslog) +add_subdirectory(system) add_subdirectory(upper) diff --git a/lib/src/system/CMakeLists.txt b/lib/src/system/CMakeLists.txt new file mode 100644 index 000000000..5ba318d19 --- /dev/null +++ b/lib/src/system/CMakeLists.txt @@ -0,0 +1,15 @@ +# +# Copyright 2013-2020 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. +# + +set(SOURCES + sys_metrics_processor.cpp) + +find_package(Threads REQUIRED) + +add_library(system STATIC ${SOURCES}) +target_link_libraries(system "${CMAKE_THREAD_LIBS_INIT}") diff --git a/lib/src/system/sys_metrics_processor.cpp b/lib/src/system/sys_metrics_processor.cpp new file mode 100644 index 000000000..61c8e6f5b --- /dev/null +++ b/lib/src/system/sys_metrics_processor.cpp @@ -0,0 +1,147 @@ +/** + * Copyright 2013-2021 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srslte/system/sys_metrics_processor.h" +#include "sys/sysinfo.h" +#include +#include +#include + +using namespace srslte; + +sys_metrics_processor::proc_stats_info::proc_stats_info() +{ + std::string line; + { + std::ifstream file("/proc/self/stat"); + 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; +} + +sys_metrics_t sys_metrics_processor::get_metrics() +{ + auto current_time = std::chrono::steady_clock::now(); + unsigned measure_interval_ms = + std::chrono::duration_cast(current_time - last_query_time).count(); + + // The time elapsed between 2 measures must be greater that 100 milliseconds. + if (measure_interval_ms < 100u) { + return {}; + } + + sys_metrics_t metrics; + + // Get the memory metrics. + mem_usage(metrics); + + // Get the stats from the proc. + proc_stats_info current_query; + metrics.thread_count = current_query.num_threads; + metrics.process_cpu_usage = 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::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; + } + + static const unsigned cpu_count = ::sysconf(_SC_NPROCESSORS_CONF); + static const float ticks_per_second = ::sysconf(_SC_CLK_TCK); + + printf("delta time is: %f\n", delta_time_in_seconds); + return ((current_query.stime + current_query.utime) - (last_query.stime + last_query.utime)) * 100.f / + (cpu_count * ticks_per_second * delta_time_in_seconds); +} + +/// Extracts and returns the memory size from the given line. +static unsigned read_memory_value_from_line(const std::string& line) +{ + std::istringstream reader(line); + std::string label, unit; + unsigned value; + + reader >> label >> value >> unit; + + return value; +} + +static void calculate_percentage_memory(sys_metrics_t& metrics) +{ + std::ifstream file("/proc/meminfo"); + std::string line; + + // Total system's memory is in the first line. + std::getline(file, line); + unsigned long long total_mem_kB = read_memory_value_from_line(line); + + // System's available memory is in the third line. + std::getline(file, line); + std::getline(file, line); + unsigned long long available_mem_kB = read_memory_value_from_line(line); + + // Calculate the metrics. + metrics.process_realmem = 100.f * (float(metrics.process_realmem_kB) / total_mem_kB); + metrics.process_virtualmem = 100.f * (float(metrics.process_virtualmem) / total_mem_kB); + metrics.system_mem = (1.f - float(available_mem_kB) / float(total_mem_kB)) * 100.f; +} + +void sys_metrics_processor::mem_usage(sys_metrics_t& metrics) const +{ + std::ifstream file("/proc/self/status"); + std::string line; + + while (std::getline(file, line)) { + // Looks for Virtual memory. + if (line.find("VmSize:") != std::string::npos) { + metrics.process_virtualmem_kB = read_memory_value_from_line(line); + continue; + } + // Looks for physical memory. + if (line.find("VmRSS:") != std::string::npos) { + metrics.process_realmem_kB = read_memory_value_from_line(line); + continue; + } + } + + // Now calculate the memory usage in percentage. + calculate_percentage_memory(metrics); +} diff --git a/srsue/hdr/ue.h b/srsue/hdr/ue.h index c0ea7b489..9f7e4d266 100644 --- a/srsue/hdr/ue.h +++ b/srsue/hdr/ue.h @@ -28,6 +28,7 @@ #include "srslte/common/log_filter.h" #include "srslte/radio/radio.h" #include "srslte/srslog/srslog.h" +#include "srslte/system/sys_metrics_processor.h" #include "stack/ue_stack_base.h" #include "ue_metrics_interface.h" @@ -108,6 +109,9 @@ private: srslte::logger* old_logger = nullptr; srslog::basic_logger& logger; + // System metrics processor. + srslte::sys_metrics_processor sys_proc; + all_args_t args; // Helper functions diff --git a/srsue/hdr/ue_metrics_interface.h b/srsue/hdr/ue_metrics_interface.h index e28af6f05..d5bd6f9a2 100644 --- a/srsue/hdr/ue_metrics_interface.h +++ b/srsue/hdr/ue_metrics_interface.h @@ -18,6 +18,7 @@ #include "phy/phy_metrics.h" #include "srslte/common/metrics_hub.h" #include "srslte/radio/radio_metrics.h" +#include "srslte/system/sys_metrics.h" #include "srslte/upper/rlc_metrics.h" #include "stack/mac/mac_metrics.h" #include "stack/rrc/rrc_metrics.h" @@ -35,10 +36,11 @@ typedef struct { } stack_metrics_t; typedef struct { - srslte::rf_metrics_t rf; - phy_metrics_t phy; - gw_metrics_t gw; - stack_metrics_t stack; + srslte::rf_metrics_t rf; + phy_metrics_t phy; + gw_metrics_t gw; + stack_metrics_t stack; + srslte::sys_metrics_t sys; } ue_metrics_t; // UE interface diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 6266394a6..6ab94f1d0 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -20,8 +20,8 @@ endif (RPATH) add_executable(srsue main.cc ue.cc metrics_stdout.cc metrics_csv.cc) -set(SRSUE_SOURCES srsue_phy srsue_stack srsue_upper srsue_mac srsue_rrc srslog) -set(SRSLTE_SOURCES srslte_common srslte_mac srslte_phy srslte_radio srslte_upper rrc_asn1 srslog) +set(SRSUE_SOURCES srsue_phy srsue_stack srsue_upper srsue_mac srsue_rrc srslog system) +set(SRSLTE_SOURCES srslte_common srslte_mac srslte_phy srslte_radio srslte_upper rrc_asn1 srslog system) set(SRSUE_SOURCES ${SRSUE_SOURCES} srsue_nr_stack srsue_rrc_nr srsue_mac_nr) set(SRSLTE_SOURCES ${SRSLTE_SOURCES} rrc_nr_asn1 ngap_nr_asn1) diff --git a/srsue/src/metrics_stdout.cc b/srsue/src/metrics_stdout.cc index 86a14afb7..6c645521b 100644 --- a/srsue/src/metrics_stdout.cc +++ b/srsue/src/metrics_stdout.cc @@ -64,13 +64,18 @@ void metrics_stdout::toggle_print(bool b) void metrics_stdout::print_table(const bool display_neighbours) { if (display_neighbours) { - cout << "--------Signal-------------Neighbour--DL-------------------------------------UL----------------------" + cout << "--------Signal-------------Neighbour--DL-------------------------------------UL-----------------------|---" + "--SYS-----" << endl; - cout << "cc pci rsrp pl cfo pci rsrp mcs snr turbo brate bler ta_us mcs buff brate bler" + cout << "cc pci rsrp pl cfo pci rsrp mcs snr turbo brate bler ta_us mcs buff brate bler " + "cpu mem" << endl; } else { - cout << "--------Signal--------------DL-------------------------------------UL----------------------" << endl; - cout << "cc pci rsrp pl cfo mcs snr turbo brate bler ta_us mcs buff brate bler" << endl; + cout << "--------Signal--------------DL-------------------------------------UL----------------------|-----SYS----" + "--" + << endl; + cout << "cc pci rsrp pl cfo mcs snr turbo brate bler ta_us mcs buff brate bler cpu mem" + << endl; } table_has_neighbours = display_neighbours; n_reports = 0; @@ -162,6 +167,10 @@ void metrics_stdout::set_metrics(const ue_metrics_t& metrics, const uint32_t per } else { cout << float_to_string(0, 1) << "%"; } + + // Write the system metrics. + cout << float_to_string(metrics.sys.process_cpu_usage, 2); + cout << float_to_string(metrics.sys.system_mem, 2); cout << endl; } diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index cecf36096..72e5ebe4c 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -328,6 +328,7 @@ bool ue::get_metrics(ue_metrics_t* m) radio->get_metrics(&m->rf); stack->get_metrics(&m->stack); gw_inst->get_metrics(m->gw, m->stack.mac[0].nof_tti); + m->sys = sys_proc.get_metrics(); return true; }