From 167012492693715e0ef1d89663feb7b3484f5cfc Mon Sep 17 00:00:00 2001 From: faluco Date: Tue, 20 Apr 2021 10:46:27 +0200 Subject: [PATCH] Implement UE metrics in JSON format. Added config options to control this feature. --- srsue/hdr/metrics_json.h | 42 +++++++ srsue/hdr/ue.h | 2 + srsue/src/CMakeLists.txt | 2 +- srsue/src/main.cc | 21 ++++ srsue/src/metrics_json.cc | 229 ++++++++++++++++++++++++++++++++++++++ srsue/ue.conf.example | 34 +++--- 6 files changed, 315 insertions(+), 15 deletions(-) create mode 100644 srsue/hdr/metrics_json.h create mode 100644 srsue/src/metrics_json.cc diff --git a/srsue/hdr/metrics_json.h b/srsue/hdr/metrics_json.h new file mode 100644 index 000000000..fcd863b6a --- /dev/null +++ b/srsue/hdr/metrics_json.h @@ -0,0 +1,42 @@ +/** + * + * \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. + * + */ + +/****************************************************************************** + * File: metrics_json.h + * Description: Metrics class printing to a json file. + *****************************************************************************/ + +#ifndef SRSUE_METRICS_JSON_H +#define SRSUE_METRICS_JSON_H + +#include "srsran/srslog/log_channel.h" +#include "ue_metrics_interface.h" + +namespace srsue { + +class metrics_json : public srsran::metrics_listener +{ +public: + explicit metrics_json(srslog::log_channel& c) : log_c(c) {} + + void set_metrics(const ue_metrics_t& m, const uint32_t period_usec) override; + void set_ue_handle(ue_metrics_interface* ue_); + void stop() override {} + +private: + srslog::log_channel& log_c; + ue_metrics_interface* ue = nullptr; +}; + +} // namespace srsue + +#endif // SRSUE_METRICS_JSON_H diff --git a/srsue/hdr/ue.h b/srsue/hdr/ue.h index 3f93425bb..46bf85c01 100644 --- a/srsue/hdr/ue.h +++ b/srsue/hdr/ue.h @@ -61,6 +61,8 @@ typedef struct { bool metrics_csv_append; int metrics_csv_flush_period_sec; std::string metrics_csv_filename; + bool metrics_json_enable; + std::string metrics_json_filename; bool tracing_enable; std::string tracing_filename; std::size_t tracing_buffcapacity; diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 63c55fbc5..6b8d0e653 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -18,7 +18,7 @@ if (RPATH) set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) endif (RPATH) -add_executable(srsue main.cc ue.cc metrics_stdout.cc metrics_csv.cc) +add_executable(srsue main.cc ue.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc) set(SRSUE_SOURCES srsue_phy srsue_stack srsue_upper srsue_mac srsue_rrc srslog system) set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_radio srsran_upper rrc_asn1 srslog system) diff --git a/srsue/src/main.cc b/srsue/src/main.cc index 215b25b9a..ab5e43cd7 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -20,6 +20,7 @@ #include "srsran/srsran.h" #include "srsran/version.h" #include "srsue/hdr/metrics_csv.h" +#include "srsue/hdr/metrics_json.h" #include "srsue/hdr/metrics_stdout.h" #include "srsue/hdr/ue.h" #include @@ -429,6 +430,14 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) bpo::value(&args->general.metrics_csv_flush_period_sec)->default_value(-1), "Periodicity in s to flush CSV file to disk (-1 for auto)") + ("general.metrics_json_enable", + bpo::value(&args->general.metrics_json_enable)->default_value(false), + "Write UE metrics to a JSON file") + + ("general.metrics_json_filename", + bpo::value(&args->general.metrics_json_filename)->default_value("/tmp/ue_metrics.json"), + "Metrics JSON filename") + ("general.tracing_enable", bpo::value(&args->general.tracing_enable)->default_value(false), "Events tracing") @@ -704,6 +713,18 @@ int main(int argc, char* argv[]) } } + // Set up the JSON log channel used by metrics. + srslog::sink& json_sink = + srslog::fetch_file_sink(args.general.metrics_json_filename, 0, srslog::create_json_formatter()); + srslog::log_channel& json_channel = srslog::fetch_log_channel("JSON_channel", json_sink, {}); + json_channel.set_enabled(args.general.metrics_json_enable); + + srsue::metrics_json json_metrics(json_channel); + if (args.general.metrics_json_enable) { + metricshub.add_listener(&json_metrics); + json_metrics.set_ue_handle(&ue); + } + pthread_t input; pthread_create(&input, nullptr, &input_loop, &args); diff --git a/srsue/src/metrics_json.cc b/srsue/src/metrics_json.cc new file mode 100644 index 000000000..a012a8a74 --- /dev/null +++ b/srsue/src/metrics_json.cc @@ -0,0 +1,229 @@ +/** + * + * \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 "srsue/hdr/metrics_json.h" +#include "srsran/srslog/context.h" + +using namespace srsue; + +void metrics_json::set_ue_handle(ue_metrics_interface* ue_) +{ + ue = ue_; +} + +namespace { + +/// Shared metrics between containers. +DECLARE_METRIC("pci", metric_pci, uint32_t, ""); +DECLARE_METRIC("dl_bitrate", metric_dl_brate, float, ""); +DECLARE_METRIC("ul_bitrate", metric_ul_brate, float, ""); +DECLARE_METRIC("rsrp", metric_rsrp, float, ""); + +/// MAC container. +DECLARE_METRIC("dl_bler", metric_dl_bler, float, ""); +DECLARE_METRIC("ul_bler", metric_ul_bler, float, ""); +DECLARE_METRIC("ul_buff", metric_ul_buff, uint32_t, ""); +DECLARE_METRIC_SET("mac_container", + mset_mac_container, + metric_dl_brate, + metric_dl_bler, + metric_ul_brate, + metric_ul_bler, + metric_ul_buff); + +/// Carrier container. +DECLARE_METRIC("earfcn", metric_earfcn, uint32_t, ""); +DECLARE_METRIC("pathloss", metric_pathloss, float, ""); +DECLARE_METRIC("cfo", metric_cfo, float, ""); +DECLARE_METRIC("dl_snr", metric_dl_snr, float, ""); +DECLARE_METRIC("dl_mcs", metric_dl_mcs, float, ""); +DECLARE_METRIC("ul_mcs", metric_ul_mcs, float, ""); +DECLARE_METRIC("ul_ta", metric_ul_ta, float, ""); +DECLARE_METRIC("distance_km", metric_distance_km, float, ""); +DECLARE_METRIC("speed_kmph", metric_speed_kmph, float, ""); +DECLARE_METRIC_SET("carrier_container", + mset_carrier_container, + metric_earfcn, + metric_pci, + metric_rsrp, + metric_pathloss, + metric_cfo, + metric_dl_snr, + metric_dl_mcs, + metric_ul_mcs, + metric_ul_ta, + metric_distance_km, + metric_speed_kmph, + mset_mac_container); +DECLARE_METRIC_LIST("carrier_list", mlist_carriers, std::vector); + +/// GW container. +DECLARE_METRIC_SET("gw_container", mset_gw_container, metric_dl_brate, metric_ul_brate); + +/// RRC container. +DECLARE_METRIC("rrc_state", metric_rrc_state, std::string, ""); +DECLARE_METRIC_SET("rrc_container", mset_rrc_container, metric_rrc_state); + +/// Neighbour cell list. +DECLARE_METRIC_SET("neighbour_cell_container", mset_neighbour_cell_container, metric_pci, metric_rsrp, metric_cfo); +DECLARE_METRIC_LIST("neighbour_cell_list", mlist_neighbours, std::vector); + +/// NAS container. +DECLARE_METRIC("emm_state", metric_emm_state, std::string, ""); +DECLARE_METRIC_SET("nas_container", mset_nas_container, metric_emm_state); + +/// RF container. +DECLARE_METRIC("rf_o", metric_rf_o, uint32_t, ""); +DECLARE_METRIC("rf_u", metric_rf_u, uint32_t, ""); +DECLARE_METRIC("rf_l", metric_rf_l, uint32_t, ""); +DECLARE_METRIC_SET("rf_container", mset_rf_container, metric_rf_o, metric_rf_u, metric_rf_l); + +/// System memory container. +DECLARE_METRIC("proc_realmem_percent", metric_proc_rmem_percent, uint32_t, ""); +DECLARE_METRIC("proc_realmem_kB", metric_proc_rmem_kB, uint32_t, ""); +DECLARE_METRIC("proc_vmem_kB", metric_proc_vmem_kB, uint32_t, ""); +DECLARE_METRIC("sys_mem_usage_percent", metric_sys_mem_percent, uint32_t, ""); +DECLARE_METRIC_SET("sys_memory_container", + mset_sys_mem_container, + metric_proc_rmem_percent, + metric_proc_rmem_kB, + metric_proc_vmem_kB, + metric_sys_mem_percent); + +/// System CPU container +DECLARE_METRIC("proc_cpu_usage", metric_proc_cpu_usage, uint32_t, ""); +DECLARE_METRIC("proc_thread_count", metric_thread_count, uint32_t, ""); +DECLARE_METRIC("sys_core_usage", metric_proc_core_usage, uint32_t, ""); +DECLARE_METRIC_SET("cpu_core_container", mset_cpu_core_container, metric_proc_core_usage); +DECLARE_METRIC_LIST("cpu_core_list", mlist_cpu_core_list, std::vector); +DECLARE_METRIC_SET("sys_cpu_container", + mset_sys_cpu_container, + metric_proc_cpu_usage, + metric_thread_count, + mlist_cpu_core_list); + +/// Metrics root object. +DECLARE_METRIC("type", metric_type_tag, std::string, ""); +DECLARE_METRIC("timestamp", metric_timestamp_tag, double, ""); + +/// Metrics context. +using metric_context_t = srslog::build_context_type; + +} // namespace + +/// Returns the current time in seconds with ms precision since UNIX epoch. +static double get_time_stamp() +{ + auto tp = std::chrono::system_clock::now().time_since_epoch(); + return std::chrono::duration_cast(tp).count() * 1e-3; +} + +void metrics_json::set_metrics(const ue_metrics_t& metrics, const uint32_t period_usec) +{ + if (!ue) { + return; + } + + metric_context_t ctx("JSON Metrics"); + + // Fill root object. + ctx.write("metrics"); + + // Fill cc container. + auto& carrier_list = ctx.get(); + carrier_list.resize(metrics.phy.nof_active_cc); + for (uint32_t i = 0, e = carrier_list.size(); i != e; ++i) { + auto& carrier = carrier_list[i]; + + // PHY. + carrier.write(metrics.phy.info[i].dl_earfcn); + carrier.write(metrics.phy.info[i].pci); + + carrier.write(metrics.phy.ch[i].rsrp); + carrier.write(metrics.phy.ch[i].pathloss); + + carrier.write(metrics.phy.sync[i].cfo); + + carrier.write(metrics.phy.ch[i].sinr); + carrier.write(metrics.phy.dl[i].mcs); + carrier.write(metrics.phy.ul[i].mcs); + carrier.write(metrics.phy.sync[i].ta_us); + carrier.write(metrics.phy.sync[i].distance_km); + carrier.write(metrics.phy.sync[i].speed_kmph); + + // MAC + carrier.get().write(metrics.stack.mac[i].rx_brate / + metrics.stack.mac[i].nof_tti * 1e-3); + carrier.get().write( + (metrics.stack.mac[i].rx_pkts > 0) ? (float)100 * metrics.stack.mac[i].rx_errors / metrics.stack.mac[i].rx_pkts + : 0); + carrier.get().write(metrics.stack.mac[i].tx_brate / + metrics.stack.mac[i].nof_tti * 1e-3); + carrier.get().write( + (metrics.stack.mac[i].tx_pkts > 0) ? (float)100 * metrics.stack.mac[i].tx_errors / metrics.stack.mac[i].tx_pkts + : 0); + carrier.get().write(metrics.stack.mac[i].ul_buffer); + } + + // Fill GW container. + ctx.get().write(metrics.gw.dl_tput_mbps); + ctx.get().write(metrics.gw.ul_tput_mbps); + + // Fill RRC container. + ctx.get().write(rrc_state_text[metrics.stack.rrc.state]); + + // Fill neighbour list. + auto& neighbour_list = ctx.get(); + neighbour_list.resize(metrics.stack.rrc.neighbour_cells.size()); + for (uint32_t i = 0, e = neighbour_list.size(); i != e; ++i) { + auto& neigbour = neighbour_list[i]; + neigbour.write(metrics.stack.rrc.neighbour_cells[i].pci); + neigbour.write(metrics.stack.rrc.neighbour_cells[i].rsrp); + neigbour.write(metrics.stack.rrc.neighbour_cells[i].cfo_hz); + } + + // Fill NAS container. + ctx.get().write(emm_state_text(metrics.stack.nas.state)); + + // Fill RF container. + ctx.get().write(metrics.rf.rf_o); + ctx.get().write(metrics.rf.rf_u); + ctx.get().write(metrics.rf.rf_l); + + // Fill system memory container. + ctx.get().write(metrics.sys.process_realmem); + ctx.get().write(metrics.sys.process_realmem_kB); + ctx.get().write(metrics.sys.process_virtualmem_kB); + ctx.get().write(metrics.sys.system_mem); + + // Fill system CPU container. + ctx.get().write(metrics.sys.process_cpu_usage); + ctx.get().write(metrics.sys.thread_count); + auto& core_list = ctx.get().get(); + core_list.resize(metrics.sys.cpu_count); + for (uint32_t i = 0, e = core_list.size(); i != e; ++i) { + core_list[i].write(metrics.sys.cpu_load[i]); + } + + // Log the context. + ctx.write(get_time_stamp()); + log_c(ctx); +} diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index 7c1cf96e9..663b9581f 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -391,26 +391,32 @@ enable = false ##################################################################### # General configuration options # -# metrics_csv_enable: Write UE metrics to CSV file. +# metrics_csv_enable: Write UE metrics to CSV file. # -# metrics_period_secs: Sets the period at which metrics are requested from the UE. +# metrics_period_secs: Sets the period at which metrics are requested from the UE. # -# metrics_csv_filename: File path to use for CSV metrics. +# metrics_csv_filename: File path to use for CSV metrics. # -# tracing_enable: Write source code tracing information to a file. +# tracing_enable: Write source code tracing information to a file. # -# tracing_filename: File path to use for tracing information. +# tracing_filename: File path to use for tracing information. # -# tracing_buffcapacity: Maximum capacity in bytes the tracing framework can store. +# tracing_buffcapacity: Maximum capacity in bytes the tracing framework can store. # -# have_tti_time_stats: Calculate TTI execution statistics using system clock +# have_tti_time_stats: Calculate TTI execution statistics using system clock +# +# metrics_json_enable: Write UE metrics to JSON file. +# +# metrics_json_filename: File path to use for JSON metrics. # ##################################################################### [general] -#metrics_csv_enable = false -#metrics_period_secs = 1 -#metrics_csv_filename = /tmp/ue_metrics.csv -#have_tti_time_stats = true -#tracing_enable = true -#tracing_filename = /tmp/ue_tracing.log -#tracing_buffcapacity = 1000000 +#metrics_csv_enable = false +#metrics_period_secs = 1 +#metrics_csv_filename = /tmp/ue_metrics.csv +#have_tti_time_stats = true +#tracing_enable = true +#tracing_filename = /tmp/ue_tracing.log +#tracing_buffcapacity = 1000000 +#metrics_json_enable = false +#metrics_json_filename = /tmp/ue_metrics.json