summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--scripts/automation/regression/functional_tests/stl_basic_tests.py6
-rw-r--r--scripts/automation/regression/setups/trex07/benchmark.yaml370
-rwxr-xr-xscripts/automation/regression/stateful_tests/trex_general_test.py71
-rwxr-xr-xscripts/automation/regression/stateful_tests/trex_rx_test.py2
-rw-r--r--scripts/automation/regression/trex.py1
-rwxr-xr-xscripts/automation/regression/trex_unit_test.py43
-rwxr-xr-xscripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py67
-rw-r--r--scripts/automation/trex_control_plane/stl/console/trex_tui.py26
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py24
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py111
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py14
-rwxr-xr-xscripts/master_daemon.py2
-rwxr-xr-xsrc/bp_sim.cpp77
-rwxr-xr-xsrc/bp_sim.h11
-rw-r--r--src/internal_api/trex_platform_api.h7
-rw-r--r--src/main_dpdk.cpp29
-rw-r--r--src/rpc-server/commands/trex_rpc_cmd_general.cpp23
-rw-r--r--src/rpc-server/commands/trex_rpc_cmds.h1
-rw-r--r--src/rpc-server/trex_rpc_cmds_table.cpp1
-rw-r--r--src/trex_defs.h4
-rwxr-xr-xsrc/utl_cpuu.cpp31
-rwxr-xr-xsrc/utl_cpuu.h17
22 files changed, 646 insertions, 292 deletions
diff --git a/scripts/automation/regression/functional_tests/stl_basic_tests.py b/scripts/automation/regression/functional_tests/stl_basic_tests.py
index a4e28ca9..e03c0742 100644
--- a/scripts/automation/regression/functional_tests/stl_basic_tests.py
+++ b/scripts/automation/regression/functional_tests/stl_basic_tests.py
@@ -85,7 +85,7 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test):
pkts1 = list(RawPcapReader(output))
pkts2 = list(RawPcapReader(golden))
- assert_equal(len(pkts1), len(pkts2))
+ assert_equal(len(pkts1), len(pkts2), 'Lengths of generated pcap (%s) and golden (%s) are different' % (output, golden))
for pkt1, pkt2, i in zip(pkts1, pkts2, range(1, len(pkts1))):
ts1 = float(pkt1[1][0]) + (float(pkt1[1][1]) / 1e6)
@@ -143,7 +143,7 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test):
os.unlink(output_cap)
try:
rc = self.run_sim(input_file, output_cap, options, silent)
- assert_equal(rc, True)
+ assert_equal(rc, True, 'Simulation on profile %s failed.' % profile)
#s='cp '+output_cap+' '+golden_file;
#print s
#os.system(s)
@@ -165,7 +165,7 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test):
profile.dump_to_code(generated_filename)
rc = self.run_sim(generated_filename, output_cap, options, silent)
- assert_equal(rc, True)
+ assert_equal(rc, True, 'Simulation on profile %s (generated) failed.' % profile)
if compare:
self.compare_caps(output_cap, golden_file)
diff --git a/scripts/automation/regression/setups/trex07/benchmark.yaml b/scripts/automation/regression/setups/trex07/benchmark.yaml
index 4778de91..38e25162 100644
--- a/scripts/automation/regression/setups/trex07/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex07/benchmark.yaml
@@ -2,169 +2,243 @@
#### TRex benchmark configuration file ####
###############################################################
-test_nbar_simple :
- multiplier : 7.5
- cores : 2
- exp_gbps : 3.5
- cpu_to_core_ratio : 20800000
- cpu2core_custom_dev: YES
- cpu2core_dev : 0.07
- exp_max_latency : 1000
-
- nbar_classification:
- rtp : 32.57
- http : 30.25
- oracle-sqlnet : 11.23
- exchange : 10.80
- citrix : 5.62
- rtsp : 2.84
- dns : 1.95
- smtp : 0.57
- pop3 : 0.36
- ssl : 0.17
- sctp : 0.13
- sip : 0.09
- unknown : 3.41
-
-test_rx_check :
- multiplier : 13
- cores : 3
- rx_sample_rate : 128
- exp_gbps : 6
- cpu_to_core_ratio : 37270000
- exp_bw : 13
- exp_latency : 1
-
-test_nat_simple : &test_nat_simple
- stat_route_dict :
+#### common templates ###
+
+stat_route_dict: &stat_route_dict
clients_start : 16.0.0.1
servers_start : 48.0.0.1
dual_port_mask : 1.0.0.0
client_destination_mask : 255.0.0.0
server_destination_mask : 255.0.0.0
- nat_dict :
+
+nat_dict: &nat_dict
clients_net_start : 16.0.0.0
client_acl_wildcard_mask : 0.0.0.255
dual_port_mask : 1.0.0.0
pool_start : 200.0.0.0
pool_netmask : 255.255.255.0
- multiplier : 12000
- cores : 1
- cpu_to_core_ratio : 37270000
- exp_bw : 1
- exp_latency : 1
- allow_timeout_dev : YES
-
-test_nat_simple_mode1 : *test_nat_simple
-test_nat_simple_mode2 : *test_nat_simple
-
-test_nat_learning :
- stat_route_dict :
- clients_start : 16.0.0.1
- servers_start : 48.0.0.1
- dual_port_mask : 1.0.0.0
- client_destination_mask : 255.0.0.0
- server_destination_mask : 255.0.0.0
- multiplier : 12000
- cores : 1
- nat_opened : 40000
- cpu_to_core_ratio : 270
- exp_bw : 8
- exp_latency : 1
- allow_timeout_dev : YES
-
-test_routing_imix_64 :
- multiplier : 430
- cores : 1
- cpu_to_core_ratio : 280
- exp_latency : 1
-
-test_routing_imix :
- multiplier : 10
- cores : 1
- cpu_to_core_ratio : 1800
- exp_latency : 1
-
-test_static_routing_imix :
- stat_route_dict :
- clients_start : 16.0.0.1
- servers_start : 48.0.0.1
- dual_port_mask : 1.0.0.0
- client_destination_mask : 255.0.0.0
- server_destination_mask : 255.0.0.0
- multiplier : 8
- cores : 1
- cpu_to_core_ratio : 1800
- exp_latency : 1
-test_static_routing_imix_asymmetric:
- stat_route_dict :
- clients_start : 16.0.0.1
- servers_start : 48.0.0.1
- dual_port_mask : 1.0.0.0
- client_destination_mask : 255.0.0.0
- server_destination_mask : 255.0.0.0
- multiplier : 8
- cores : 1
- cpu_to_core_ratio : 1800
- exp_latency : 1
-
-test_ipv6_simple :
- multiplier : 9
- cores : 2
- cpu_to_core_ratio : 30070000
- cpu2core_custom_dev: YES
- cpu2core_dev : 0.07
-
-
-test_rx_check_sfr:
- multiplier : 10
- cores : 2
- rx_sample_rate : 16
- # allow 0.03% errors, bad router
- error_tolerance : 0.03
-
-test_rx_check_http:
- multiplier : 15000
- cores : 1
- rx_sample_rate : 16
- # allow 0.03% errors, bad routerifconfig
- error_tolerance : 0.03
-test_rx_check_sfr_ipv6:
- multiplier : 10
- cores : 2
- rx_sample_rate : 16
- # allow 0.03% errors, bad router
- error_tolerance : 0.03
+### stateful ###
+
+test_jumbo:
+ multiplier : 17
+ cores : 1
+ bw_per_core : 543.232
+
+
+test_routing_imix:
+ multiplier : 10
+ cores : 1
+ bw_per_core : 34.128
+
+
+test_routing_imix_64:
+ multiplier : 430
+ cores : 1
+ bw_per_core : 5.893
+
+
+test_static_routing_imix: &test_static_routing_imix
+ stat_route_dict : *stat_route_dict
+ multiplier : 8
+ cores : 1
+ bw_per_core : 34.339
+
+test_static_routing_imix_asymmetric: *test_static_routing_imix
+
+
+test_ipv6_simple:
+ multiplier : 9
+ cores : 2
+ bw_per_core : 19.064
+
+
+test_nat_simple_mode1: &test_nat_simple
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+ multiplier : 12000
+ cores : 1
+ nat_opened : 40000
+ allow_timeout_dev : True
+ bw_per_core : 44.445
+
+test_nat_simple_mode2: *test_nat_simple
+
+test_nat_learning:
+ << : *test_nat_simple
+ nat_opened : 40000
+
+
+test_nbar_simple:
+ multiplier : 7.5
+ cores : 2
+ bw_per_core : 17.174
+ nbar_classification:
+ rtp : 32.57
+ http : 30.25
+ oracle_sqlnet : 11.23
+ exchange : 10.80
+ citrix : 5.62
+ rtsp : 2.84
+ dns : 1.95
+ smtp : 0.57
+ pop3 : 0.36
+ ssl : 0.17
+ sctp : 0.13
+ sip : 0.09
+ unknown : 3.41
+
+
+test_rx_check_http: &rx_http
+ multiplier : 15000
+ cores : 1
+ rx_sample_rate : 16
+ bw_per_core : 39.560
test_rx_check_http_ipv6:
- multiplier : 15000
- cores : 1
- rx_sample_rate : 16
- # allow 0.03% errors, bad router
- error_tolerance : 0.03
+ << : *rx_http
+ bw_per_core : 49.237
test_rx_check_http_negative:
- multiplier : 13000
- cores : 1
- rx_sample_rate : 16
- # allow 0.03% errors, bad router
- error_tolerance : 0.03
- stat_route_dict :
- clients_start : 16.0.0.1
- servers_start : 48.0.0.1
- dual_port_mask : 1.0.0.0
- client_destination_mask : 255.0.0.0
- server_destination_mask : 255.0.0.0
- nat_dict :
- clients_net_start : 16.0.0.0
- client_acl_wildcard_mask : 0.0.0.255
- dual_port_mask : 1.0.0.0
- pool_start : 200.0.0.0
- pool_netmask : 255.255.255.0
+ << : *rx_http
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+
+
+test_rx_check_sfr: &rx_sfr
+ multiplier : 10
+ cores : 3
+ rx_sample_rate : 16
+ bw_per_core : 16.082
+
+test_rx_check_sfr_ipv6:
+ << : *rx_sfr
+ bw_per_core : 19.198
+
+
+
+### stateless ###
+
+test_CPU_benchmark:
+ profiles:
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# causes queue full
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 64, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# not enough memory + queue full if memory increase
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 9000, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/imix.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/pcap.py
+ kwargs : {ipg_usec: 2, loop_count: 0}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/hlt/hlt_udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
-test_jumbo:
- multiplier : 17
- cores : 1
diff --git a/scripts/automation/regression/stateful_tests/trex_general_test.py b/scripts/automation/regression/stateful_tests/trex_general_test.py
index 82b1d9d1..8ff4fdaf 100755
--- a/scripts/automation/regression/stateful_tests/trex_general_test.py
+++ b/scripts/automation/regression/stateful_tests/trex_general_test.py
@@ -139,41 +139,42 @@ class CTRexGeneral_Test(unittest.TestCase):
if res[name] != float(val):
self.fail('TRex results[%s]==%f and not as expected %f ' % (name, res[name], val))
- def check_CPU_benchmark (self, trex_res, err = 25, minimal_cpu = 30, maximal_cpu = 85):
- #cpu_util = float(trex_res.get_last_value("trex-global.data.m_cpu_util"))
- cpu_util = sum(trex_res.get_value_list("trex-global.data.m_cpu_util")[-4:-1]) / 3.0 # mean of 3 values before last
-
- if '1G' in self.modes:
- minimal_cpu /= 10.0
-
- if not self.is_virt_nics:
- if cpu_util > maximal_cpu:
- self.fail("CPU is too high (%s%%), probably queue full." % cpu_util )
- #if cpu_util < minimal_cpu:
- # self.fail("CPU is too low (%s%%), can't verify performance in such low CPU%%." % cpu_util )
-
- test_norm_cpu = sum(trex_res.get_value_list("trex-global.data.m_bw_per_core")[-4:-1]) / 3.0
-
- print("TRex CPU utilization: %g%%, norm_cpu is : %g Gb/core" % (round(cpu_util, 2), round(test_norm_cpu)))
-
- expected_norm_cpu = self.get_benchmark_param('bw_per_core')
- if not expected_norm_cpu:
- expected_norm_cpu = 1
-
- calc_error_precent = abs(100.0 * test_norm_cpu / expected_norm_cpu - 100)
- print('Err percent: %s' % calc_error_precent)
- #if calc_error_precent > err and cpu_util > 10:
- # self.fail('Excepted bw_per_core ratio: %s, got: %g' % (expected_norm_cpu, round(test_norm_cpu)))
-
- # report benchmarks
- if self.GAManager:
- try:
- setup_test = '%s.%s' % (CTRexScenario.setup_name, self.get_name())
- self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core', value = int(test_norm_cpu))
- self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core_exp', value = int(expected_norm_cpu))
- self.GAManager.emptyAndReportQ()
- except Exception as e:
- print('Sending GA failed: %s' % e)
+ def check_CPU_benchmark (self, trex_res, err = 25, minimal_cpu = 10, maximal_cpu = 85):
+ cpu_util = trex_res.get_avg_steady_state_value('trex-global.data.m_cpu_util_raw')
+ trex_tx_bps = trex_res.get_avg_steady_state_value('trex-global.data.m_tx_bps')
+ expected_norm_cpu = self.get_benchmark_param('bw_per_core')
+ cores = self.get_benchmark_param('cores')
+ ports_count = trex_res.get_ports_count()
+ test_norm_cpu = trex_tx_bps / (cpu_util * ports_count * cores * 2.5e6)
+
+ if '1G' in self.modes:
+ minimal_cpu /= 10.0
+
+ if not self.is_virt_nics:
+ if cpu_util > maximal_cpu:
+ self.fail("CPU is too high (%s%%), probably queue full." % cpu_util )
+ #if cpu_util < minimal_cpu:
+ # self.fail("CPU is too low (%s%%), can't verify performance in such low CPU%%." % cpu_util )
+
+ print("TRex CPU utilization: %g%%, norm_cpu is : %g Gb/core" % (round(cpu_util, 2), round(test_norm_cpu, 2)))
+
+ if not expected_norm_cpu:
+ expected_norm_cpu = 1
+
+ calc_error_precent = abs(100.0 * test_norm_cpu / expected_norm_cpu - 100)
+ print('Err percent: %s' % calc_error_precent)
+ #if calc_error_precent > err and cpu_util > 10:
+ # self.fail('Excepted bw_per_core ratio: %s, got: %g' % (expected_norm_cpu, round(test_norm_cpu)))
+
+ # report benchmarks
+ if self.GAManager:
+ try:
+ setup_test = '%s.%s' % (CTRexScenario.setup_name, self.get_name())
+ self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core', value = int(test_norm_cpu))
+ self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core_exp', value = int(expected_norm_cpu))
+ self.GAManager.emptyAndReportQ()
+ except Exception as e:
+ print('Sending GA failed: %s' % e)
def check_results_gt (self, res, name, val):
if res is None:
diff --git a/scripts/automation/regression/stateful_tests/trex_rx_test.py b/scripts/automation/regression/stateful_tests/trex_rx_test.py
index 40528d16..c08ad1ea 100755
--- a/scripts/automation/regression/stateful_tests/trex_rx_test.py
+++ b/scripts/automation/regression/stateful_tests/trex_rx_test.py
@@ -250,7 +250,7 @@ class CTRexRx_Test(CTRexGeneral_Test):
print('Run until finish, expect errors')
old_errors = copy.deepcopy(self.fail_reasons)
- nat_dict = self.get_benchmark_param('nat_dict', test_name = 'test_nat_simple')
+ nat_dict = self.get_benchmark_param('nat_dict', test_name = 'test_nat_simple_mode1')
nat_obj = CNatConfig(nat_dict)
self.router.config_nat(nat_obj)
self.router.config_zbf()
diff --git a/scripts/automation/regression/trex.py b/scripts/automation/regression/trex.py
index 44f2faba..7286b166 100644
--- a/scripts/automation/regression/trex.py
+++ b/scripts/automation/regression/trex.py
@@ -40,6 +40,7 @@ class CTRexScenario:
no_daemon = False
router_image = None
debug_image = False
+ test = None
class CTRexRunner:
"""This is an instance for generating a CTRexRunner"""
diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py
index 915cd682..4ff21b80 100755
--- a/scripts/automation/regression/trex_unit_test.py
+++ b/scripts/automation/regression/trex_unit_test.py
@@ -30,7 +30,7 @@ import outer_packages
import nose
from nose.plugins import Plugin
-import logging
+from nose.selector import Selector
import CustomLogger
import misc_methods
from rednose import RedNose
@@ -43,11 +43,30 @@ from trex_stl_lib.utils.GAObjClass import GAmanager
import trex
import socket
from pprint import pprint
-import subprocess
-import re
import time
from distutils.dir_util import mkpath
+# nose overrides
+
+# option to select wanted test by name without file, class etc.
+def new_Selector_wantMethod(self, method, orig_Selector_wantMethod = Selector.wantMethod):
+ result = orig_Selector_wantMethod(self, method)
+ if not CTRexScenario.test:
+ return result
+ else:
+ return CTRexScenario.test in getattr(method, '__name__', '')
+
+Selector.wantMethod = new_Selector_wantMethod
+
+def new_Selector_wantFunction(self, function, orig_Selector_wantFunction = Selector.wantFunction):
+ result = orig_Selector_wantFunction(self, function)
+ if not CTRexScenario.test:
+ return result
+ else:
+ return CTRexScenario.test in getattr(function, '__name__', '')
+
+Selector.wantFunction = new_Selector_wantFunction
+
# override nose's strange representation of setUpClass errors
def __suite_repr__(self):
if hasattr(self.context, '__module__'): # inside class, setUpClass etc.
@@ -59,6 +78,8 @@ def __suite_repr__(self):
nose.suite.ContextSuite.__repr__ = __suite_repr__
nose.suite.ContextSuite.__str__ = __suite_repr__
+# /nose overrides
+
def check_trex_path(trex_path):
if os.path.isfile('%s/trex_daemon_server' % trex_path):
return os.path.abspath(trex_path)
@@ -78,7 +99,7 @@ def get_trex_path():
def address_to_ip(address):
- for i in range(10):
+ for i in range(5):
try:
return socket.gethostbyname(address)
except:
@@ -149,6 +170,8 @@ class CTRexTestConfiguringPlugin(Plugin):
parser.add_option('--trex-args', action='store', default = '',
dest="trex_args",
help="Additional TRex arguments (--no-watchdog etc.).")
+ parser.add_option('-t', '--test', action='store', default = '', dest='test',
+ help='Test name to run (without file, class etc.)')
def configure(self, options, conf):
@@ -160,6 +183,7 @@ class CTRexTestConfiguringPlugin(Plugin):
self.json_verbose = options.json_verbose
self.telnet_verbose = options.telnet_verbose
self.no_daemon = options.no_daemon
+ CTRexScenario.test = options.test
if self.collect_only or self.functional:
return
if CTRexScenario.setup_dir and options.config_path:
@@ -181,7 +205,7 @@ class CTRexTestConfiguringPlugin(Plugin):
self.loggerPath = options.log_path
# initialize CTRexScenario global testing class, to be used by all tests
CTRexScenario.configuration = self.configuration
- CTRexScenario.no_daemon = self.no_daemon
+ CTRexScenario.no_daemon = options.no_daemon
CTRexScenario.benchmark = self.benchmark
CTRexScenario.modes = set(self.modes)
CTRexScenario.server_logs = self.server_logs
@@ -347,15 +371,14 @@ if __name__ == "__main__":
nose_argv += sys_args
- config_plugin = CTRexTestConfiguringPlugin()
- red_nose = RedNose()
+ addplugins = [RedNose(), CTRexTestConfiguringPlugin()]
result = True
try:
if len(CTRexScenario.test_types['functional_tests']):
additional_args = ['--func'] + CTRexScenario.test_types['functional_tests']
if xml_arg:
additional_args += ['--with-xunit', xml_arg.replace('.xml', '_functional.xml')]
- result = nose.run(argv = nose_argv + additional_args, addplugins = [red_nose, config_plugin])
+ result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins)
if len(CTRexScenario.test_types['stateful_tests']):
additional_args = ['--stf']
if '--warmup' in sys.argv:
@@ -365,14 +388,14 @@ if __name__ == "__main__":
additional_args.extend(['-a', '!client_package'])
if xml_arg:
additional_args += ['--with-xunit', xml_arg.replace('.xml', '_stateful.xml')]
- result = nose.run(argv = nose_argv + additional_args, addplugins = [red_nose, config_plugin]) and result
+ result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins) and result
if len(CTRexScenario.test_types['stateless_tests']):
additional_args = ['--stl', 'stateless_tests/stl_general_test.py:STLBasic_Test.test_connectivity'] + CTRexScenario.test_types['stateless_tests']
if not test_client_package:
additional_args.extend(['-a', '!client_package'])
if xml_arg:
additional_args += ['--with-xunit', xml_arg.replace('.xml', '_stateless.xml')]
- result = nose.run(argv = nose_argv + additional_args, addplugins = [red_nose, config_plugin]) and result
+ result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins) and result
#except Exception as e:
# result = False
# print(e)
diff --git a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
index f044f623..c8827afe 100755
--- a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
+++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
@@ -535,7 +535,7 @@ class CTRexClient(object):
finally:
self.prompt_verbose_data()
- def sample_until_condition (self, condition_func, time_between_samples = 5):
+ def sample_until_condition (self, condition_func, time_between_samples = 1):
"""
Automatically sets ongoing sampling of TRex data, with sampling rate described by time_between_samples.
@@ -549,7 +549,7 @@ class CTRexClient(object):
time_between_samples : int
determines the time between each sample of the server
- default value : **5**
+ default value : **1**
:return:
the first result object (see :class:`CTRexResult` for further details) of the TRex run on which the condition has been met.
@@ -579,7 +579,7 @@ class CTRexClient(object):
# this could come from provided method 'condition_func'
raise
- def sample_to_run_finish (self, time_between_samples = 5):
+ def sample_to_run_finish (self, time_between_samples = 1):
"""
Automatically sets automatically sampling of TRex data with sampling rate described by time_between_samples until TRex run finished.
@@ -587,7 +587,7 @@ class CTRexClient(object):
time_between_samples : int
determines the time between each sample of the server
- default value : **5**
+ default value : **1**
:return:
the latest result object (see :class:`CTRexResult` for further details) with sampled data.
@@ -609,7 +609,7 @@ class CTRexClient(object):
results = self.get_result_obj()
return results
- def sample_x_seconds (self, sample_time, time_between_samples = 5):
+ def sample_x_seconds (self, sample_time, time_between_samples = 1):
"""
Automatically sets ongoing sampling of TRex data for sample_time seconds, with sampling rate described by time_between_samples.
Does not stop the TRex afterwards!
@@ -623,7 +623,7 @@ class CTRexClient(object):
time_between_samples : int
determines the time between each sample of the server
- default value : **5**
+ default value : **1**
:return:
the first result object (see :class:`CTRexResult` for further details) of the TRex run after given sample_time.
@@ -1271,7 +1271,7 @@ class CTRexResult(object):
.. tip:: | Use '.' to enter one level deeper in dictionary hierarchy.
| Use '[i]' to access the i'th indexed object of an array.
- tree_path_to_key : regex
+ regex : regex
apply a regex to filter results out from a multiple results set.
Filter applies only on keys of dictionary type.
@@ -1299,7 +1299,7 @@ class CTRexResult(object):
.. tip:: | Use '.' to enter one level deeper in dictionary hierarchy.
| Use '[i]' to access the i'th indexed object of an array.
- tree_path_to_key : regex
+ regex : regex
apply a regex to filter results out from a multiple results set.
Filter applies only on keys of dictionary type.
@@ -1352,7 +1352,7 @@ class CTRexResult(object):
if not len(self._history):
return -1
- return len(self.__get_value_by_path(self._history[-1], 'trex-global.data', 'opackets-\d+'))
+ return len(self.get_last_value('trex-global.data', 'opackets-\d+'))
def update_result_data (self, latest_dump):
@@ -1383,6 +1383,7 @@ class CTRexResult(object):
# check for up to 2% change between expected and actual
if (self._current_tx_rate['m_tx_bps'] > 0.98 * self._expected_tx_rate['m_tx_expected_bps']):
self._done_warmup = True
+ latest_dump['warmup_barrier'] = True
# handle latency data
if self.latency_checked:
@@ -1427,12 +1428,12 @@ class CTRexResult(object):
for i, p in re.findall(r'(\d+)|([\w|-]+)', tree_path):
dct = dct[p or int(i)]
if regex is not None and isinstance(dct, dict):
- res = {}
- for key,val in dct.items():
- match = re.match(regex, key)
- if match:
- res[key]=val
- return res
+ res = {}
+ for key,val in dct.items():
+ match = re.match(regex, key)
+ if match:
+ res[key]=val
+ return res
else:
return dct
except (KeyError, TypeError):
@@ -1499,6 +1500,42 @@ class CTRexResult(object):
return result
+ # history iterator after warmup period
+ def _get_steady_state_history_iterator(self):
+ if not self.is_done_warmup():
+ raise Exception('Warm-up period not finished')
+ for index, res in enumerate(self._history):
+ if 'warmup_barrier' in res:
+ for steady_state_index in range(index, max(index, len(self._history) - 1)):
+ yield self._history[steady_state_index]
+ return
+ for index in range(len(self._history) - 1):
+ yield self._history[index]
+
+
+ def get_avg_steady_state_value(self, tree_path_to_key):
+ '''
+ Gets average value after warmup period.
+ For example: <result object>.get_avg_steady_state_value('trex-global.data.m_tx_bps')
+ Usually more accurate than latest history value.
+
+ :parameters:
+ tree_path_to_key : str
+ defines a path to desired data.
+
+ :return:
+ average value at steady state
+
+ :raises:
+ Exception in case steady state period was not reached or tree_path_to_key was not found in result.
+ '''
+ values_arr = [self.__get_value_by_path(res, tree_path_to_key) for res in self._get_steady_state_history_iterator()]
+ values_arr = list(filter(lambda x: x is not None, values_arr))
+ if not values_arr:
+ raise Exception('All the keys are None, probably wrong tree_path_to_key: %s' % tree_path_to_key)
+ return sum(values_arr) / float(len(values_arr))
+
+
if __name__ == "__main__":
c = CTRexClient('127.0.0.1')
print('restarting daemon')
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 c1833754..dc0035b3 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 ###########
@@ -1664,6 +1667,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):
@@ -1878,8 +1898,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 3effa1f0..678adb4e 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
+get_number_of_bytes_cache = {}
+# get number of bytes: '64b'->64, '9kb'->9000 etc.
+def get_number_of_bytes(val):
+ if val not in get_number_of_bytes_cache:
+ get_number_of_bytes_cache[val] = int(val[:-1].replace('k', '000'))
+ return get_number_of_bytes_cache[val]
+
# 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,73 @@ 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(use_1sec_cache = True)
+ stats_table = text_tables.TRexTextTable()
+ if util_stats:
+ if '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.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])
+ else:
+ stats_table.add_row(['No Data.'])
+ return {'cpu_util(%)': ExportableStats(None, stats_table)}
+
+ def _generate_mbuf_util_stats(self):
+ util_stats = self._util_stats_ref.get_stats(use_1sec_cache = True)
+ stats_table = text_tables.TRexTextTable()
+ if util_stats:
+ if '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 = get_number_of_bytes)
+ self._util_stats_ref.mbuf_types_list = mbuf_keys
+ types_len = len(self._util_stats_ref.mbuf_types_list)
+ stats_table.set_cols_align(['l'] + ['r'] * (types_len + 1))
+ stats_table.set_cols_width([10] + [7] * (types_len + 1))
+ stats_table.set_cols_dtype(['t'] * (types_len + 2))
+ stats_table.header([''] + self._util_stats_ref.mbuf_types_list + ['RAM(MB)'])
+ total_list = []
+ sum_totals = 0
+ for mbuf_type in self._util_stats_ref.mbuf_types_list:
+ sum_totals += first_socket_mbufs[mbuf_type][1] * get_number_of_bytes(mbuf_type) + 64
+ total_list.append(first_socket_mbufs[mbuf_type][1])
+ sum_totals *= len(list(mbuf_stats.values()))
+ total_list.append(int(sum_totals/1e6))
+ stats_table.add_row(['Total:'] + total_list)
+ stats_table.add_row(['Used:'] + [''] * (types_len + 1))
+ for socket_name in sorted(list(mbuf_stats.keys())):
+ mbufs = mbuf_stats[socket_name]
+ socket_show_name = socket_name.replace('cpu-', '').replace('-', ' ').capitalize() + ':'
+ sum_used = 0
+ used_list = []
+ percentage_list = []
+ for mbuf_type in self._util_stats_ref.mbuf_types_list:
+ used = mbufs[mbuf_type][1] - mbufs[mbuf_type][0]
+ sum_used += used * get_number_of_bytes(mbuf_type) + 64
+ used_list.append(used)
+ percentage_list.append('%s%%' % int(100 * used / mbufs[mbuf_type][1]))
+ used_list.append(int(sum_used/1e6))
+ stats_table.add_row([socket_show_name] + used_list)
+ stats_table.add_row(['Percent:'] + percentage_list + [''])
+ else:
+ stats_table.add_row(['No Data.'])
+ 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
@@ -1276,7 +1359,27 @@ 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
+ self.last_update_ts = 0
+
+ def get_stats(self, use_1sec_cache = False):
+ time_now = time.time()
+ if self.last_update_ts + 1 < time_now or not self.history or not use_1sec_cache:
+ if self.client.is_connected():
+ rc = self.client._transmit('get_utilization')
+ if not rc:
+ raise Exception(rc)
+ self.last_update_ts = time_now
+ self.history.append(rc.data())
+ else:
+ self.history.append({})
+ 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 bb28d3af..ceaf1af8 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
@@ -340,6 +342,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',
@@ -365,7 +375,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/scripts/master_daemon.py b/scripts/master_daemon.py
index aa49f207..a44f55a8 100755
--- a/scripts/master_daemon.py
+++ b/scripts/master_daemon.py
@@ -181,7 +181,7 @@ parser.add_argument('--type', '--daemon-type', '--daemon_type', choices = daemon
action = 'store', help = 'Specify daemon type to start/stop etc.\nDefault is master_daemon.')
args = parser.parse_args()
-args.trex_dir = os.path.normpath(args.trex_dir)
+args.trex_dir = os.path.abspath(args.trex_dir)
args.daemon_type = args.daemon_type or 'master_daemon'
stl_rpc_proxy_dir = os.path.join(args.trex_dir, 'automation', 'trex_control_plane', 'stl', 'examples')
diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp
index f9e96b6b..6b5acd42 100755
--- a/src/bp_sim.cpp
+++ b/src/bp_sim.cpp
@@ -498,40 +498,23 @@ 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+="," ;
- }
- return (json);
+void 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);
}
@@ -548,30 +531,24 @@ 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\":{";
+void CGlobalInfo::dump_pool_as_json(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);
+ m_mem_pool[i].dump_as_json(json["mbuf_stats"][socket_id]);
}
}
+}
- 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){
+ Json::Value json;
+ dump_pool_as_json(json);
+ return (json.toStyledString());
}
void CGlobalInfo::free_pools(){
@@ -4517,6 +4494,16 @@ double CFlowGenList::GetCpuUtil(){
return (c/m_threads_info.size());
}
+double CFlowGenList::GetCpuUtilRaw(){
+ int i;
+ double c=0.0;
+ for (i=0; i<(int)m_threads_info.size(); i++) {
+ CFlowGenListPerThread * lp=m_threads_info[i];
+ c+=lp->m_cpu_cp_u.GetValRaw();
+ }
+ return (c/m_threads_info.size());
+}
+
void CFlowGenList::UpdateFast(){
diff --git a/src/bp_sim.h b/src/bp_sim.h
index 56e37272..05900351 100755
--- a/src/bp_sim.h
+++ b/src/bp_sim.h
@@ -1182,13 +1182,10 @@ public:
void dump_in_case_of_error(FILE *fd);
- std::string dump_as_json(uint8_t id,bool last);
+ void dump_as_json(Json::Value &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 */
@@ -1282,7 +1279,8 @@ public:
}
- static std::string dump_pool_as_json(void);
+ static void dump_pool_as_json(Json::Value &json);
+ static std::string dump_pool_as_json_str(void);
public:
@@ -3879,6 +3877,7 @@ public:
void DumpPktSize();
void UpdateFast();
double GetCpuUtil();
+ double GetCpuUtilRaw();
public:
double get_total_kcps();
diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h
index a52f9e60..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 <string.h>
#include "flow_stat_parser.h"
#include "trex_defs.h"
+#include <json/json.h>
/**
* Global stats
@@ -156,6 +157,8 @@ public:
virtual bool get_promiscuous(uint8_t port_id) const = 0;
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() {}
};
@@ -188,6 +191,8 @@ public:
bool get_promiscuous(uint8_t port_id) const;
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;
};
@@ -252,6 +257,8 @@ public:
void flush_dp_messages() const {
}
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 906aa2b7..e98c2305 100644
--- a/src/main_dpdk.cpp
+++ b/src/main_dpdk.cpp
@@ -2445,6 +2445,7 @@ public:
float m_active_flows;
float m_open_flows;
float m_cpu_util;
+ float m_cpu_util_raw;
float m_rx_cpu_util;
float m_bw_per_core;
uint8_t m_threads;
@@ -2454,7 +2455,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);
@@ -2494,7 +2495,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,";
@@ -2512,6 +2513,7 @@ void CGlobalStats::dump_json(std::string & json, bool baseline,uint32_t stats_ti
#define GET_FIELD_PORT(p,f) get_field_port(p,std::string(#f),lp->f)
json+=GET_FIELD(m_cpu_util);
+ json+=GET_FIELD(m_cpu_util_raw);
json+=GET_FIELD(m_bw_per_core);
json+=GET_FIELD(m_rx_cpu_util);
json+=GET_FIELD(m_platform_factor);
@@ -2561,10 +2563,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}}" ;
}
@@ -3581,6 +3579,7 @@ void CGlobalTRex::get_stats(CGlobalStats & stats){
stats.m_num_of_ports = m_max_ports;
stats.m_cpu_util = m_fl.GetCpuUtil();
+ stats.m_cpu_util_raw = m_fl.GetCpuUtilRaw();
if (get_is_stateless()) {
stats.m_rx_cpu_util = m_rx_sl.get_cpu_util();
}
@@ -3812,7 +3811,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 */
@@ -3906,7 +3905,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());
}
}
@@ -5794,6 +5793,20 @@ int TrexDpdkPlatformApi::get_active_pgids(flow_stat_active_t &result) const {
return g_trex.m_trex_stateless->m_rx_flow_stat.get_active_pgids(result);
}
+int TrexDpdkPlatformApi::get_cpu_util_full(cpu_util_full_t &cpu_util_full) const {
+ cpu_util_full.resize((int)g_trex.m_fl.m_threads_info.size());
+ 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];
+ lp->m_cpu_cp_u.GetHistory(cpu_util_full[thread_id]);
+ }
+ return 0;
+}
+
+int TrexDpdkPlatformApi::get_mbuf_util(Json::Value &mbuf_pool) const {
+ CGlobalInfo::dump_pool_as_json(mbuf_pool);
+ 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 68ea2587..27010e0e 100644
--- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp
+++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp
@@ -157,6 +157,29 @@ TrexRpcCmdGetActivePGIds::_run(const Json::Value &params, Json::Value &result) {
return (TREX_RPC_CMD_OK);
}
+// get utilization of CPU per thread with up to 20 latest values + mbufs per socket
+trex_rpc_cmd_rc_e
+TrexRpcCmdGetUtilization::_run(const Json::Value &params, Json::Value &result) {
+ cpu_util_full_t cpu_util_full;
+
+ Json::Value &section = result["result"];
+
+ 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["cpu"][thread_id].append(cpu_util_full[thread_id][history_id]);
+ }
+ }
+ return (TREX_RPC_CMD_OK);
+}
+
/**
* get the CPU model
*
diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h
index affa65c1..2776727d 100644
--- a/src/rpc-server/commands/trex_rpc_cmds.h
+++ b/src/rpc-server/commands/trex_rpc_cmds.h
@@ -67,6 +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(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 7104792e..6144d265 100644
--- a/src/rpc-server/trex_rpc_cmds_table.cpp
+++ b/src/rpc-server/trex_rpc_cmds_table.cpp
@@ -39,6 +39,7 @@ TrexRpcCommandsTable::TrexRpcCommandsTable() {
register_command(new TrexRpcCmdGetCmds());
register_command(new TrexRpcCmdGetVersion());
register_command(new TrexRpcCmdGetActivePGIds());
+ 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 665c1edc..bbf3f3ba 100644
--- a/src/trex_defs.h
+++ b/src/trex_defs.h
@@ -14,6 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#include <set>
+#include <queue>
+#include <vector>
#ifndef __TREX_DEFS_H__
#define __TREX_DEFS_H__
@@ -36,5 +38,7 @@ limitations under the License.
typedef std::set<uint32_t> flow_stat_active_t;
typedef std::set<uint32_t>::iterator flow_stat_active_it_t;
+typedef std::vector<std::vector<uint8_t>> cpu_util_full_t;
+typedef std::vector<uint8_t> cpu_vct_t;
#endif
diff --git a/src/utl_cpuu.cpp b/src/utl_cpuu.cpp
index 4d75cf6f..7786356e 100755
--- a/src/utl_cpuu.cpp
+++ b/src/utl_cpuu.cpp
@@ -25,7 +25,9 @@ limitations under the License.
void CCpuUtlCp::Create(CCpuUtlDp * cdp){
m_dpcpu=cdp;
- m_cpu_util=0.0;
+ 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;
}
@@ -41,17 +43,34 @@ void CCpuUtlCp::Update(){
m_work++;
}
if (m_ticks==100) {
- double window_cpu_u = ((double)m_work/(double)m_ticks);
/* LPF*/
- m_cpu_util = (m_cpu_util*0.75)+(window_cpu_u*0.25);
+ 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 % */
+/* return cpu % Smoothed */
double CCpuUtlCp::GetVal(){
- return (m_cpu_util*100);
+ return (m_cpu_util_lpf);
+}
+
+/* return cpu % Raw */
+uint8_t CCpuUtlCp::GetValRaw(){
+ return (m_cpu_util[m_history_latest_index]);
}
+/* get cpu % utilization history */
+void CCpuUtlCp::GetHistory(cpu_vct_t &cpu_vct){
+ cpu_vct.clear();
+ for (int i = m_history_latest_index + m_history_size; i > m_history_latest_index; i--) {
+ cpu_vct.push_back(m_cpu_util[i % m_history_size]);
+ }
+}
+
+/* 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 e5305783..109fff4f 100755
--- a/src/utl_cpuu.h
+++ b/src/utl_cpuu.h
@@ -22,6 +22,8 @@ limitations under the License.
*/
#include <stdint.h>
+#include <cstring>
+#include "trex_defs.h"
#include "os_time.h"
#include "mbuf.h"
@@ -56,13 +58,18 @@ public:
void Update();
/* return cpu % */
double GetVal();
-
+ uint8_t GetValRaw();
+ void GetHistory(cpu_vct_t &cpu_vct);
private:
- CCpuUtlDp * m_dpcpu;
- uint16_t m_ticks;
- uint16_t m_work;
+ void AppendHistory(uint8_t);
+ CCpuUtlDp * m_dpcpu;
+ uint8_t m_ticks;
+ uint8_t m_work;
- double m_cpu_util;
+ 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;
};
#endif