diff options
Diffstat (limited to 'src/sim/trex_sim_stateless.cpp')
-rw-r--r-- | src/sim/trex_sim_stateless.cpp | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/src/sim/trex_sim_stateless.cpp b/src/sim/trex_sim_stateless.cpp new file mode 100644 index 00000000..6d470704 --- /dev/null +++ b/src/sim/trex_sim_stateless.cpp @@ -0,0 +1,537 @@ +/* + Itay Marom + Cisco Systems, Inc. +*/ + +/* +Copyright (c) 2015-2015 Cisco Systems, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "trex_sim.h" +#include <trex_stateless.h> +#include <trex_stateless_messaging.h> +#include <trex_rpc_cmd_api.h> +#include <json/json.h> +#include <stdexcept> +#include <sstream> +#include <trex_streams_compiler.h> + +using namespace std; + +class DPCoreStats { +public: + DPCoreStats() { + m_simulated_pkts = 0; + m_non_active_pkts = 0; + m_written_pkts = 0; + } + + uint64_t get_on_wire_count() { + return (m_simulated_pkts - m_non_active_pkts); + } + + uint64_t m_simulated_pkts; + uint64_t m_non_active_pkts; + uint64_t m_written_pkts; +}; + +/****** utils ******/ +static string format_num(double num, const string &suffix = "") { + const char x[] = {' ','K','M','G','T','P'}; + + double my_num = num; + + for (int i = 0; i < sizeof(x); i++) { + if (std::abs(my_num) < 1000.0) { + stringstream ss; + + char buf[100]; + snprintf(buf, sizeof(buf), "%.2f", my_num); + + ss << buf << " " << x[i] << suffix; + return ss.str(); + + } else { + my_num /= 1000.0; + } + } + + return "NaN"; +} + + +class SimRunException : public std::runtime_error +{ +public: + SimRunException() : std::runtime_error("") { + + } + SimRunException(const std::string &what) : std::runtime_error(what) { + } +}; + +/** + * handler for DP to CP messages + * + * @author imarom (19-Nov-15) + */ +class DpToCpHandler { +public: + virtual void handle(TrexStatelessDpToCpMsgBase *msg) = 0; +}; + +/************************ + * + * stateless sim object + * +************************/ +class SimPublisher : public TrexPublisher { +public: + + /* override create */ + bool Create(uint16_t port, bool disable) { + return true; + } + + void Delete() { + + } + + void publish_json(const std::string &s) { + } + + virtual ~SimPublisher() { + } +}; + +/************************ + * + * stateless sim object + * +************************/ + +SimStateless::SimStateless() { + m_publisher = NULL; + m_dp_to_cp_handler = NULL; + m_verbose = false; + m_dp_core_count = -1; + m_dp_core_index = -1; + m_port_count = -1; + m_limit = 0; + m_is_dry_run = false; + + /* override ownership checks */ + TrexRpcCommand::test_set_override_ownership(true); + TrexRpcCommand::test_set_override_api(true); +} + + +/** + * on the simulation we first construct CP and then DP + * the only way to "assume" which DP will be active during + * the run is by checking for pending CP messages on the cores + * + * @author imarom (8/10/2016) + */ +void +SimStateless::find_active_dp_cores() { + for (int core_index = 0; core_index < m_dp_core_count; core_index++) { + CFlowGenListPerThread *lpt = m_fl.m_threads_info[core_index]; + if (lpt->are_any_pending_cp_messages()) { + m_active_dp_cores.push_back(core_index); + } + } +} + +int +SimStateless::run(const string &json_filename, + const string &out_filename, + int port_count, + int dp_core_count, + int dp_core_index, + int limit, + bool is_dry_run) { + + assert(dp_core_count > 0); + + /* -1 means its not set or positive value between 0 and the dp core count - 1*/ + assert( (dp_core_index == -1) || ( in_range(dp_core_index, 0, dp_core_count - 1)) ); + + m_dp_core_count = dp_core_count; + m_dp_core_index = dp_core_index; + m_port_count = port_count; + m_limit = limit; + m_is_dry_run = is_dry_run; + + prepare_dataplane(); + prepare_control_plane(); + + try { + execute_json(json_filename); + } catch (const SimRunException &e) { + std::cout << "*** test failed ***\n\n" << e.what() << "\n"; + return (-1); + } + + find_active_dp_cores(); + + run_dp(out_filename); + + return 0; +} + + +SimStateless::~SimStateless() { + + if (get_stateless_obj()) { + delete get_stateless_obj(); + set_stateless_obj(NULL); + } + + if (m_publisher) { + delete m_publisher; + m_publisher = NULL; + } + + m_fl.Delete(); +} + +/** + * prepare control plane for test + * + * @author imarom (28-Dec-15) + */ +void +SimStateless::prepare_control_plane() { + TrexStatelessCfg cfg; + + m_publisher = new SimPublisher(); + + TrexRpcServerConfig rpc_req_resp_cfg(TrexRpcServerConfig::RPC_PROT_MOCK, 0, NULL); + + cfg.m_port_count = m_port_count; + cfg.m_rpc_req_resp_cfg = &rpc_req_resp_cfg; + cfg.m_rpc_server_verbose = false; + cfg.m_platform_api = new SimPlatformApi(m_dp_core_count); + cfg.m_publisher = m_publisher; + + set_stateless_obj(new TrexStateless(cfg)); + + get_stateless_obj()->launch_control_plane(); + + for (auto &port : get_stateless_obj()->get_port_list()) { + port->acquire("test", 0, true); + } + +} + + +/** + * prepare the data plane for test + * + */ +void +SimStateless::prepare_dataplane() { + + CGlobalInfo::m_options.m_expected_portd = m_port_count; + CGlobalInfo::m_options.m_run_mode = CParserOption::RUN_MODE_INTERACTIVE; + + assert(CMsgIns::Ins()->Create(m_dp_core_count)); + m_fl.Create(); + m_fl.generate_p_thread_info(m_dp_core_count); + + for (int i = 0; i < m_dp_core_count; i++) { + if (should_capture_core(i)) { + m_fl.m_threads_info[i]->set_vif(&m_erf_vif); + } else { + m_fl.m_threads_info[i]->set_vif(&m_null_erf_vif); + } + } +} + + + +void +SimStateless::execute_json(const std::string &json_filename) { + std::string rep; + std::ifstream test(json_filename); + std::stringstream buffer; + buffer << test.rdbuf(); + + try { + rep = get_stateless_obj()->get_rpc_server()->test_inject_request(buffer.str()); + } catch (TrexRpcException &e) { + throw SimRunException(e.what()); + } + + Json::Value root; + Json::Reader reader; + reader.parse(rep, root, false); + + if (is_verbose()) { + std::cout << "server response: \n\n" << root << "\n\n"; + } + + validate_response(root); + +} + +void +SimStateless::validate_response(const Json::Value &resp) { + std::stringstream ss; + + if (resp.isArray()) { + for (const auto &single : resp) { + if (single["error"] != Json::nullValue) { + ss << "failed with:\n\n" << single["error"] << "\n\n"; + throw SimRunException(ss.str()); + } + } + } else { + if (resp["error"] != Json::nullValue) { + ss << "failed with:\n\n" << resp["error"] << "\n\n"; + throw SimRunException(ss.str()); + } + } + +} + +static inline bool is_debug() { + #ifdef DEBUG + return true; + #else + return false; + #endif +} + +void +SimStateless::show_intro(const std::string &out_filename) { + double pps; + double bps_L1; + double bps_L2; + double percentage; + + std::cout << "\nGeneral info:\n"; + std::cout << "------------\n\n"; + + std::cout << "image type: " << (is_debug() ? "debug" : "release") << "\n"; + std::cout << "I/O output: " << (m_is_dry_run ? "*DRY*" : out_filename) << "\n"; + + if (m_limit > 0) { + std::cout << "packet limit: " << m_limit << "\n"; + } else { + std::cout << "packet limit: " << "*NO LIMIT*" << "\n"; + } + + if (m_dp_core_index != -1) { + std::cout << "core recording: " << m_dp_core_index << "\n"; + } else { + std::cout << "core recording: merge all\n"; + } + + std::cout << "\nConfiguration info:\n"; + std::cout << "-------------------\n\n"; + + std::cout << "ports: " << m_port_count << "\n"; + std::cout << "cores: " << m_dp_core_count << "\n"; + + + std::cout << "\nPort Config:\n"; + std::cout << "------------\n\n"; + + TrexStatelessPort *port = get_stateless_obj()->get_port_by_id(0); + + std::cout << "stream count: " << port->get_stream_count() << "\n"; + + port->get_port_effective_rate(pps, bps_L1, bps_L2, percentage); + + std::cout << "max PPS : " << format_num(pps, "pps") << "\n"; + std::cout << "max BPS L1 : " << format_num(bps_L1, "bps") << "\n"; + std::cout << "max BPS L2 : " << format_num(bps_L2, "bps") << "\n"; + std::cout << "line util. : " << format_num(percentage, "%") << "\n"; + + std::cout << "\n\nStarting simulation...\n"; +} + +void +SimStateless::run_dp(const std::string &out_filename) { + std::vector<DPCoreStats> core_stats(m_dp_core_count); + DPCoreStats total; + + show_intro(out_filename); + + if (is_multiple_capture()) { + for (int i : m_active_dp_cores) { + std::stringstream ss; + ss << out_filename << "-" << i; + run_dp_core(i, ss.str(), core_stats, total); + } + + } else { + for (int i : m_active_dp_cores) { + run_dp_core(i, out_filename, core_stats, total); + } + } + + /* cleanup */ + cleanup(); + + + std::cout << "\n\nSimulation summary:\n"; + std::cout << "-------------------\n\n"; + + for (int i = 0; i < m_dp_core_count; i++) { + std::cout << "core index " << i << "\n"; + std::cout << "-----------------\n\n"; + std::cout << " simulated packets : " << core_stats[i].m_simulated_pkts << "\n"; + std::cout << " non active packets : " << core_stats[i].m_non_active_pkts << "\n"; + std::cout << " on-wire packets : " << core_stats[i].get_on_wire_count() << "\n\n"; + } + + std::cout << "Total:" << "\n"; + std::cout << "-----------------\n\n"; + std::cout << " simulated packets : " << total.m_simulated_pkts << "\n"; + std::cout << " non active packets : " << total.m_non_active_pkts << "\n"; + std::cout << " on-wire packets : " << total.get_on_wire_count() << "\n\n"; + + if (m_is_dry_run) { + std::cout << "*DRY RUN* - no packets were written\n"; + } else { + std::cout << "written " << total.m_written_pkts << " packets " << "to '" << out_filename << "'\n\n"; + } + + std::cout << "\n"; +} + +void +SimStateless::cleanup() { + + for (int port_id = 0; port_id < get_stateless_obj()->get_port_count(); port_id++) { + get_stateless_obj()->get_port_by_id(port_id)->stop_traffic(); + get_stateless_obj()->get_port_by_id(port_id)->remove_and_delete_all_streams(); + } + for (int i = 0; i < m_dp_core_count; i++) { + flush_cp_to_dp_messages_core(i); + flush_dp_to_cp_messages_core(i); + } +} + +uint64_t +SimStateless::get_limit_per_core(int core_index) { + /* global no limit ? */ + if (m_limit == 0) { + return (0); + } else { + uint64_t l = std::max((uint64_t)1, m_limit / m_active_dp_cores.size()); + if (core_index == m_active_dp_cores[0]) { + l += (m_limit % m_active_dp_cores.size()); + } + return l; + } +} + +void +SimStateless::run_dp_core(int core_index, + const std::string &out_filename, + std::vector<DPCoreStats> &stats, + DPCoreStats &total) { + + CFlowGenListPerThread *lpt = m_fl.m_threads_info[core_index]; + + lpt->start_stateless_simulation_file((std::string)out_filename, CGlobalInfo::m_options.preview, get_limit_per_core(core_index)); + lpt->start_stateless_daemon_simulation(); + lpt->stop_stateless_simulation_file(); + + flush_dp_to_cp_messages_core(core_index); + + /* core */ + stats[core_index].m_simulated_pkts = lpt->m_node_gen.m_cnt; + stats[core_index].m_non_active_pkts = lpt->m_node_gen.m_non_active; + + /* total */ + total.m_simulated_pkts += lpt->m_node_gen.m_cnt; + total.m_non_active_pkts += lpt->m_node_gen.m_non_active; + + if (should_capture_core(core_index)) { + stats[core_index].m_written_pkts = (lpt->m_node_gen.m_cnt - lpt->m_node_gen.m_non_active); + total.m_written_pkts += (lpt->m_node_gen.m_cnt - lpt->m_node_gen.m_non_active); + } +} + + +void +SimStateless::flush_dp_to_cp_messages_core(int core_index) { + + CNodeRing *ring = CMsgIns::Ins()->getCpDp()->getRingDpToCp(core_index); + + while ( true ) { + CGenNode * node = NULL; + if (ring->Dequeue(node) != 0) { + break; + } + assert(node); + + TrexStatelessDpToCpMsgBase * msg = (TrexStatelessDpToCpMsgBase *)node; + if (m_dp_to_cp_handler) { + m_dp_to_cp_handler->handle(msg); + } + + delete msg; + } +} + +void +SimStateless::flush_cp_to_dp_messages_core(int core_index) { + + CNodeRing *ring = CMsgIns::Ins()->getCpDp()->getRingCpToDp(core_index); + + while ( true ) { + CGenNode * node = NULL; + if (ring->Dequeue(node) != 0) { + break; + } + assert(node); + + TrexStatelessCpToDpMsgBase * msg = (TrexStatelessCpToDpMsgBase *)node; + delete msg; + } +} + +bool +SimStateless::should_capture_core(int i) { + + /* dry run - no core should be recordered */ + if (m_is_dry_run) { + return false; + } + + /* no specific core index ? record all */ + if (m_dp_core_index == -1) { + return true; + } else { + return (i == m_dp_core_index); + } +} + +bool +SimStateless::is_multiple_capture() { + /* dry run - no core should be recordered */ + if (m_is_dry_run) { + return false; + } + + return ( (m_dp_core_count > 1) && (m_dp_core_index == -1) ); +} + |