diff --git a/compiler/generate/t_cpp_generator.cc b/compiler/generate/t_cpp_generator.cc index 9be6bc6d..38a2c2db 100755 --- a/compiler/generate/t_cpp_generator.cc +++ b/compiler/generate/t_cpp_generator.cc @@ -119,6 +119,13 @@ class t_cpp_generator : public t_oop_generator { void generate_logger_map_element (std::ofstream& out, t_map* tmap, string iter, bool log_value_only); void generate_logger_set_element (std::ofstream& out, t_set* tset, string iter, bool log_value_only); void generate_logger_list_element (std::ofstream& out, t_list* tlist, string iter, bool log_value_only); + void generate_sandesh_get_size (std::ofstream& out, t_sandesh* tsandesh); + void generate_get_size_field (std::ofstream& out, t_field *tfield); + void generate_get_size_struct (std::ofstream& out, t_struct *tstruct, string name); + void generate_get_size_container (std::ofstream& out, t_type* ttype, string name); + void generate_get_size_map_element (std::ofstream& out, t_map* tmap, string name); + void generate_get_size_list_element(std::ofstream& out, t_list* ttype, string name); + void generate_get_size_set_element (std::ofstream& out, t_set* ttype, string name); void generate_sandesh_trace (std::ofstream& out, t_sandesh* tsandesh); void generate_sandesh_context (std::ofstream& out, t_sandesh* tsandesh, string val); void generate_sandesh_seqnum(std::ofstream& out, t_sandesh* tsandesh); @@ -160,6 +167,8 @@ class t_cpp_generator : public t_oop_generator { void generate_static_const_string_definition(std::ofstream& out, t_struct* tstruct); void generate_struct_logger (ofstream& out, const string& name, const vector& fields); + void generate_struct_get_size (ofstream& out, const string& name, + const vector& fields); #endif /** @@ -941,6 +950,7 @@ void t_cpp_generator::generate_cpp_struct(t_struct* tstruct, bool is_exception) generate_struct_writer(out, tstruct); #ifdef SANDESH generate_struct_logger(out, tstruct->get_name(), tstruct->get_members()); + generate_struct_get_size(out, tstruct->get_name(), tstruct->get_members()); #endif } @@ -974,6 +984,7 @@ void t_cpp_generator::generate_cpp_sandesh(t_sandesh* tsandesh) { generate_sandesh_reader(out, tsandesh); generate_sandesh_writer(out, tsandesh); generate_sandesh_loggers(out, tsandesh); + generate_sandesh_get_size(out, tsandesh); if (!is_trace) { generate_sandesh_static_seqnum_def(out, tsandesh); @@ -1866,6 +1877,8 @@ void t_cpp_generator::generate_sandesh_definition(ofstream& out, out << indent() << "std::string ToString() const;" << endl; + out << indent() << "size_t GetSize() const;" << endl; + // Private members out << endl; out << "private:" << endl << endl; @@ -2267,6 +2280,7 @@ void t_cpp_generator::generate_struct_definition(ofstream& out, } #ifdef SANDESH out << indent() << "std::string log() const;" << endl; + out << indent() << "size_t GetSize() const;" << endl; #endif out << endl; @@ -2724,6 +2738,28 @@ void t_cpp_generator::generate_struct_writer(ofstream& out, } #ifdef SANDESH +/** + * Generate get size for structs + * + * @param out Stream to write to + * @param tstruct The struct tstruct->get_members() tstruct->get_name() + */ +void t_cpp_generator::generate_struct_get_size(ofstream& out, const string& name, + const vector& fields) { + //Generate GetSize function to return size of sandesh + indent(out) << "size_t " << name << + "::GetSize() const {" << endl; + indent_up(); + indent(out) << "size_t size = 0;" << endl; + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + generate_get_size_field(out, *f_iter); + } + indent(out) << "return size;" << endl; + indent_down(); + indent(out) << "}" << endl << endl; +} + /** * Generate logger for structs * @@ -3282,6 +3318,35 @@ void t_cpp_generator::generate_sandesh_creator(ofstream& out, return; } +/** + * finds size of a field of any type. + */ +void t_cpp_generator::generate_get_size_field(ofstream& out, t_field *tfield) { + t_type* type = get_true_type(tfield->get_type()); + string name = tfield->get_name(); + // Handle optional elements + if (tfield->get_req() == t_field::T_OPTIONAL) { + out << indent() << "if (__isset." << name << ") {" << + endl; + indent_up(); + } + if (type->is_struct()) { + generate_get_size_struct(out, (t_struct *)type, name); + } else if (type->is_container()) { + generate_get_size_container(out, type, name); + } else if (type->is_string() || type->is_xml() || + type->is_static_const_string()) { + out << indent() << "size += " << name << ".length();" << endl; + } else { + out << indent() << "size += sizeof(" + name +");" << endl; + } + // Handle optional elements + if (tfield->get_req() == t_field::T_OPTIONAL) { + indent_down(); + out << indent() << "}" << endl; + } +} + /** * Logs a field of any type. */ @@ -3357,6 +3422,62 @@ void t_cpp_generator::generate_logger_field(ofstream& out, } } +/** + * Generate code to find size of a container + */ +void t_cpp_generator::generate_get_size_container(ofstream& out, + t_type* ttype, + string name) { + scope_up(out); + string iter = tmp("_iter"); + out << indent() << type_name(ttype) << "::const_iterator " << iter + << ";" << endl; + out << indent() << "for (" << iter << " = " << name << ".begin(); " + << iter << " != " << name << ".end(); ++" << iter << ")" << endl; + scope_up(out); + if (ttype->is_map()) { + generate_get_size_map_element(out, (t_map *)ttype, iter); + } else if(ttype->is_set()) { + generate_get_size_set_element(out, (t_set *)ttype, iter); + } else if(ttype->is_list()) { + generate_get_size_list_element(out, (t_list *)ttype, iter); + } + scope_down(out); + scope_down(out); +} + +/** + * Generate code to find size of a map element + */ +void t_cpp_generator::generate_get_size_map_element(ofstream& out, + t_map* tmap, + string iter) { + t_field kfield(tmap->get_key_type(), iter + "->first"); + generate_get_size_field(out, &kfield); + t_field vfield(tmap->get_val_type(), iter + "->second"); + generate_get_size_field(out, &vfield); +} + +/** + * Generate code to find size of list element + */ +void t_cpp_generator::generate_get_size_list_element(ofstream& out, + t_list* tlist, + string iter) { + t_field efield(tlist->get_elem_type(), "(*" + iter + ")"); + generate_get_size_field(out, &efield); +} + +/** + * Generate code to find size of set element + */ +void t_cpp_generator::generate_get_size_set_element(ofstream& out, + t_set* tset, + string iter) { + t_field efield(tset->get_elem_type(), "(*" + iter + ")"); + generate_get_size_field(out, &efield); +} + /** * Generate code to log a container */ @@ -3423,6 +3544,16 @@ void t_cpp_generator::generate_logger_list_element(ofstream& out, generate_logger_field(out, &efield, prefix, log_value_only, true); } +/** + * Generate code to find size of a struct. + */ +void t_cpp_generator::generate_get_size_struct(ofstream& out, + t_struct *tstruct, + string name) { + (void) tstruct; + out << indent() << "size += " << name << ".GetSize();" << endl; +} + /** * Generate code to log a struct. */ @@ -3554,6 +3685,29 @@ void t_cpp_generator::generate_sandesh_logger(ofstream& out, } } +/** + * Generate GetSize for sandesh + * + * @param out The output stream + * @param tsandesh The sandesh + */ +void t_cpp_generator::generate_sandesh_get_size(ofstream& out, + t_sandesh* tsandesh) { + //Generate GetSize function to return size of sandesh + indent(out) << "size_t " << tsandesh->get_name() << + "::GetSize() const {" << endl; + indent_up(); + indent(out) << "size_t size = 0;" << endl; + const vector& fields = tsandesh->get_members(); + vector::const_iterator f_iter; + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + generate_get_size_field(out, *f_iter); + } + indent(out) << "return size;" << endl; + indent_down(); + indent(out) << "}" << endl << endl; +} + /** * Generate loggers for sandesh * diff --git a/library/cpp/sandesh.cc b/library/cpp/sandesh.cc index a7fda403..be504f28 100644 --- a/library/cpp/sandesh.cc +++ b/library/cpp/sandesh.cc @@ -469,7 +469,9 @@ bool Sandesh::Enqueue(SandeshQueue *queue) { Release(); return false; } - if (!queue->Enqueue(this)) { + //Frame an elemet object and enqueue it + SandeshElement elem(this); + if (!queue->Enqueue(elem)) { // XXX Change when WorkQueue implements bounded queues return true; } @@ -840,3 +842,18 @@ bool SandeshSystem::HandleTest(SandeshLevel::type level, } return false; } + +template<> +size_t Sandesh::SandeshQueue::AtomicIncrementQueueCount( + SandeshElement *element) + { + size_t sandesh_size = element->GetSize(); + return count_.fetch_and_add(sandesh_size) + sandesh_size; +} + +template<> +size_t Sandesh::SandeshQueue::AtomicDecrementQueueCount( + SandeshElement *element) { + size_t sandesh_size = element->GetSize(); + return count_.fetch_and_add((size_t)(0-sandesh_size)) - sandesh_size; +} diff --git a/library/cpp/sandesh.h b/library/cpp/sandesh.h index a0c7d679..1b7a7886 100644 --- a/library/cpp/sandesh.h +++ b/library/cpp/sandesh.h @@ -120,10 +120,12 @@ class SandeshMessageStats; class SandeshConnection; class SandeshRequest; +struct SandeshElement; + class Sandesh { public: typedef WorkQueue SandeshRxQueue; - typedef WorkQueue SandeshQueue; + typedef WorkQueue SandeshQueue; typedef WorkQueue< boost::shared_ptr > SandeshBufferQueue; @@ -201,6 +203,9 @@ class Sandesh { static std::string LoggingCategory() { return logging_category_; } static void SendLoggingResponse(std::string context); + //GetSize method to report the size + virtual size_t GetSize() const = 0; + // Send queue processing static void SetSendQueue(bool enable); static inline bool IsSendQueueEnabled() { @@ -293,6 +298,7 @@ class Sandesh { } static uint32_t get_send_rate_limit() { return sandesh_send_ratelimit_; } + protected: void set_timestamp(time_t timestamp) { timestamp_ = timestamp; } void set_type(SandeshType::type type) { type_ = type; } @@ -390,6 +396,27 @@ class Sandesh { static tbb::atomic sandesh_send_ratelimit_; }; +struct SandeshElement { + Sandesh *snh_; + //Explicit constructor creating only if Sandesh is passed as arg + explicit SandeshElement(Sandesh *snh):snh_(snh),size_(snh->GetSize()) { + } + SandeshElement():size_(0) { } + size_t GetSize() const { + return size_; + } + private: + size_t size_; +}; + +template<> +size_t Sandesh::SandeshQueue::AtomicIncrementQueueCount( + SandeshElement *element); + +template<> +size_t Sandesh::SandeshQueue::AtomicDecrementQueueCount( + SandeshElement *element); + #define SANDESH_LOG(_Level, _Msg) \ do { \ if (LoggingDisabled()) break; \ diff --git a/library/cpp/sandesh_connection.cc b/library/cpp/sandesh_connection.cc index d39d5b4d..2fd0b7cb 100644 --- a/library/cpp/sandesh_connection.cc +++ b/library/cpp/sandesh_connection.cc @@ -82,7 +82,8 @@ bool SandeshConnection::SendSandesh(Sandesh *snh) { return false; } // XXX No bounded work queue - session_->send_queue()->Enqueue(snh); + SandeshElement element(snh); + session_->send_queue()->Enqueue(element); return true; } diff --git a/library/cpp/sandesh_session.cc b/library/cpp/sandesh_session.cc index 913d0d24..3fb689d8 100644 --- a/library/cpp/sandesh_session.cc +++ b/library/cpp/sandesh_session.cc @@ -285,7 +285,8 @@ SandeshSession::SandeshSession(TcpServer *client, Socket *socket, reader_(new SandeshReader(this)), send_queue_(new Sandesh::SandeshQueue(writer_task_id, task_instance, - boost::bind(&SandeshSession::SendMsg, this, _1))), + boost::bind(&SandeshSession::SendMsg, this, _1), + kQueueSize)), keepalive_idle_time_(kSessionKeepaliveIdleTime), keepalive_interval_(kSessionKeepaliveInterval), keepalive_probes_(kSessionKeepaliveProbes), @@ -350,7 +351,8 @@ void SandeshSession::OnRead(Buffer buffer) { reader_->OnRead(buffer); } -bool SandeshSession::SendMsg(Sandesh *sandesh) { +bool SandeshSession::SendMsg(SandeshElement element) { + Sandesh *sandesh = element.snh_; tbb::mutex::scoped_lock lock(send_mutex_); if (!IsEstablished()) { if (sandesh->IsLoggingDroppedAllowed()) { diff --git a/library/cpp/sandesh_session.h b/library/cpp/sandesh_session.h index 3faaceb9..da8e8b3f 100644 --- a/library/cpp/sandesh_session.h +++ b/library/cpp/sandesh_session.h @@ -223,8 +223,9 @@ class SandeshSession : public TcpSession { static const int kSessionKeepaliveInterval = 3; // in seconds static const int kSessionKeepaliveProbes = 5; // count static const int kSessionTcpUserTimeout = 30000; // ms + static const int kQueueSize = 200 * 1024 * 1024; // 200 MB - bool SendMsg(Sandesh *sandesh); + bool SendMsg(SandeshElement element); bool SendBuffer(boost::shared_ptr sbuffer); bool SessionSendReady(); diff --git a/library/cpp/test/SConscript b/library/cpp/test/SConscript index 20432f29..044d3e7e 100644 --- a/library/cpp/test/SConscript +++ b/library/cpp/test/SConscript @@ -26,6 +26,7 @@ SandeshMessageTestGenFiles = env.SandeshGenCpp('sandesh_message_test.sandesh') SandeshTraceTestGenFiles = env.SandeshGenCpp('sandesh_trace_test.sandesh') SandeshHttpTestGenFiles = env.SandeshGenCpp('sandesh_http_test.sandesh') SandeshPerfTestGenFiles = env.SandeshGenCpp('sandesh_perf_test.sandesh') +SandeshSendQueueTestGenFiles = env.SandeshGenCpp('sandesh_send_queue_test.sandesh') SandeshRWTestGenSrcs = env.ExtractCpp(SandeshRWTestGenFiles) SandeshMessageTestGenSrcs = env.ExtractCpp(SandeshMessageTestGenFiles) @@ -33,6 +34,7 @@ SandeshTraceTestGenSrcs = env.ExtractCpp(SandeshTraceTestGenFiles) SandeshTraceTestGenObjs = env.Object(SandeshTraceTestGenSrcs) SandeshHttpTestGenSrcs = env.ExtractCpp(SandeshHttpTestGenFiles) SandeshPerfTestGenSrcs = env.ExtractCpp(SandeshPerfTestGenFiles) +SandeshSendQueueTestGenSrcs = env.ExtractCpp(SandeshSendQueueTestGenFiles) SandeshLibPath = ['#/build/lib', Dir(env['TOP']).abspath + '/base', @@ -79,6 +81,12 @@ if sys.platform != 'darwin': if sys.platform.startswith('freebsd'): env.Append(LIBS = ['z', 'lzma', 'iconv']) +sandesh_send_queue_test = env.UnitTest('sandesh_send_queue_test', + SandeshSendQueueTestGenSrcs + + ['sandesh_send_queue_test.cc'], + ) +env.Alias('src/sandesh:sandesh_send_queue_test', sandesh_send_queue_test) + sandesh_perf_test = env.UnitTest('sandesh_perf_test', SandeshPerfTestGenSrcs + ['sandesh_perf_test.cc'], @@ -155,6 +163,7 @@ test_suite = [sandesh_message_test, sandesh_client_test, sandesh_statistics_test, sandesh_request_test, + sandesh_send_queue_test, ] test = env.TestSuite('sandesh-test', test_suite) diff --git a/library/cpp/test/sandesh_perf_test.cc b/library/cpp/test/sandesh_perf_test.cc index 2d8731d2..69e82472 100644 --- a/library/cpp/test/sandesh_perf_test.cc +++ b/library/cpp/test/sandesh_perf_test.cc @@ -82,8 +82,8 @@ class SandeshPerfTestEnqueue : public ::testing::Test { virtual void TearDown() { } - bool DequeueSandesh(Sandesh *entry) { - entry->Release(); + bool DequeueSandesh(SandeshElement element) { + element.snh_->Release(); return true; } @@ -137,7 +137,8 @@ struct WorkQueueDelete { TEST_F(SandeshPerfTestEnqueue, DISABLED_SandeshEnqueue) { for (int i = 0; i < 1000000; i++) { PerfTestSandesh *sandesh = new PerfTestSandesh(); - sandesh_queue_->Enqueue(sandesh); + SandeshElement element(sandesh); + sandesh_queue_->Enqueue(element); } sandesh_queue_->Shutdown(); } diff --git a/library/cpp/test/sandesh_send_queue_test.cc b/library/cpp/test/sandesh_send_queue_test.cc new file mode 100644 index 00000000..36b1b22d --- /dev/null +++ b/library/cpp/test/sandesh_send_queue_test.cc @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. + */ + +// +// sandesh_send_queue_test.cc +// +// Sandesh send queue Test +// + +#include +#include +#include +#include +#include +#include + +#include "testing/gunit.h" + +#include "base/logging.h" +#include "base/util.h" + +#include +#include +#include + +#include +#include +#include "sandesh_send_queue_test_types.h" + +using namespace contrail::sandesh::protocol; +using namespace contrail::sandesh::transport; + +static const int32_t test_i32 = 0xdeadbeef; +static const uint32_t test_list_size = 5; + +static const int kQueueSize = 200 * 1024 * 1024; + +class SandeshSendQueueTest : public ::testing::Test { +protected: + virtual void SetUp() { + sandesh_queue_.reset(new Sandesh::SandeshQueue(TaskScheduler:: + GetInstance()->GetTaskId("sandesh::Test::SendQueueTest" + "::sandesh_queue"), + Task::kTaskInstanceAny, + boost::bind(&SandeshSendQueueTest::DequeueEvent, this, _1), + kQueueSize)); + } + + //Create a structure of size 278 bytes + void CreateMessages() { + uint8_t *buffer; + uint32_t offset, wxfer, rxfer; + // Initialize the struct + msg1.set_i32Test(test_i32); //4 bytes + // Create a vector of SandeshStructTest + std::vector test_list; + for (uint32_t i = 0; i < test_list_size; i++) { + SandeshListTestElement tmp; + tmp.set_i32Elem(i); + test_list.push_back(tmp); // 0, 1, 2, 3, 4 + } + msg1.set_listTest(test_list);//20 bytes + // Create a vector of int32_t + std::vector test_basic_type_list; + for (uint32_t i = 0; i < test_list_size; i++) { + test_basic_type_list.push_back(i); // 0, 1, 2, 3, 4 + } + msg1.set_basicTypeListTest(test_basic_type_list); //20 bytes + // Create a vector of uuid + std::vector test_uuid_list; + for (uint32_t i = 0; i < test_list_size; i++) { + boost::uuids::uuid uuid_temp = + {0x00+i,0x00+i,0x01+i,0x01+i,0x02+i,0x02+i,0x03+i,0x03+i, + 0x04+i,0x04+i,0x05+i,0x05+i,0x06+i,0x06+i,0x07+i,0x07+i}; + test_uuid_list.push_back(uuid_temp); + } + msg1.set_uuidListTest(test_uuid_list); //80 bytes + // Create a map of + std::map test_basic_type_map; + for (uint32_t i = 0; i < test_list_size; i++) { + // (0, "0"), (1, "1"), (2, "2"), (3, "3"), (4, "4") + test_basic_type_map.insert(std::pair + (i, integerToString(i))); + } + msg1.set_basicTypeMapTest(test_basic_type_map);//25 bytes + // Create a map of + std::map test_complex_type_map; + for (uint32_t i = 0; i < test_list_size; i++) { + SandeshListTestElement tmp; + tmp.set_i32Elem(i); + test_complex_type_map.insert(std::pair + (i, tmp)); + } + msg1.set_complexTypeMapTest(test_complex_type_map);//40 bytes + msg1.set_u16Test(65535);//2 bytes + msg1.set_u32Test(4294967295u);//4 bytes + msg1.set_u64Test(18446744073709551615ull);//8 bytes + msg1.set_ipv4Test(4294967295u);//4 bytes + boost::uuids::uuid uuid_test = + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, + 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};//16 bytes + msg1.set_uuidTest(uuid_test); + msg1.set_xmlTest("");//5 bytes + msg1.set_xmlTest1("abc");//3 bytes + msg1.set_xmlTest2("ab]");//3 bytes + msg1.set_xmlTest3("abc]]");//5 bytes + } + + virtual void TearDown() { + } + + bool DequeueEvent(SandeshElement element) { + element.snh_->Release(); + return true; + } + + boost::scoped_ptr sandesh_queue_; + SandeshStructTest msg1; + SandeshStructTest msg2; +}; + +/* + * Check if GetSize function works for the given sandesh + */ +TEST_F(SandeshSendQueueTest, SizeTest) { + CreateMessages(); + SandeshResponseTest *sandesh = new SandeshResponseTest(); + sandesh->set_data(msg1); + EXPECT_EQ(sandesh->GetSize(), 278); + sandesh->Release(); +} + +/* + * Check if sandesh queue size increases upon sending the message + */ +TEST_F(SandeshSendQueueTest, EnqueuTest) { + CreateMessages(); + SandeshResponseTest *sandesh = new SandeshResponseTest(); + sandesh->set_data(msg1); + //Intial size of queue is 0 + EXPECT_EQ(sandesh_queue_->Length(), 0); + SandeshElement element_snd(sandesh), element_rcv; + sandesh_queue_->Enqueue(element_snd); + //Size of queue after enqueue + EXPECT_EQ(sandesh_queue_->Length(), element_snd.GetSize()); + sandesh_queue_->Dequeue(&element_rcv); + //Size of queue after dequeue + EXPECT_EQ(sandesh_queue_->Length(), 0); + sandesh->Release(); +} + +int main(int argc, char **argv) { + LoggingInit(); + ::testing::InitGoogleTest(&argc, argv); + bool success = RUN_ALL_TESTS(); + return success; +} diff --git a/library/cpp/test/sandesh_send_queue_test.sandesh b/library/cpp/test/sandesh_send_queue_test.sandesh new file mode 100644 index 00000000..dbd6e7bc --- /dev/null +++ b/library/cpp/test/sandesh_send_queue_test.sandesh @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015 Juniper Networks, Inc. All rights reserved. + */ + +/* + * sandesh_send_queue_test.sandesh + * + * Sandesh definitions for sandesh_send_queue test + */ + +struct SandeshListTestElement { + 1: i32 i32Elem; +} + +struct SandeshStructTest { + 1: "Const static string is"; + 2: i32 i32Test; + 3: list listTest; + 4: list basicTypeListTest; + 5: map basicTypeMapTest; + 6: map complexTypeMapTest; + 7: u16 u16Test; + 8: u32 u32Test; + 9: u64 u64Test; + 10: xml xmlTest; + 11: xml xmlTest1; + 12: xml xmlTest2; + 13: xml xmlTest3; + 14: ipv4 ipv4Test; + 15: uuid_t uuidTest; + 16: list uuidListTest; + 17: uuid_t uuidDefaultTest = "00010203-0405-0607-0423-023434265323"; +} + +response sandesh SandeshResponseTest { + 1: SandeshStructTest data +} +