From 300ec24108211721d99ac9faf67fd1f91057b95b Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Thu, 2 Jun 2016 16:24:49 +0300 Subject: cpu utilization per thread + mbufs per socket + add in tui --- .../trex_control_plane/server/singleton_daemon.py | 37 +++++---- .../trex_control_plane/stl/console/trex_tui.py | 26 +++++- .../stl/trex_stl_lib/trex_stl_client.py | 24 +++++- .../stl/trex_stl_lib/trex_stl_stats.py | 93 +++++++++++++++++++++- .../stl/trex_stl_lib/utils/parsing_opts.py | 14 +++- src/bp_sim.cpp | 67 ++++++---------- src/bp_sim.h | 10 +-- src/internal_api/trex_platform_api.h | 4 + src/main_dpdk.cpp | 21 ++--- src/rpc-server/commands/trex_rpc_cmd_general.cpp | 13 +-- src/rpc-server/commands/trex_rpc_cmds.h | 2 +- src/rpc-server/trex_rpc_cmds_table.cpp | 2 +- src/trex_defs.h | 2 +- src/utl_cpuu.cpp | 34 ++++---- src/utl_cpuu.h | 15 ++-- 15 files changed, 247 insertions(+), 117 deletions(-) diff --git a/scripts/automation/trex_control_plane/server/singleton_daemon.py b/scripts/automation/trex_control_plane/server/singleton_daemon.py index 7cfbc3bc..0a3b9c09 100755 --- a/scripts/automation/trex_control_plane/server/singleton_daemon.py +++ b/scripts/automation/trex_control_plane/server/singleton_daemon.py @@ -88,8 +88,7 @@ class SingletonDaemon(object): ret_code, stdout, stderr = run_command('kill -9 %s' % pid) # unconditional kill if ret_code: raise Exception('Failed to run kill -9 command for %s: %s' % (self.name, [ret_code, stdout, stderr])) - poll_rate = 0.1 - for i in range(inr(timeout / poll_rate)): + for i in range(int(timeout / poll_rate)): if not self.is_running(): return True sleep(poll_rate) @@ -105,23 +104,23 @@ class SingletonDaemon(object): raise Exception('No starting command registered for %s' % self.name) if type(self.run_cmd) is types.FunctionType: self.run_cmd() - return - with tempfile.TemporaryFile() as stdout_file, tempfile.TemporaryFile() as stderr_file: - proc = Popen(shlex.split('%s -p %s' % (self.run_cmd, self.port)), cwd = self.dir, close_fds = True, - stdout = stdout_file, stderr = stderr_file) - if timeout > 0: - poll_rate = 0.1 - for i in range(int(timeout/poll_rate)): - sleep(poll_rate) - if bool(proc.poll()): # process ended with error - stdout_file.seek(0) - stderr_file.seek(0) - raise Exception('Run of %s ended unexpectfully: %s' % (self.name, [proc.returncode, stdout_file.read().decode(errors = 'replace'), stderr_file.read().decode(errors = 'replace')])) - elif proc.poll() == 0: # process runs other process, and ended - break - if self.is_running(): - return True - raise Exception('%s failed to run.' % self.name) + else: + with tempfile.TemporaryFile() as stdout_file, tempfile.TemporaryFile() as stderr_file: + proc = Popen(shlex.split('%s -p %s' % (self.run_cmd, self.port)), cwd = self.dir, close_fds = True, + stdout = stdout_file, stderr = stderr_file) + if timeout > 0: + poll_rate = 0.1 + for i in range(int(timeout/poll_rate)): + sleep(poll_rate) + if bool(proc.poll()): # process ended with error + stdout_file.seek(0) + stderr_file.seek(0) + raise Exception('Run of %s ended unexpectfully: %s' % (self.name, [proc.returncode, stdout_file.read().decode(errors = 'replace'), stderr_file.read().decode(errors = 'replace')])) + elif proc.poll() == 0: # process runs other process, and ended + break + if self.is_running(): + return True + raise Exception('%s failed to run.' % self.name) # restart the daemon def restart(self, timeout = 5): diff --git a/scripts/automation/trex_control_plane/stl/console/trex_tui.py b/scripts/automation/trex_control_plane/stl/console/trex_tui.py index d3be4435..a2ffcad6 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -210,7 +210,7 @@ class TrexTUILatencyStats(TrexTUIPanel): super(TrexTUILatencyStats, self).__init__(mng, "lstats") self.key_actions = OrderedDict() self.key_actions['c'] = {'action': self.action_clear, 'legend': 'clear', 'show': True} - self.key_actions['t'] = {'action': self.action_toggle_histogram, 'legend': 'toggle histogram', 'show': True} + self.key_actions['h'] = {'action': self.action_toggle_histogram, 'legend': 'histogram toggle', 'show': True} self.is_histogram = False @@ -238,6 +238,23 @@ class TrexTUILatencyStats(TrexTUIPanel): self.stateless_client.latency_stats.clear_stats() return "" + +# utilization stats +class TrexTUIUtilizationStats(TrexTUIPanel): + def __init__ (self, mng): + super(TrexTUIUtilizationStats, self).__init__(mng, "ustats") + self.key_actions = {} + + def show (self): + stats = self.stateless_client._get_formatted_stats(port_id_list = None, stats_mask = trex_stl_stats.UT_COMPAT) + # print stats to screen + for stat_type, stat_data in stats.items(): + text_tables.print_table_with_header(stat_data.text_table, stat_type) + + def get_key_actions (self): + return self.key_actions + + # log class TrexTUILog(): def __init__ (self): @@ -270,12 +287,14 @@ class TrexTUIPanelManager(): self.panels['dashboard'] = TrexTUIDashBoard(self) self.panels['sstats'] = TrexTUIStreamsStats(self) self.panels['lstats'] = TrexTUILatencyStats(self) + self.panels['ustats'] = TrexTUIUtilizationStats(self) self.key_actions = OrderedDict() self.key_actions['q'] = {'action': self.action_quit, 'legend': 'quit', 'show': True} self.key_actions['d'] = {'action': self.action_show_dash, 'legend': 'dashboard', 'show': True} self.key_actions['s'] = {'action': self.action_show_sstats, 'legend': 'streams', 'show': True} self.key_actions['l'] = {'action': self.action_show_lstats, 'legend': 'latency', 'show': True} + self.key_actions['u'] = {'action': self.action_show_ustats, 'legend': 'util', 'show': True} # start with dashboard self.main_panel = self.panels['dashboard'] @@ -392,6 +411,11 @@ class TrexTUIPanelManager(): self.init(self.show_log) return "" + def action_show_ustats(self): + self.main_panel = self.panels['ustats'] + self.init(self.show_log) + return "" + # shows a textual top style window class TrexTUI(): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 22895a75..70f38bb0 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -555,14 +555,17 @@ class STLClient(object): self.latency_stats = trex_stl_stats.CLatencyStats(self.ports) + self.util_stats = trex_stl_stats.CUtilStats(self) + self.stats_generator = trex_stl_stats.CTRexInfoGenerator(self.global_stats, self.ports, self.flow_stats, self.latency_stats, + self.util_stats, self.async_client.monitor) - + ############# private functions - used by the class itself ########### @@ -1519,6 +1522,23 @@ class STLClient(object): if not rc: raise STLError(rc) + @__api_check(True) + def get_util_stats(self): + """ + Get utilization stats: + History of TRex CPU utilization per thread (list of lists) + MBUFs memory consumption per CPU socket. + + :parameters: + None + + :raises: + + :exc:`STLError` + + """ + self.logger.pre_cmd('Getting Utilization stats') + return self.util_stats.get_stats() + @__api_check(True) def reset(self, ports = None): @@ -1739,8 +1759,6 @@ class STLClient(object): raise STLError(rc) - - @__api_check(True) def stop (self, ports = None, rx_delay_ms = 10): """ diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py index 94a45577..06009ac3 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py @@ -11,7 +11,6 @@ import datetime import time import re import math -import copy import threading import pprint @@ -22,13 +21,16 @@ PORT_STATUS = 'ps' STREAMS_STATS = 's' LATENCY_STATS = 'ls' LATENCY_HISTOGRAM = 'lh' +CPU_STATS = 'c' +MBUF_STATS = 'm' -ALL_STATS_OPTS = [GLOBAL_STATS, PORT_STATS, PORT_STATUS, STREAMS_STATS, LATENCY_STATS, PORT_GRAPH, LATENCY_HISTOGRAM] +ALL_STATS_OPTS = [GLOBAL_STATS, PORT_STATS, PORT_STATUS, STREAMS_STATS, LATENCY_STATS, PORT_GRAPH, LATENCY_HISTOGRAM, CPU_STATS, MBUF_STATS] COMPACT = [GLOBAL_STATS, PORT_STATS] GRAPH_PORT_COMPACT = [GLOBAL_STATS, PORT_GRAPH] SS_COMPAT = [GLOBAL_STATS, STREAMS_STATS] # stream stats LS_COMPAT = [GLOBAL_STATS, LATENCY_STATS] # latency stats LH_COMPAT = [GLOBAL_STATS, LATENCY_HISTOGRAM] # latency histogram +UT_COMPAT = [GLOBAL_STATS, CPU_STATS, MBUF_STATS] # utilization ExportableStats = namedtuple('ExportableStats', ['raw_data', 'text_table']) @@ -104,6 +106,13 @@ def calculate_diff_raw (samples): return total +# used to sort '64b', '9kb' etc. +def key_cmp_bytes(val): + multiplier = 1 + if 'kb' in val: + multiplier = 1000 + return multiplier * int(val.replace('k', '').replace('b', '')) + # a simple object to keep a watch over a field class WatchedField(object): @@ -138,11 +147,12 @@ class CTRexInfoGenerator(object): STLClient and the ports. """ - def __init__(self, global_stats_ref, ports_dict_ref, rx_stats_ref, latency_stats_ref, async_monitor): + def __init__(self, global_stats_ref, ports_dict_ref, rx_stats_ref, latency_stats_ref, util_stats_ref, async_monitor): self._global_stats = global_stats_ref self._ports_dict = ports_dict_ref self._rx_stats_ref = rx_stats_ref self._latency_stats_ref = latency_stats_ref + self._util_stats_ref = util_stats_ref self._async_monitor = async_monitor def generate_single_statistic(self, port_id_list, statistic_type): @@ -167,6 +177,12 @@ class CTRexInfoGenerator(object): elif statistic_type == LATENCY_HISTOGRAM: return self._generate_latency_histogram() + elif statistic_type == CPU_STATS: + return self._generate_cpu_util_stats() + + elif statistic_type == MBUF_STATS: + return self._generate_mbuf_util_stats() + else: # ignore by returning empty object return {} @@ -404,6 +420,59 @@ class CTRexInfoGenerator(object): stats_table.header(header) return {"latency_histogram": ExportableStats(None, stats_table)} + def _generate_cpu_util_stats(self): + util_stats = self._util_stats_ref.get_stats() + if not util_stats or 'cpu' not in util_stats: + raise Exception("Excepting 'cpu' section in stats %s" % util_stats) + cpu_stats = util_stats['cpu'] + hist_len = len(cpu_stats[0]) + avg_len = min(5, hist_len) + show_len = min(15, hist_len) + stats_table = text_tables.TRexTextTable() + stats_table.header(['Thread', 'Avg', 'Latest'] + list(range(-1, 0 - show_len, -1))) + stats_table.set_cols_align(['l'] + ['r'] * (show_len + 1)) + stats_table.set_cols_width([8, 3, 6] + [3] * (show_len - 1)) + stats_table.set_cols_dtype(['t'] * (show_len + 2)) + for i in range(min(14, len(cpu_stats))): + avg = int(round(sum(cpu_stats[i][:avg_len]) / avg_len)) + stats_table.add_row([i, avg] + cpu_stats[i][:show_len]) + return {'cpu_util(%)': ExportableStats(None, stats_table)} + + def _generate_mbuf_util_stats(self): + util_stats = self._util_stats_ref.get_stats() + if not util_stats or 'mbuf_stats' not in util_stats: + raise Exception("Excepting 'mbuf_stats' section in stats %s" % util_stats) + mbuf_stats = util_stats['mbuf_stats'] + for mbufs_per_socket in mbuf_stats.values(): + first_socket_mbufs = mbufs_per_socket + break + if not self._util_stats_ref.mbuf_types_list: + mbuf_keys = list(first_socket_mbufs.keys()) + mbuf_keys.sort(key = key_cmp_bytes) + self._util_stats_ref.mbuf_types_list = mbuf_keys + types_len = len(self._util_stats_ref.mbuf_types_list) + stats_table = text_tables.TRexTextTable() + stats_table.set_cols_align(['l'] + ['r'] * types_len) + stats_table.set_cols_width([10] + [7] * types_len) + stats_table.set_cols_dtype(['t'] * (types_len + 1)) + stats_table.header([''] + self._util_stats_ref.mbuf_types_list) + total_list = [] + for mbuf_type in self._util_stats_ref.mbuf_types_list: + total_list.append(first_socket_mbufs[mbuf_type][1]) + stats_table.add_row(['Total:'] + total_list) + stats_table.add_row(['Used:'] + [''] * types_len) + for socket_name, mbufs in mbuf_stats.items(): + socket_show_name = socket_name.replace('cpu-', '').replace('-', ' ').capitalize() + ':' + used_list = [] + percentage_list = [] + for mbuf_type in self._util_stats_ref.mbuf_types_list: + used = mbufs[mbuf_type][1] - mbufs[mbuf_type][0] + used_list.append(used) + percentage_list.append('%s%%' % int(100 * used / mbufs[mbuf_type][1])) + stats_table.add_row([socket_show_name] + used_list) + stats_table.add_row(['Percent:'] + percentage_list) + return {'mbuf_util': ExportableStats(None, stats_table)} + @staticmethod def _get_rational_block_char(value, range_start, interval): # in Konsole, utf-8 is sometimes printed with artifacts, return ascii for now @@ -1280,7 +1349,23 @@ class CRxStats(CTRexStats): return stats - +class CUtilStats(CTRexStats): + + def __init__(self, client): + super(CUtilStats, self).__init__() + self.client = client + self.history = deque(maxlen = 1) + self.mbuf_types_list = None + + def get_stats(self, force = False): + time_now = time.time() + if self.last_update_ts + 1 < time_now or not self.history or force: + rc = self.client._transmit('get_utilization') + if not rc: + raise Exception(rc) + self.last_update_ts = time_now + self.history.append(rc.data()) + return self.history[-1] if __name__ == "__main__": pass diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py index 98e3ca6a..a435e54e 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py @@ -39,6 +39,8 @@ PORT_STATS = 51 PORT_STATUS = 52 STREAMS_STATS = 53 STATS_MASK = 54 +CPU_STATS = 55 +MBUF_STATS = 56 STREAMS_MASK = 60 # ALL_STREAMS = 61 @@ -346,6 +348,14 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'action': 'store_true', 'help': "Fetch only streams stats"}), + CPU_STATS: ArgumentPack(['-c'], + {'action': 'store_true', + 'help': "Fetch only CPU utilization stats"}), + + MBUF_STATS: ArgumentPack(['-m'], + {'action': 'store_true', + 'help': "Fetch only MBUF utilization stats"}), + STREAMS_MASK: ArgumentPack(['--streams'], {"nargs": '+', 'dest':'streams', @@ -371,7 +381,9 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], STATS_MASK: ArgumentGroup(MUTEX, [GLOBAL_STATS, PORT_STATS, PORT_STATUS, - STREAMS_STATS], + STREAMS_STATS, + CPU_STATS, + MBUF_STATS], {}) } diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp index 27a79268..be9de21e 100755 --- a/src/bp_sim.cpp +++ b/src/bp_sim.cpp @@ -496,39 +496,24 @@ void CRteMemPool::dump_in_case_of_error(FILE *fd){ dump(fd); } -std::string CRteMemPool::add_to_json( - std::string name, - rte_mempool_t * pool, - bool last){ +void CRteMemPool::add_to_json(Json::Value &json, std::string name, rte_mempool_t * pool){ uint32_t p_free = rte_mempool_count(pool); uint32_t p_size = pool->size; - char buff[200]; - sprintf(buff,"\"%s\":[%llu,%llu]",name.c_str(),(unsigned long long)p_free,(unsigned long long)p_size); - std::string json = std::string(buff) + (last?std::string(""):std::string(",")); - return (json); + json[name].append((unsigned long long)p_free); + json[name].append((unsigned long long)p_size); } -std::string CRteMemPool::dump_as_json(uint8_t id,bool last){ - - char buff[200]; - sprintf(buff,"\"socket-%d\":{", (int)id); - - std::string json=std::string(buff); - - json+=add_to_json("64b",m_small_mbuf_pool); - json+=add_to_json("128b",m_mbuf_pool_128); - json+=add_to_json("256b",m_mbuf_pool_256); - json+=add_to_json("512b",m_mbuf_pool_512); - json+=add_to_json("1024b",m_mbuf_pool_1024); - json+=add_to_json("2048b",m_mbuf_pool_2048); - json+=add_to_json("4096b",m_mbuf_pool_4096); - json+=add_to_json("9kb",m_mbuf_pool_9k,true); - - json+="}" ; - if (last==false) { - json+="," ; - } +Json::Value CRteMemPool::dump_as_json(){ + Json::Value json; + add_to_json(json, "64b", m_small_mbuf_pool); + add_to_json(json, "128b", m_mbuf_pool_128); + add_to_json(json, "256b", m_mbuf_pool_256); + add_to_json(json, "512b", m_mbuf_pool_512); + add_to_json(json, "1024b", m_mbuf_pool_1024); + add_to_json(json, "2048b", m_mbuf_pool_2048); + add_to_json(json, "4096b", m_mbuf_pool_4096); + add_to_json(json, "9kb", m_mbuf_pool_9k); return (json); } @@ -546,32 +531,26 @@ void CRteMemPool::dump(FILE *fd){ DUMP_MBUF("mbuf_9k",m_mbuf_pool_9k); } -//////////////////////////////////////// +//////////////////////////////////////// -std::string CGlobalInfo::dump_pool_as_json(void){ - - std::string json="\"mbuf_stats\":{"; +Json::Value CGlobalInfo::dump_pool_as_json(void){ + Json::Value json; CPlatformSocketInfo * lpSocket =&m_socket; - int last_socket=-1; - /* calc the last socket */ - int i; - for (i=0; i<(int)MAX_SOCKETS_SUPPORTED; i++) { + for (int i=0; i<(int)MAX_SOCKETS_SUPPORTED; i++) { if (lpSocket->is_sockets_enable((socket_id_t)i)) { - last_socket=i; + std::string socket_id = "cpu-socket-" + std::to_string(i); + json["mbuf_stats"][socket_id] = m_mem_pool[i].dump_as_json(); } } - - for (i=0; i<(int)MAX_SOCKETS_SUPPORTED; i++) { - if (lpSocket->is_sockets_enable((socket_id_t)i)) { - json+=m_mem_pool[i].dump_as_json(i,last_socket==i?true:false); - } - } - json+="},"; return json; } +std::string CGlobalInfo::dump_pool_as_json_str(void){ + return ("\"mbuf_stats\":" + dump_pool_as_json().toStyledString() + ","); +} + void CGlobalInfo::free_pools(){ CPlatformSocketInfo * lpSocket =&m_socket; CRteMemPool * lpmem; diff --git a/src/bp_sim.h b/src/bp_sim.h index 7b4dfdd5..8aead5f1 100755 --- a/src/bp_sim.h +++ b/src/bp_sim.h @@ -1149,13 +1149,10 @@ public: void dump_in_case_of_error(FILE *fd); - std::string dump_as_json(uint8_t id,bool last); + Json::Value dump_as_json(); private: - std::string add_to_json(std::string name, - rte_mempool_t * pool, - bool last=false); - + void add_to_json(Json::Value &json, std::string name, rte_mempool_t * pool); public: rte_mempool_t * m_small_mbuf_pool; /* pool for start packets */ @@ -1249,7 +1246,8 @@ public: } - static std::string dump_pool_as_json(void); + static Json::Value dump_pool_as_json(void); + static std::string dump_pool_as_json_str(void); public: diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h index ca37b0da..b0294883 100644 --- a/src/internal_api/trex_platform_api.h +++ b/src/internal_api/trex_platform_api.h @@ -28,6 +28,7 @@ limitations under the License. #include #include "flow_stat_parser.h" #include "trex_defs.h" +#include /** * Global stats @@ -157,6 +158,7 @@ public: virtual void flush_dp_messages() const = 0; virtual int get_active_pgids(flow_stat_active_t &result) const = 0; virtual int get_cpu_util_full(cpu_util_full_t &result) const = 0; + virtual int get_mbuf_util(Json::Value &result) const = 0; virtual CFlowStatParser *get_flow_stat_parser() const = 0; virtual ~TrexPlatformApi() {} }; @@ -190,6 +192,7 @@ public: void flush_dp_messages() const; int get_active_pgids(flow_stat_active_t &result) const; int get_cpu_util_full(cpu_util_full_t &result) const; + int get_mbuf_util(Json::Value &result) const; CFlowStatParser *get_flow_stat_parser() const; }; @@ -255,6 +258,7 @@ public: } int get_active_pgids(flow_stat_active_t &result) const {return 0;} int get_cpu_util_full(cpu_util_full_t &result) const {return 0;} + int get_mbuf_util(Json::Value &result) const {return 0;} CFlowStatParser *get_flow_stat_parser() const {return new CFlowStatParser();} private: diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index ff4b2b6a..94679d72 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -2427,7 +2427,7 @@ public: public: void Dump(FILE *fd,DumpFormat mode); void DumpAllPorts(FILE *fd); - void dump_json(std::string & json, bool baseline,uint32_t stats_tick); + void dump_json(std::string & json, bool baseline); private: std::string get_field(std::string name,float &f); std::string get_field(std::string name,uint64_t &f); @@ -2467,7 +2467,7 @@ std::string CGlobalStats::get_field_port(int port,std::string name,uint64_t &f){ } -void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_tick){ +void CGlobalStats::dump_json(std::string & json, bool baseline){ /* refactor this to JSON */ json="{\"name\":\"trex-global\",\"type\":0,"; @@ -2535,10 +2535,6 @@ void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_ti json+=GET_FIELD_PORT(i,m_total_rx_pps); } json+=m_template.dump_as_json("template"); - if ( stats_tick %4==0){ - json+=CGlobalInfo::dump_pool_as_json(); /* no need a feq update beacuse it trash the cores D cache, once in 2 sec */ - } - json+="\"unknown\":0}}" ; } @@ -3776,7 +3772,7 @@ CGlobalTRex::publish_async_data(bool sync_now, bool baseline) { get_stats(m_stats); } - m_stats.dump_json(json, baseline,m_stats_cnt); + m_stats.dump_json(json, baseline); m_zmq_publisher.publish_json(json); /* generator json , all cores are the same just sample the first one */ @@ -3869,7 +3865,7 @@ CGlobalTRex::handle_slow_path(bool &was_stopped) { if (m_io_modes.m_g_mode == CTrexGlobalIoMode::gMem) { if ( m_stats_cnt%4==0) { - fprintf (stdout," %s \n",CGlobalInfo::dump_pool_as_json().c_str()); + fprintf (stdout," %s \n",CGlobalInfo::dump_pool_as_json_str().c_str()); } } @@ -5707,13 +5703,18 @@ int TrexDpdkPlatformApi::get_active_pgids(flow_stat_active_t &result) const { } int TrexDpdkPlatformApi::get_cpu_util_full(cpu_util_full_t &cpu_util_full) const { - for (int i=0; i<(int)g_trex.m_fl.m_threads_info.size(); i++) { - CFlowGenListPerThread * lp=g_trex.m_fl.m_threads_info[i]; + for (int thread_id=0; thread_id<(int)g_trex.m_fl.m_threads_info.size(); thread_id++) { + CFlowGenListPerThread * lp=g_trex.m_fl.m_threads_info[thread_id]; cpu_util_full.push_back(lp->m_cpu_cp_u.GetHistory()); } return 0; } +int TrexDpdkPlatformApi::get_mbuf_util(Json::Value &mbuf_pool) const { + mbuf_pool = CGlobalInfo::dump_pool_as_json(); + return 0; +} + CFlowStatParser *TrexDpdkPlatformApi::get_flow_stat_parser() const { return CTRexExtendedDriverDb::Ins()->get_drv()->get_flow_stat_parser(); } diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp index 541ff099..37eafef8 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp @@ -156,22 +156,23 @@ TrexRpcCmdGetActivePGIds::_run(const Json::Value ¶ms, Json::Value &result) { return (TREX_RPC_CMD_OK); } -/* - Get cpu utilization per thread with up to 20 latest values, Latest value will be first in array. - */ + +// get utilization of CPU per thread with up to 20 latest values + mbufs per socket trex_rpc_cmd_rc_e -TrexRpcCmdGetCpuUtilFull::_run(const Json::Value ¶ms, Json::Value &result) { +TrexRpcCmdGetUtilization::_run(const Json::Value ¶ms, Json::Value &result) { cpu_util_full_t cpu_util_full; Json::Value §ion = result["result"]; - section = Json::arrayValue; + + if (get_stateless_obj()->get_platform_api()->get_mbuf_util(section) != 0) + return TREX_RPC_CMD_INTERNAL_ERR; if (get_stateless_obj()->get_platform_api()->get_cpu_util_full(cpu_util_full) != 0) return TREX_RPC_CMD_INTERNAL_ERR; for (int thread_id = 0; thread_id < cpu_util_full.size(); thread_id++) { for (int history_id = 0; history_id < cpu_util_full[thread_id].size(); history_id++) { - section[thread_id].append(cpu_util_full.at(thread_id).at(history_id)); + section["cpu"][thread_id].append(cpu_util_full.at(thread_id).at(history_id)); } } return (TREX_RPC_CMD_OK); diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h index 0df9fd2b..2776727d 100644 --- a/src/rpc-server/commands/trex_rpc_cmds.h +++ b/src/rpc-server/commands/trex_rpc_cmds.h @@ -67,7 +67,7 @@ TREX_RPC_CMD_DEFINE(TrexRpcPublishNow, "publish_now", 2, false, TREX_RPC_CMD_DEFINE(TrexRpcCmdGetCmds, "get_supported_cmds", 0, false, APIClass::API_CLASS_TYPE_CORE); TREX_RPC_CMD_DEFINE(TrexRpcCmdGetVersion, "get_version", 0, false, APIClass::API_CLASS_TYPE_CORE); TREX_RPC_CMD_DEFINE(TrexRpcCmdGetActivePGIds, "get_active_pgids", 0, false, APIClass::API_CLASS_TYPE_CORE); -TREX_RPC_CMD_DEFINE(TrexRpcCmdGetCpuUtilFull, "get_cpu_util_full", 0, false, APIClass::API_CLASS_TYPE_CORE); +TREX_RPC_CMD_DEFINE(TrexRpcCmdGetUtilization, "get_utilization", 0, false, APIClass::API_CLASS_TYPE_CORE); TREX_RPC_CMD_DEFINE_EXTENDED(TrexRpcCmdGetSysInfo, "get_system_info", 0, false, APIClass::API_CLASS_TYPE_CORE, diff --git a/src/rpc-server/trex_rpc_cmds_table.cpp b/src/rpc-server/trex_rpc_cmds_table.cpp index 37fed848..6144d265 100644 --- a/src/rpc-server/trex_rpc_cmds_table.cpp +++ b/src/rpc-server/trex_rpc_cmds_table.cpp @@ -39,7 +39,7 @@ TrexRpcCommandsTable::TrexRpcCommandsTable() { register_command(new TrexRpcCmdGetCmds()); register_command(new TrexRpcCmdGetVersion()); register_command(new TrexRpcCmdGetActivePGIds()); - register_command(new TrexRpcCmdGetCpuUtilFull()); + register_command(new TrexRpcCmdGetUtilization()); register_command(new TrexRpcCmdGetSysInfo()); register_command(new TrexRpcCmdGetOwner()); register_command(new TrexRpcCmdAcquire()); diff --git a/src/trex_defs.h b/src/trex_defs.h index 55e10fb2..62024fa8 100644 --- a/src/trex_defs.h +++ b/src/trex_defs.h @@ -37,6 +37,6 @@ limitations under the License. typedef std::set flow_stat_active_t; typedef std::set::iterator flow_stat_active_it_t; -typedef std::vector> cpu_util_full_t; +typedef std::vector> cpu_util_full_t; #endif diff --git a/src/utl_cpuu.cpp b/src/utl_cpuu.cpp index 1954058c..1191eb74 100755 --- a/src/utl_cpuu.cpp +++ b/src/utl_cpuu.cpp @@ -25,8 +25,8 @@ limitations under the License. void CCpuUtlCp::Create(CCpuUtlDp * cdp){ m_dpcpu=cdp; - m_cpu_util.clear(); - m_cpu_util.push_back(0.0); // here it's same as insert to front + memset(m_cpu_util, 0, sizeof(m_cpu_util)); + m_history_latest_index = 0; m_cpu_util_lpf=0.0; m_ticks=0; m_work=0; @@ -43,29 +43,35 @@ void CCpuUtlCp::Update(){ m_work++; } if (m_ticks==100) { - double window_cpu_u = (double)m_work/m_ticks; /* LPF*/ - m_cpu_util_lpf = (m_cpu_util_lpf*0.75)+(window_cpu_u*0.25); - m_cpu_util.insert(m_cpu_util.begin(), window_cpu_u); - if (m_cpu_util.size() > history_size) - m_cpu_util.pop_back(); + m_cpu_util_lpf = (m_cpu_util_lpf*0.75)+((double)m_work*0.25); + AppendHistory(m_work); m_ticks=0; m_work=0; - } } /* return cpu % Smoothed */ double CCpuUtlCp::GetVal(){ - return (m_cpu_util_lpf*100); // percentage + return (m_cpu_util_lpf); } /* return cpu % Raw */ -double CCpuUtlCp::GetValRaw(){ - return (m_cpu_util.front()*100); // percentage +uint8_t CCpuUtlCp::GetValRaw(){ + return (m_cpu_util[m_history_latest_index]); +} + +/* return cpu % utilization history */ +std::vector CCpuUtlCp::GetHistory(){ + std::vector history_vect; + for (int i = m_history_latest_index + m_history_size; i > m_history_latest_index; i--) { + history_vect.insert(history_vect.begin(), m_cpu_util[i % m_history_size]); + } + return (history_vect); } -/* return cpu utilization history */ -std::vector CCpuUtlCp::GetHistory(){ - return (m_cpu_util); +/* save last CPU % util in history */ +void CCpuUtlCp::AppendHistory(uint8_t val){ + m_history_latest_index = (m_history_latest_index + 1) % m_history_size; + m_cpu_util[m_history_latest_index] = val; } diff --git a/src/utl_cpuu.h b/src/utl_cpuu.h index f96f4688..ad4b2e3b 100755 --- a/src/utl_cpuu.h +++ b/src/utl_cpuu.h @@ -23,6 +23,7 @@ limitations under the License. #include #include +#include #include "os_time.h" #include "mbuf.h" @@ -57,15 +58,17 @@ public: void Update(); /* return cpu % */ double GetVal(); - double GetValRaw(); - std::vector GetHistory(); + uint8_t GetValRaw(); + std::vector GetHistory(); private: + void AppendHistory(uint8_t); CCpuUtlDp * m_dpcpu; - uint16_t m_ticks; - uint16_t m_work; + uint8_t m_ticks; + uint8_t m_work; - std::vector m_cpu_util; // save history - const int history_size=20; + static const int m_history_size=20; + uint8_t m_cpu_util[m_history_size]; // history as cyclic array + uint8_t m_history_latest_index; double m_cpu_util_lpf; }; -- cgit 1.2.3-korg