diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gtest/trex_stateless_gtest.cpp | 200 | ||||
-rw-r--r-- | src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 6 | ||||
-rw-r--r-- | src/stateless/cp/trex_stream.h | 8 | ||||
-rw-r--r-- | src/stateless/cp/trex_streams_compiler.cpp | 301 | ||||
-rw-r--r-- | src/stateless/cp/trex_streams_compiler.h | 26 |
5 files changed, 525 insertions, 16 deletions
diff --git a/src/gtest/trex_stateless_gtest.cpp b/src/gtest/trex_stateless_gtest.cpp index c845c32e..9148d5ae 100644 --- a/src/gtest/trex_stateless_gtest.cpp +++ b/src/gtest/trex_stateless_gtest.cpp @@ -29,7 +29,7 @@ limitations under the License. #include <trex_stream.h> #include <trex_stateless_port.h> #include <trex_rpc_server_api.h> - +#include <iostream> #define EXPECT_EQ_UINT32(a,b) EXPECT_EQ((uint32_t)(a),(uint32_t)(b)) @@ -239,7 +239,7 @@ TEST_F(basic_stl, single_pkt_burst1) { TrexStream * stream1 = new TrexStream(TrexStream::stSINGLE_BURST, 0,0); stream1->set_pps(1.0); - stream1->set_signle_burtst(5); + stream1->set_single_burst(5); stream1->m_enabled = true; stream1->m_self_start = true; @@ -344,7 +344,7 @@ TEST_F(basic_stl, multi_pkt1) { streams.push_back(stream1); - TrexStream * stream2 = new TrexStream(TrexStream::stCONTINUOUS,0,0); + TrexStream * stream2 = new TrexStream(TrexStream::stCONTINUOUS,0,1); stream2->set_pps(2.0); stream2->m_enabled = true; @@ -482,6 +482,200 @@ TEST_F(basic_stl, multi_burst1) { EXPECT_EQ_UINT32(1, res?1:0)<< "pass"; } +/********************************************* Itay Tests Start *************************************/ + +/** + * check that continous stream does not point to another stream + * (makes no sense) + */ +TEST_F(basic_stl, compile_bad_1) { + + TrexStreamsCompiler compile; + std::vector<TrexStream *> streams; + + TrexStream * stream1 = new TrexStream(TrexStream::stCONTINUOUS,0,2); + stream1->m_enabled = true; + stream1->set_pps(52.0); + stream1->m_next_stream_id = 3; + + streams.push_back(stream1); + + TrexStreamsCompiledObj comp_obj(0,1.0); + + std::string err_msg; + EXPECT_FALSE(compile.compile(streams, comp_obj, &err_msg)); + + delete stream1; + +} + +/** + * check for streams pointing to non exsistant streams + * + * @author imarom (16-Nov-15) + */ +TEST_F(basic_stl, compile_bad_2) { + + TrexStreamsCompiler compile; + std::vector<TrexStream *> streams; + + TrexStream * stream1 = new TrexStream(TrexStream::stSINGLE_BURST,0,1); + stream1->m_enabled = true; + stream1->set_pps(1.0); + stream1->set_single_burst(200); + + /* non existant next stream */ + stream1->m_next_stream_id = 5; + + TrexStream * stream2 = new TrexStream(TrexStream::stCONTINUOUS,0,2); + stream1->set_pps(52.0); + + streams.push_back(stream1); + streams.push_back(stream2); + + TrexStreamsCompiledObj comp_obj(0,1.0); + + std::string err_msg; + EXPECT_FALSE(compile.compile(streams, comp_obj, &err_msg)); + + delete stream1; + delete stream2; + +} + +/** + * check for "dead streams" in the mesh + * a streams that cannot be reached + * + * @author imarom (16-Nov-15) + */ +TEST_F(basic_stl, compile_bad_3) { + + TrexStreamsCompiler compile; + std::vector<TrexStream *> streams; + TrexStream *stream; + + /* stream 1 */ + stream = new TrexStream(TrexStream::stSINGLE_BURST, 0, 231); + stream->m_enabled = true; + stream->set_pps(1.0); + stream->set_single_burst(200); + + stream->m_next_stream_id = 5481; + stream->m_self_start = true; + + streams.push_back(stream); + /* stream 2 */ + stream = new TrexStream(TrexStream::stCONTINUOUS, 0, 5481); + stream->m_enabled = true; + stream->m_next_stream_id = -1; + stream->m_self_start = false; + stream->set_pps(52.0); + + streams.push_back(stream); + + /* stream 3 */ + + stream = new TrexStream(TrexStream::stSINGLE_BURST, 0, 1928); + stream->m_enabled = true; + stream->set_pps(1.0); + stream->set_single_burst(200); + + stream->m_next_stream_id = -1; + stream->m_self_start = true; + + streams.push_back(stream); + + /* stream 4 */ + + stream = new TrexStream(TrexStream::stSINGLE_BURST, 0, 41231); + stream->m_enabled = true; + stream->set_pps(1.0); + stream->set_single_burst(200); + + stream->m_next_stream_id = 3928; + stream->m_self_start = false; + + streams.push_back(stream); + + /* stream 5 */ + + stream = new TrexStream(TrexStream::stSINGLE_BURST, 0, 3928); + stream->m_enabled = true; + stream->set_pps(1.0); + stream->set_single_burst(200); + + stream->m_next_stream_id = 41231; + stream->m_self_start = false; + + streams.push_back(stream); + + /* compile */ + TrexStreamsCompiledObj comp_obj(0,1.0); + + std::string err_msg; + EXPECT_FALSE(compile.compile(streams, comp_obj, &err_msg)); + + for (auto stream : streams) { + delete stream; + } + +} + +TEST_F(basic_stl, compile_with_warnings) { + + TrexStreamsCompiler compile; + std::vector<TrexStream *> streams; + TrexStream *stream; + + /* stream 1 */ + stream = new TrexStream(TrexStream::stSINGLE_BURST, 0, 231); + stream->m_enabled = true; + stream->set_pps(1.0); + stream->set_single_burst(200); + + stream->m_next_stream_id = 1928; + stream->m_self_start = true; + + streams.push_back(stream); + + /* stream 2 */ + stream = new TrexStream(TrexStream::stSINGLE_BURST, 0, 5481); + stream->m_enabled = true; + stream->m_next_stream_id = 1928; + stream->m_self_start = true; + stream->set_pps(52.0); + + streams.push_back(stream); + + /* stream 3 */ + + stream = new TrexStream(TrexStream::stSINGLE_BURST, 0, 1928); + stream->m_enabled = true; + stream->set_pps(1.0); + stream->set_single_burst(200); + + stream->m_next_stream_id = -1; + stream->m_self_start = true; + + streams.push_back(stream); + + + + /* compile */ + TrexStreamsCompiledObj comp_obj(0,1.0); + + std::string err_msg; + EXPECT_TRUE(compile.compile(streams, comp_obj, &err_msg)); + + EXPECT_TRUE(compile.get_last_compile_warnings().size() == 1); + + for (auto stream : streams) { + delete stream; + } + +} +/********************************************* Itay Tests End *************************************/ diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index 5ec92afc..cdd13ed6 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -143,6 +143,10 @@ TrexRpcCmdAddStream::allocate_new_stream(const Json::Value §ion, uint8_t por stream = new TrexStream( TrexStream::stCONTINUOUS, port_id, stream_id); stream->set_pps(pps); + if (stream->m_next_stream_id != -1) { + generate_parse_err(result, "continious stream cannot provide next stream id - only -1 is valid"); + } + } else if (type == "single_burst") { uint32_t total_pkts = parse_int(mode, "total_pkts", result); @@ -150,7 +154,7 @@ TrexRpcCmdAddStream::allocate_new_stream(const Json::Value §ion, uint8_t por stream = new TrexStream(TrexStream::stSINGLE_BURST,port_id, stream_id); stream->set_pps(pps); - stream->set_signle_burtst(total_pkts); + stream->set_single_burst(total_pkts); } else if (type == "multi_burst") { diff --git a/src/stateless/cp/trex_stream.h b/src/stateless/cp/trex_stream.h index 151723ad..c2628cc3 100644 --- a/src/stateless/cp/trex_stream.h +++ b/src/stateless/cp/trex_stream.h @@ -92,21 +92,21 @@ public: m_type = type; } - uint8_t get_type(void){ + uint8_t get_type(void) const { return ( m_type ); } void set_multi_burst(uint32_t burst_total_pkts, - uint32_t num_bursts, - double ibg_usec){ + uint32_t num_bursts, + double ibg_usec) { m_burst_total_pkts = burst_total_pkts; m_num_bursts = num_bursts; m_ibg_usec = ibg_usec; } - void set_signle_burtst(uint32_t burst_total_pkts){ + void set_single_burst(uint32_t burst_total_pkts){ set_multi_burst(burst_total_pkts,1,0.0); } diff --git a/src/stateless/cp/trex_streams_compiler.cpp b/src/stateless/cp/trex_streams_compiler.cpp index 580db51c..0c3b4ef0 100644 --- a/src/stateless/cp/trex_streams_compiler.cpp +++ b/src/stateless/cp/trex_streams_compiler.cpp @@ -19,9 +19,116 @@ See the License for the specific language governing permissions and limitations under the License. */ -#include <string.h> +#include <string> +#include <sstream> #include <trex_streams_compiler.h> #include <trex_stream.h> +#include <assert.h> +#include <trex_stateless.h> +#include <iostream> + +/** + * describes a graph node in the pre compile check + * + * @author imarom (16-Nov-15) + */ +class GraphNode { +public: + GraphNode(TrexStream *stream, GraphNode *next) : m_stream(stream), m_next(next) { + marked = false; + } + + uint32_t get_stream_id() const { + return m_stream->m_stream_id; + } + + const TrexStream *m_stream; + GraphNode *m_next; + std::vector<const GraphNode *> m_parents; + bool marked; +}; + +/** + * node map + * + */ +class GraphNodeMap { +public: + + GraphNodeMap() : m_dead_end(NULL, NULL) { + + } + + bool add(GraphNode *node) { + if (has(node->get_stream_id())) { + return false; + } + + m_nodes[node->get_stream_id()] = node; + + if (node->m_stream->m_self_start) { + m_roots.push_back(node); + } + + return true; + } + + bool has(uint32_t stream_id) { + + return (get(stream_id) != NULL); + } + + GraphNode * get(uint32_t stream_id) { + + if (stream_id == -1) { + return &m_dead_end; + } + + auto search = m_nodes.find(stream_id); + + if (search != m_nodes.end()) { + return search->second; + } else { + return NULL; + } + } + + void clear_marks() { + for (auto node : m_nodes) { + node.second->marked = false; + } + } + + void get_unmarked(std::vector <GraphNode *> &unmarked) { + for (auto node : m_nodes) { + if (!node.second->marked) { + unmarked.push_back(node.second); + } + } + } + + + ~GraphNodeMap() { + for (auto node : m_nodes) { + delete node.second; + } + m_nodes.clear(); + } + + std::vector <GraphNode *> & get_roots() { + return m_roots; + } + + + std::unordered_map<uint32_t, GraphNode *> get_nodes() { + return m_nodes; + } + +private: + std::unordered_map<uint32_t, GraphNode *> m_nodes; + std::vector <GraphNode *> m_roots; + GraphNode m_dead_end; +}; /************************************** * stream compiled object @@ -63,11 +170,196 @@ TrexStreamsCompiledObj::clone() { return new_compiled_obj; } +void +TrexStreamsCompiler::add_warning(const std::string &warning) { + m_warnings.push_back("*** warning: " + warning); +} + +void +TrexStreamsCompiler::err(const std::string &err) { + throw TrexException("*** error: " + err); +} + +void +TrexStreamsCompiler::check_stream(const TrexStream *stream) { + std::stringstream ss; + + /* cont. stream can point only on itself */ + if (stream->get_type() == TrexStream::stCONTINUOUS) { + if (stream->m_next_stream_id != -1) { + ss << "continous stream '" << stream->m_stream_id << "' cannot point on another stream"; + err(ss.str()); + } + } +} + +void +TrexStreamsCompiler::allocate_pass(const std::vector<TrexStream *> &streams, + GraphNodeMap *nodes) { + std::stringstream ss; + + /* first pass - allocate all nodes and check for duplicates */ + for (auto stream : streams) { + + /* skip non enabled streams */ + if (!stream->m_enabled) { + continue; + } + + /* sanity check on the stream itself */ + check_stream(stream); + + /* duplicate stream id ? */ + if (nodes->has(stream->m_stream_id)) { + ss << "duplicate instance of stream id " << stream->m_stream_id; + err(ss.str()); + } + + GraphNode *node = new GraphNode(stream, NULL); + + /* add to the map */ + assert(nodes->add(node)); + } + +} + +/** + * on this pass we direct the graph to point to the right nodes + * + */ +void +TrexStreamsCompiler::direct_pass(GraphNodeMap *nodes) { + + /* second pass - direct the graph */ + for (auto p : nodes->get_nodes()) { + + GraphNode *node = p.second; + const TrexStream *stream = node->m_stream; + + /* check the stream points on an existing stream */ + GraphNode *next_node = nodes->get(stream->m_next_stream_id); + if (!next_node) { + std::stringstream ss; + ss << "stream " << node->get_stream_id() << " is pointing on non existent stream " << stream->m_next_stream_id; + err(ss.str()); + } + + node->m_next = next_node; + + /* do we have more than one parent ? */ + next_node->m_parents.push_back(node); + } + + + /* check for multiple parents */ + for (auto p : nodes->get_nodes()) { + GraphNode *node = p.second; + + if (node->m_parents.size() > 0 ) { + std::stringstream ss; + + ss << "stream " << node->get_stream_id() << " is triggered by multiple streams: "; + for (auto x : node->m_parents) { + ss << x->get_stream_id() << " "; + } + + add_warning(ss.str()); + } + } +} + +/** + * mark sure all the streams are reachable + * + */ +void +TrexStreamsCompiler::check_for_unreachable_streams(GraphNodeMap *nodes) { + /* start with the roots */ + std::vector <GraphNode *> next_nodes = nodes->get_roots(); + + + nodes->clear_marks(); + + /* run BFS from all the roots */ + while (!next_nodes.empty()) { + + /* pull one */ + GraphNode *node = next_nodes.back(); + next_nodes.pop_back(); + if (node->marked) { + continue; + } + + node->marked = true; + + if (node->m_next != NULL) { + next_nodes.push_back(node->m_next); + } + + } + + std::vector <GraphNode *> unmarked; + nodes->get_unmarked(unmarked); + + if (!unmarked.empty()) { + std::stringstream ss; + for (auto node : unmarked) { + ss << "stream " << node->get_stream_id() << " is unreachable from any other stream\n"; + } + err(ss.str()); + } + + +} + +/** + * check validation of streams for compile + * + * @author imarom (16-Nov-15) + * + * @param streams + * @param fail_msg + * + * @return bool + */ +void +TrexStreamsCompiler::pre_compile_check(const std::vector<TrexStream *> &streams) { + + GraphNodeMap nodes; + + m_warnings.clear(); + + /* allocate nodes */ + allocate_pass(streams, &nodes); + + /* direct the graph */ + direct_pass(&nodes); + + /* check for non reachable streams inside the graph */ + check_for_unreachable_streams(&nodes); + +} + /************************************** * stream compiler *************************************/ bool -TrexStreamsCompiler::compile(const std::vector<TrexStream *> &streams, TrexStreamsCompiledObj &obj) { +TrexStreamsCompiler::compile(const std::vector<TrexStream *> &streams, + TrexStreamsCompiledObj &obj, + std::string *fail_msg) { + + /* compile checks */ + try { + pre_compile_check(streams); + } catch (const TrexException &ex) { + if (fail_msg) { + *fail_msg = ex.what(); + } else { + std::cout << ex.what(); + } + return false; + } + /* for now we do something trivial, */ for (auto stream : streams) { @@ -76,11 +368,6 @@ TrexStreamsCompiler::compile(const std::vector<TrexStream *> &streams, TrexStrea continue; } - /* for now skip also non self started streams */ - if (!stream->m_self_start) { - continue; - } - /* add it */ obj.add_compiled_stream(stream); } diff --git a/src/stateless/cp/trex_streams_compiler.h b/src/stateless/cp/trex_streams_compiler.h index 44c8a0fc..42cfc5b8 100644 --- a/src/stateless/cp/trex_streams_compiler.h +++ b/src/stateless/cp/trex_streams_compiler.h @@ -23,9 +23,11 @@ limitations under the License. #include <stdint.h> #include <vector> +#include <string> class TrexStreamsCompiler; class TrexStream; +class GraphNodeMap; /** * compiled object for a table of streams @@ -68,13 +70,35 @@ private: class TrexStreamsCompiler { public: + /** * compiles a vector of streams to an object passable to the DP * * @author imarom (28-Oct-15) * */ - bool compile(const std::vector<TrexStream *> &streams, TrexStreamsCompiledObj &obj); + bool compile(const std::vector<TrexStream *> &streams, TrexStreamsCompiledObj &obj, std::string *fail_msg = NULL); + + /** + * + * returns a reference pointer to the last compile warnings + * if no warnings were produced - the vector is empty + */ + const std::vector<std::string> & get_last_compile_warnings() { + return m_warnings; + } + +private: + + void pre_compile_check(const std::vector<TrexStream *> &streams); + void allocate_pass(const std::vector<TrexStream *> &streams, GraphNodeMap *nodes); + void direct_pass(GraphNodeMap *nodes); + void check_for_unreachable_streams(GraphNodeMap *nodes); + void check_stream(const TrexStream *stream); + void add_warning(const std::string &warning); + void err(const std::string &err); + + std::vector<std::string> m_warnings; }; #endif /* __TREX_STREAMS_COMPILER_H__ */ |