diff options
Diffstat (limited to 'test/network_tests/cpu_load_tests')
11 files changed, 1116 insertions, 0 deletions
diff --git a/test/network_tests/cpu_load_tests/conf/cpu_load_test_client_master.json.in b/test/network_tests/cpu_load_tests/conf/cpu_load_test_client_master.json.in new file mode 100644 index 0000000..ec9dcd7 --- /dev/null +++ b/test/network_tests/cpu_load_tests/conf/cpu_load_test_client_master.json.in @@ -0,0 +1,38 @@ +{ + "unicast" : "@TEST_IP_MASTER@", + "netmask" : "255.255.255.0", + "logging" : + { + "level" : "debug", + "console" : "true", + "file" : + { + "enable" : "false", + "path" : "/var/log/vsomeip.log" + }, + + "dlt" : "false" + }, + + "applications" : + [ + { + "name" : "cpu_load_test_client", + "id" : "0x2222" + } + ], + "npdu-default-timings" : { + "debounce-time-request" : "0", + "debounce-time-response" : "0", + "max-retention-time-request" : "0", + "max-retention-time-response" : "0" + }, + "routing" : "cpu_load_test_client", + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp" + } +} diff --git a/test/network_tests/cpu_load_tests/conf/cpu_load_test_client_slave.json.in b/test/network_tests/cpu_load_tests/conf/cpu_load_test_client_slave.json.in new file mode 100644 index 0000000..4da82f4 --- /dev/null +++ b/test/network_tests/cpu_load_tests/conf/cpu_load_test_client_slave.json.in @@ -0,0 +1,38 @@ +{ + "unicast" : "@TEST_IP_SLAVE@", + "netmask" : "255.255.255.0", + "logging" : + { + "level" : "debug", + "console" : "true", + "file" : + { + "enable" : "false", + "path" : "/var/log/vsomeip.log" + }, + + "dlt" : "false" + }, + + "applications" : + [ + { + "name" : "cpu_load_test_client", + "id" : "0x2222" + } + ], + "npdu-default-timings" : { + "debounce-time-request" : "0", + "debounce-time-response" : "0", + "max-retention-time-request" : "0", + "max-retention-time-response" : "0" + }, + "routing" : "cpu_load_test_client", + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp" + } +} diff --git a/test/network_tests/cpu_load_tests/conf/cpu_load_test_service_master.json.in b/test/network_tests/cpu_load_tests/conf/cpu_load_test_service_master.json.in new file mode 100644 index 0000000..da34e22 --- /dev/null +++ b/test/network_tests/cpu_load_tests/conf/cpu_load_test_service_master.json.in @@ -0,0 +1,51 @@ +{ + "unicast" : "@TEST_IP_MASTER@", + "logging" : + { + "level" : "debug", + "console" : "true", + "file" : + { + "enable" : "false", + "path" : "/tmp/vsomeip.log" + }, + + "dlt" : "false" + }, + + "applications" : + [ + { + "name" : "cpu_load_test_service", + "id" : "0x1111" + } + ], + + "services" : + [ + { + "service" : "0x1111", + "instance" : "0x1", + "unreliable" : "30510", + "reliable" : + { + "port" : "30510", + "enable-magic-cookies" : "false" + } + } + ], + "npdu-default-timings" : { + "debounce-time-request" : "0", + "debounce-time-response" : "0", + "max-retention-time-request" : "0", + "max-retention-time-response" : "0" + }, + "routing" : "cpu_load_test_service", + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp" + } +} diff --git a/test/network_tests/cpu_load_tests/conf/cpu_load_test_service_slave.json.in b/test/network_tests/cpu_load_tests/conf/cpu_load_test_service_slave.json.in new file mode 100644 index 0000000..389aea0 --- /dev/null +++ b/test/network_tests/cpu_load_tests/conf/cpu_load_test_service_slave.json.in @@ -0,0 +1,51 @@ +{ + "unicast" : "@TEST_IP_SLAVE@", + "logging" : + { + "level" : "debug", + "console" : "true", + "file" : + { + "enable" : "false", + "path" : "/tmp/vsomeip.log" + }, + + "dlt" : "false" + }, + + "applications" : + [ + { + "name" : "cpu_load_test_service", + "id" : "0x1111" + } + ], + + "services" : + [ + { + "service" : "0x1111", + "instance" : "0x1", + "unreliable" : "30510", + "reliable" : + { + "port" : "30510", + "enable-magic-cookies" : "false" + } + } + ], + "npdu-default-timings" : { + "debounce-time-request" : "0", + "debounce-time-response" : "0", + "max-retention-time-request" : "0", + "max-retention-time-response" : "0" + }, + "routing" : "cpu_load_test_service", + "service-discovery" : + { + "enable" : "true", + "multicast" : "224.0.0.1", + "port" : "30490", + "protocol" : "udp" + } +} diff --git a/test/network_tests/cpu_load_tests/cpu_load_measurer.cpp b/test/network_tests/cpu_load_tests/cpu_load_measurer.cpp new file mode 100644 index 0000000..d4360b3 --- /dev/null +++ b/test/network_tests/cpu_load_tests/cpu_load_measurer.cpp @@ -0,0 +1,159 @@ +// Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "cpu_load_measurer.hpp" + +#include <fstream> +#include <string> +#include <iostream> +#include <sstream> +#include <vector> +#include <stdexcept> +#include <cstdio> + +#include <sys/types.h> +#include <unistd.h> + +cpu_load_measurer::~cpu_load_measurer() { +} + +cpu_load_measurer::cpu_load_measurer(std::uint32_t _pid) : + pid_(_pid), + jiffies_complete_start_(0), + jiffies_idle_start_(0), + jiffies_complete_stop_(0), + jiffies_idle_stop_(0), + clock_ticks_(0), + jiffies_passed_pid_start_(0), + jiffies_passed_pid_stop_(0), + cpu_load_pid_(0.0), + cpu_load_overall_(0.0), + cpu_load_pid_wo_idle_(0.0) { +} + +void cpu_load_measurer::start() { + // reset everything + jiffies_complete_start_ = 0; + jiffies_idle_start_ = 0; + jiffies_complete_stop_ = 0; + jiffies_idle_stop_ = 0; + clock_ticks_ = 0; + jiffies_passed_pid_start_ = 0; + jiffies_passed_pid_stop_ = 0; + cpu_load_pid_= 0.0; + cpu_load_overall_ = 0.0; + cpu_load_pid_wo_idle_ = 0.0; + //start + jiffies_complete_start_ = read_proc_stat(&jiffies_idle_start_); + jiffies_passed_pid_start_ = read_proc_pid_stat(); +} + +void cpu_load_measurer::stop() { + jiffies_complete_stop_ = read_proc_stat(&jiffies_idle_stop_); + jiffies_passed_pid_stop_ = read_proc_pid_stat(); + if(jiffies_complete_stop_ < jiffies_complete_start_ || jiffies_passed_pid_stop_ < jiffies_passed_pid_start_) { + std::cerr << "Overflow of values in procfs occured, can't calculate load" << std::endl; + exit(0); + } + cpu_load_pid_ = 100.0 + * static_cast<double>(jiffies_passed_pid_stop_ + - jiffies_passed_pid_start_) + / static_cast<double>(jiffies_complete_stop_ + - jiffies_complete_start_); + cpu_load_overall_ = 100.0 + * static_cast<double>((jiffies_complete_stop_ - jiffies_idle_stop_) + - (jiffies_complete_start_ - jiffies_idle_start_)) + / static_cast<double>(jiffies_complete_stop_ + - jiffies_complete_start_); + cpu_load_pid_wo_idle_ = 100.0 + * static_cast<double>(jiffies_passed_pid_stop_ + - jiffies_passed_pid_start_) + / static_cast<double>((jiffies_complete_stop_ - jiffies_idle_stop_) + - (jiffies_complete_start_ - jiffies_idle_start_)); + +} + +void cpu_load_measurer::print_cpu_load() const { + std::cout << "Used Jiffies complete: " + << jiffies_complete_stop_ - jiffies_complete_start_ << " (worked: " + << (jiffies_complete_stop_ - jiffies_idle_stop_) + - (jiffies_complete_start_ - jiffies_idle_start_) + << " idled: " << jiffies_idle_stop_ - jiffies_idle_start_ + << ")" << std::endl; + std::cout << "Used Jiffies of pid " << pid_ << ": " << jiffies_passed_pid_stop_ - jiffies_passed_pid_start_ << std::endl; + std::cout << "Cpu load pid " << pid_ << " [%]: " << cpu_load_pid_ << std::endl; + std::cout << "Overall cpu load[%]: " << cpu_load_overall_ << std::endl; + std::cout << "Load caused by pid " << pid_ << " of overall cpu load [%]:" << cpu_load_pid_wo_idle_ << std::endl; +} + +double cpu_load_measurer::get_cpu_load() const { + return cpu_load_pid_; +} + +std::uint64_t cpu_load_measurer::read_proc_pid_stat() { + std::string path("/proc/" + std::to_string(pid_) + "/stat"); + FILE* f = std::fopen(path.c_str(), "r"); + if(!f) { + std::perror(std::string("Failed to open " + path).c_str()); + exit(1); + } + // see Table 1-4 Contents of the stat files (as of 2.6.30-rc7) + // at https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/Documentation/filesystems/proc.txt?id=refs/tags/v3.10.98 + // and man proc (for conversion specifier) + std::uint64_t utime(0); + std::uint64_t stime(0); + std::int64_t cutime(0); + std::int64_t cstime(0); + if (std::fscanf(f, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u " + "%lu %lu %ld %ld", // utime, stime, cutime, cstime + &utime, &stime, &cutime, &cstime) == EOF) { + std::cerr << "Failed to read " + path << std::endl; + exit(1); + } + std::fclose(f); + return utime + stime + static_cast<std::uint64_t>(cutime) + + static_cast<std::uint64_t>(cstime); +} + +std::uint64_t cpu_load_measurer::read_proc_stat(std::uint64_t* _idle) { + FILE* f = std::fopen("/proc/stat", "r"); + if(!f) { + std::perror("Failed to open /proc/stat"); + exit(1); + } + + // see 1.8 Miscellaneous kernel statistics in /proc/stat + // at https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/tree/Documentation/filesystems/proc.txt?id=refs/tags/v3.10.98 + std::uint64_t user(0); + std::uint64_t nice(0); + std::uint64_t system(0); + std::uint64_t idle(0); + std::uint64_t iowait(0); + std::uint64_t irq(0); + std::uint64_t softirq(0); + std::uint64_t steal(0); + std::uint64_t guest(0); + std::uint64_t guest_nice(0); + if (std::fscanf(f, "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", &user, + &nice, &system, &idle, &iowait, &irq, &softirq, &steal, &guest, + &guest_nice) == EOF) { + std::cerr << "Failed to read /proc/stat" << std::endl; + exit(1); + } + std::fclose(f); + *_idle = idle; + return user + nice + system + idle + iowait + irq + softirq + steal + guest + + guest_nice; +} + +bool cpu_load_measurer::read_clock_ticks() { + long val(::sysconf(_SC_CLK_TCK)); + if(val < 0 && errno == EINVAL) { + std::perror(__func__); + return false; + } + clock_ticks_ = static_cast<std::uint64_t>(val); + return true; +} diff --git a/test/network_tests/cpu_load_tests/cpu_load_measurer.hpp b/test/network_tests/cpu_load_tests/cpu_load_measurer.hpp new file mode 100644 index 0000000..dfdcf80 --- /dev/null +++ b/test/network_tests/cpu_load_tests/cpu_load_measurer.hpp @@ -0,0 +1,35 @@ +// Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include <cstdint> + +class cpu_load_measurer { +public: + cpu_load_measurer(std::uint32_t _pid); + virtual ~cpu_load_measurer(); + void start(); + void stop(); + void print_cpu_load() const; + double get_cpu_load() const; + +private: + std::uint64_t read_proc_stat(std::uint64_t* _idle); + std::uint64_t read_proc_pid_stat(); + bool read_clock_ticks(); +private: + std::uint32_t pid_; + std::uint64_t jiffies_complete_start_; + std::uint64_t jiffies_idle_start_; + std::uint64_t jiffies_complete_stop_; + std::uint64_t jiffies_idle_stop_; + std::uint64_t clock_ticks_; + std::uint64_t jiffies_passed_pid_start_; + std::uint64_t jiffies_passed_pid_stop_; + double cpu_load_pid_; + double cpu_load_overall_; + double cpu_load_pid_wo_idle_; +}; diff --git a/test/network_tests/cpu_load_tests/cpu_load_test_client.cpp b/test/network_tests/cpu_load_tests/cpu_load_test_client.cpp new file mode 100644 index 0000000..d1d5104 --- /dev/null +++ b/test/network_tests/cpu_load_tests/cpu_load_test_client.cpp @@ -0,0 +1,390 @@ +// Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> + +#include <thread> +#include <mutex> +#include <condition_variable> +#include <functional> +#include <iomanip> +#include <numeric> +#include <cmath> // for isfinite +#include <atomic> + +#include "cpu_load_test_globals.hpp" +#include <vsomeip/internal/logger.hpp> +#include "cpu_load_measurer.hpp" + +// for getpid +#include <sys/types.h> +#include <unistd.h> + + +enum protocol_e { + PR_UNKNOWN, + PR_TCP, + PR_UDP +}; + +class cpu_load_test_client +{ +public: + cpu_load_test_client(protocol_e _protocol, std::uint32_t _number_of_calls, + std::uint32_t _payload_size, bool _call_service_sync, + bool _shutdown_service) : + protocol_(_protocol), + app_(vsomeip::runtime::get()->create_application("cpu_load_test_client")), + request_(vsomeip::runtime::get()->create_request(protocol_ == protocol_e::PR_TCP)), + call_service_sync_(_call_service_sync), + shutdown_service_at_end_(_shutdown_service), + sliding_window_size_(_number_of_calls), + wait_for_availability_(true), + is_available_(false), + number_of_calls_(_number_of_calls), + number_of_calls_current_(0), + number_of_sent_messages_(0), + number_of_sent_messages_total_(0), + number_of_acknowledged_messages_(0), + payload_size_(_payload_size), + wait_for_all_msg_acknowledged_(true), + initialized_(false), + sender_(std::bind(&cpu_load_test_client::run, this)) { + if (!app_->init()) { + ADD_FAILURE() << "Couldn't initialize application"; + return; + } + initialized_ = true; + app_->register_state_handler( + std::bind(&cpu_load_test_client::on_state, this, + std::placeholders::_1)); + + app_->register_message_handler(vsomeip::ANY_SERVICE, + vsomeip::ANY_INSTANCE, vsomeip::ANY_METHOD, + std::bind(&cpu_load_test_client::on_message, this, + std::placeholders::_1)); + + app_->register_availability_handler(cpu_load_test::service_id, + cpu_load_test::instance_id, + std::bind(&cpu_load_test_client::on_availability, this, + std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3)); + VSOMEIP_INFO << "Starting..."; + app_->start(); + } + + ~cpu_load_test_client() { + { + std::lock_guard<std::mutex> its_lock(mutex_); + wait_for_availability_ = false; + condition_.notify_one(); + } + { + std::lock_guard<std::mutex> its_lock(all_msg_acknowledged_mutex_); + wait_for_all_msg_acknowledged_ = false; + all_msg_acknowledged_cv_.notify_one(); + } + sender_.join(); + } + +private: + void stop() { + VSOMEIP_INFO << "Stopping..."; + // shutdown the service + if(shutdown_service_at_end_) + { + shutdown_service(); + } + app_->clear_all_handler(); + } + + void on_state(vsomeip::state_type_e _state) { + if(_state == vsomeip::state_type_e::ST_REGISTERED) + { + app_->request_service(cpu_load_test::service_id, + cpu_load_test::instance_id); + } + } + + void on_availability(vsomeip::service_t _service, + vsomeip::instance_t _instance, bool _is_available) { + VSOMEIP_INFO << "Service [" << std::setw(4) << std::setfill('0') + << std::hex << _service << "." << _instance << "] is " + << (_is_available ? "available." : "NOT available."); + + if (cpu_load_test::service_id == _service + && cpu_load_test::instance_id == _instance) { + if (is_available_ && !_is_available) { + is_available_ = false; + } else if (_is_available && !is_available_) { + is_available_ = true; + std::lock_guard<std::mutex> its_lock(mutex_); + wait_for_availability_ = false; + condition_.notify_one(); + } + } + } + void on_message(const std::shared_ptr<vsomeip::message> &_response) { + + number_of_acknowledged_messages_++; + ASSERT_EQ(_response->get_service(), cpu_load_test::service_id); + ASSERT_EQ(_response->get_method(), cpu_load_test::method_id); + if(call_service_sync_) + { + // We notify the sender thread every time a message was acknowledged + std::lock_guard<std::mutex> lk(all_msg_acknowledged_mutex_); + wait_for_all_msg_acknowledged_ = false; + all_msg_acknowledged_cv_.notify_one(); + } + else + { + // We notify the sender thread only if all sent messages have been acknowledged + if(number_of_acknowledged_messages_ == number_of_calls_current_) + { + std::lock_guard<std::mutex> lk(all_msg_acknowledged_mutex_); + number_of_acknowledged_messages_ = 0; + wait_for_all_msg_acknowledged_ = false; + all_msg_acknowledged_cv_.notify_one(); + } + else if(number_of_acknowledged_messages_ % sliding_window_size_ == 0) + { + std::lock_guard<std::mutex> lk(all_msg_acknowledged_mutex_); + wait_for_all_msg_acknowledged_ = false; + all_msg_acknowledged_cv_.notify_one(); + } + } + } + + void run() { + std::unique_lock<std::mutex> its_lock(mutex_); + while (wait_for_availability_) { + condition_.wait(its_lock); + } + + request_->set_service(cpu_load_test::service_id); + request_->set_instance(cpu_load_test::instance_id); + request_->set_method(cpu_load_test::method_id); + std::shared_ptr<vsomeip::payload> payload = vsomeip::runtime::get()->create_payload(); + std::vector<vsomeip::byte_t> payload_data; + payload_data.assign(payload_size_, cpu_load_test::load_test_data); + payload->set_data(payload_data); + request_->set_payload(payload); + + // lock the mutex + for(std::uint32_t i=0; i <= number_of_calls_; i++) { + number_of_calls_current_ = i; + sliding_window_size_ = i; + std::unique_lock<std::mutex> lk(all_msg_acknowledged_mutex_); + call_service_sync_ ? send_messages_sync(lk, i) : send_messages_async(lk, i); + } + const double average_load(std::accumulate(results_.begin(), results_.end(), 0.0) / static_cast<double>(results_.size())); + VSOMEIP_INFO << "Sent: " << number_of_sent_messages_total_ + << " messages in total (excluding control messages). This caused: " + << std::fixed << std::setprecision(2) + << average_load << "% load in average (average of " + << results_.size() << " measurements)."; + + std::vector<double> results_no_zero; + for(const auto &v : results_) { + if(v > 0.0) { + results_no_zero.push_back(v); + } + } + const double average_load_no_zero(std::accumulate(results_no_zero.begin(), results_no_zero.end(), 0.0) / static_cast<double>(results_no_zero.size())); + VSOMEIP_INFO << "Sent: " << number_of_sent_messages_total_ + << " messages in total (excluding control messages). This caused: " + << std::fixed << std::setprecision(2) + << average_load_no_zero << "% load in average, if measured " + << "cpu load was greater zero (average of " + << results_no_zero.size() << " measurements)."; + + wait_for_availability_ = true; + + stop(); + if (initialized_) { + app_->stop(); + } + } + + + void send_messages_sync(std::unique_lock<std::mutex>& lk, std::uint32_t _messages_to_send) { + cpu_load_measurer c(static_cast<std::uint32_t>(::getpid())); + send_service_start_measuring(true); + c.start(); + for (number_of_sent_messages_ = 0; + number_of_sent_messages_ < _messages_to_send; + number_of_sent_messages_++, number_of_sent_messages_total_++) + { + app_->send(request_); + // wait until the send messages has been acknowledged + while(wait_for_all_msg_acknowledged_) { + all_msg_acknowledged_cv_.wait(lk); + } + wait_for_all_msg_acknowledged_ = true; + } + c.stop(); + send_service_start_measuring(false); + VSOMEIP_DEBUG << "Synchronously sent " << std::setw(4) << std::setfill('0') + << number_of_sent_messages_ << " messages. CPU load [%]: " + << std::fixed << std::setprecision(2) + << (std::isfinite(c.get_cpu_load()) ? c.get_cpu_load() : 0.0); + results_.push_back(std::isfinite(c.get_cpu_load()) ? c.get_cpu_load() : 0.0); + + } + + void send_messages_async(std::unique_lock<std::mutex>& lk, std::uint32_t _messages_to_send) { + cpu_load_measurer c(static_cast<std::uint32_t>(::getpid())); + send_service_start_measuring(true); + c.start(); + for (number_of_sent_messages_ = 0; + number_of_sent_messages_ < _messages_to_send; + number_of_sent_messages_++, number_of_sent_messages_total_++) + { + app_->send(request_); + if((number_of_sent_messages_+1) % sliding_window_size_ == 0) + { + // wait until all send messages have been acknowledged + while(wait_for_all_msg_acknowledged_) { + all_msg_acknowledged_cv_.wait(lk); + } + wait_for_all_msg_acknowledged_ = true; + } + } + c.stop(); + send_service_start_measuring(false); + VSOMEIP_DEBUG << "Asynchronously sent " << std::setw(4) << std::setfill('0') + << number_of_sent_messages_ << " messages. CPU load [%]: " + << std::fixed << std::setprecision(2) + << (std::isfinite(c.get_cpu_load()) ? c.get_cpu_load() : 0.0); + results_.push_back(std::isfinite(c.get_cpu_load()) ? c.get_cpu_load() : 0.0); + } + + void send_service_start_measuring(bool _start_measuring) { + std::shared_ptr<vsomeip::message> m = vsomeip::runtime::get()->create_request(protocol_ == protocol_e::PR_TCP); + m->set_service(cpu_load_test::service_id); + m->set_instance(cpu_load_test::instance_id); + _start_measuring ? m->set_method(cpu_load_test::method_id_cpu_measure_start) : m->set_method(cpu_load_test::method_id_cpu_measure_stop); + app_->send(m); + } + + void shutdown_service() { + request_->set_service(cpu_load_test::service_id); + request_->set_instance(cpu_load_test::instance_id); + request_->set_method(cpu_load_test::method_id_shutdown); + app_->send(request_); + } + +private: + protocol_e protocol_; + std::shared_ptr<vsomeip::application> app_; + std::shared_ptr<vsomeip::message> request_; + bool call_service_sync_; + bool shutdown_service_at_end_; + std::uint32_t sliding_window_size_; + std::mutex mutex_; + std::condition_variable condition_; + bool wait_for_availability_; + bool is_available_; + const std::uint32_t number_of_calls_; + std::uint32_t number_of_calls_current_; + std::uint32_t number_of_sent_messages_; + std::uint32_t number_of_sent_messages_total_; + std::uint32_t number_of_acknowledged_messages_; + + std::uint32_t payload_size_; + + bool wait_for_all_msg_acknowledged_; + std::mutex all_msg_acknowledged_mutex_; + std::condition_variable all_msg_acknowledged_cv_; + std::vector<double> results_; + std::atomic<bool> initialized_; + std::thread sender_; +}; + + +// this variables are changed via cmdline parameters +static protocol_e protocol(protocol_e::PR_UNKNOWN); +static std::uint32_t number_of_calls(0); +static std::uint32_t payload_size(40); +static bool call_service_sync(true); +static bool shutdown_service(true); + + +TEST(someip_load_test, DISABLED_send_messages_and_measure_cpu_load) +{ + cpu_load_test_client test_client_(protocol, number_of_calls, payload_size, call_service_sync, shutdown_service); +} + +#if defined(__linux__) || defined(ANDROID) +int main(int argc, char** argv) +{ + int i = 0; + while (i < argc) { + if(std::string("--protocol") == std::string(argv[i]) + || std::string("-p") == std::string(argv[i])) { + if(std::string("udp") == std::string(argv[i+1]) || + std::string("UDP") == std::string(argv[i+1])) { + protocol = protocol_e::PR_UDP; + i++; + } else if(std::string("tcp") == std::string(argv[i+1]) || + std::string("TCP") == std::string(argv[i+1])) { + protocol = protocol_e::PR_TCP; + i++; + } + } else if(std::string("--calls") == std::string(argv[i]) + || std::string("-c") == std::string(argv[i])) { + try { + number_of_calls = static_cast<std::uint32_t>(std::stoul(std::string(argv[i+1]), nullptr, 10)); + } catch (const std::exception &e) { + std::cerr << "Please specify a valid value for number of calls" << std::endl; + return(EXIT_FAILURE); + } + i++; + } else if(std::string("--mode") == std::string(argv[i]) + || std::string("-m") == std::string(argv[i])) { + if(std::string("sync") == std::string(argv[i+1]) || + std::string("SYNC") == std::string(argv[i+1])) { + call_service_sync = true; + i++; + } else if(std::string("async") == std::string(argv[i+1]) || + std::string("ASYNC") == std::string(argv[i+1])) { + call_service_sync = false; + i++; + } + } else if(std::string("--payload-size") == std::string(argv[i]) + || std::string("-pl") == std::string(argv[i])) { + try { + payload_size = static_cast<std::uint32_t>(std::stoul(std::string(argv[i+1]), nullptr, 10)); + } catch (const std::exception &e) { + std::cerr << "Please specify a valid values for payload size" << std::endl; + return(EXIT_FAILURE); + } + i++; + } else if(std::string("--help") == std::string(argv[i]) + || std::string("-h") == std::string(argv[i])) { + std::cout << "Available options:" << std::endl; + std::cout << "--protocol|-p: valid values TCP or UDP" << std::endl; + std::cout << "--calls|-c: number of message calls to do" << std::endl; + std::cout << "--mode|-m: mode sync or async" << std::endl; + std::cout << "--payload-size|-pl: payload size in Bytes default: 40" << std::endl; + } + i++; + } + + if(protocol == protocol_e::PR_UNKNOWN) { + std::cerr << "Please specify valid protocol mode, see --help" << std::endl; + return(EXIT_FAILURE); + } + if(!number_of_calls) { + std::cerr << "Please specify valid number of calls, see --help" << std::endl; + return(EXIT_FAILURE); + } + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/network_tests/cpu_load_tests/cpu_load_test_globals.hpp b/test/network_tests/cpu_load_tests/cpu_load_test_globals.hpp new file mode 100644 index 0000000..e6897e3 --- /dev/null +++ b/test/network_tests/cpu_load_tests/cpu_load_test_globals.hpp @@ -0,0 +1,18 @@ +// Copyright (C) 2014-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +namespace cpu_load_test { + +static constexpr vsomeip::service_t service_id(0x1111); +static constexpr vsomeip::instance_t instance_id(0x1); +static constexpr vsomeip::method_t method_id(0x1111); +static constexpr vsomeip::byte_t load_test_data(0xDD); +static constexpr vsomeip::length_t default_payload_length(40); +static constexpr vsomeip::method_t method_id_shutdown(0x7777); +static constexpr vsomeip::method_t method_id_cpu_measure_start(0x8888); +static constexpr vsomeip::method_t method_id_cpu_measure_stop(0x9999); +} diff --git a/test/network_tests/cpu_load_tests/cpu_load_test_master_starter.sh b/test/network_tests/cpu_load_tests/cpu_load_test_master_starter.sh new file mode 100755 index 0000000..30010c2 --- /dev/null +++ b/test/network_tests/cpu_load_tests/cpu_load_test_master_starter.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +FAIL=0 + +export VSOMEIP_CONFIGURATION=cpu_load_test_client_master.json +./cpu_load_test_client --protocol UDP --calls 1000 & +TEST_CLIENT_PID=$! +sleep 1 + +if [ ! -z "$USE_LXC_TEST" ]; then + echo "starting cpu load test on slave LXC" + ssh -tt -i $SANDBOX_ROOT_DIR/commonapi_main/lxc-config/.ssh/mgc_lxc/rsa_key_file.pub -o StrictHostKeyChecking=no root@$LXC_TEST_SLAVE_IP 'bash -ci "set -m; cd \$SANDBOX_TARGET_DIR/vsomeip_lib/test/network_tests; ./cpu_load_test_slave_starter.sh"' & +elif [ ! -z "$USE_DOCKER" ]; then + docker exec $DOCKER_IMAGE sh -c "cd $DOCKER_TESTS && ./cpu_load_test_slave_starter.sh" & +else +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Please now run: +** cpu_load_test_slave_starter.sh +** from an external host to successfully complete this test. +** +** You probably will need to adapt the 'unicast' settings in +** cpu_load_test_client_master.json, +** cpu_load_test_service_master.json, +** cpu_load_test_client_client.json and +** cpu_load_test_service_client.json to your personal setup. +******************************************************************************* +******************************************************************************* +End-of-message +fi + +# Fail gets incremented if either client or service exit +# with a non-zero exit code +wait $TEST_CLIENT_PID || FAIL=$(($FAIL+1)) + + +sleep 4 +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Now switching roles and running service on this host (master) +******************************************************************************* +******************************************************************************* +End-of-message + +export VSOMEIP_CONFIGURATION=cpu_load_test_service_master.json +./cpu_load_test_service & +sleep 1 + +# now we can wait to all jobs to finish +for job in $(jobs -p) +do + # Fail gets incremented if either client or service exit + # with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +# Check if both exited successfully +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi diff --git a/test/network_tests/cpu_load_tests/cpu_load_test_service.cpp b/test/network_tests/cpu_load_tests/cpu_load_test_service.cpp new file mode 100644 index 0000000..58c72e7 --- /dev/null +++ b/test/network_tests/cpu_load_tests/cpu_load_test_service.cpp @@ -0,0 +1,209 @@ +// Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <gtest/gtest.h> + +#include <vsomeip/vsomeip.hpp> + +#include <thread> +#include <mutex> +#include <condition_variable> +#include <functional> +#include <numeric> +#include <cmath> // for isfinite + +#include "cpu_load_test_globals.hpp" +#include <vsomeip/internal/logger.hpp> +#include "cpu_load_measurer.hpp" + +// for getpid +#include <sys/types.h> +#include <unistd.h> + +class cpu_load_test_service +{ +public: + cpu_load_test_service() : + app_(vsomeip::runtime::get()->create_application("cpu_load_test_service")), + is_registered_(false), + blocked_(false), + number_of_received_messages_(0), + number_of_received_messages_total_(0), + load_measurer_(static_cast<std::uint32_t>(::getpid())), + offer_thread_(std::bind(&cpu_load_test_service::run, this)) + { + } + + ~cpu_load_test_service() { + { + std::lock_guard<std::mutex> its_lock(mutex_); + blocked_ = true; + condition_.notify_one(); + } + offer_thread_.join(); + } + + bool init() + { + std::lock_guard<std::mutex> its_lock(mutex_); + + if (!app_->init()) { + ADD_FAILURE() << "Couldn't initialize application"; + return false; + } + app_->register_message_handler(cpu_load_test::service_id, + cpu_load_test::instance_id, cpu_load_test::method_id, + std::bind(&cpu_load_test_service::on_message, this, + std::placeholders::_1)); + + app_->register_message_handler(cpu_load_test::service_id, + cpu_load_test::instance_id, cpu_load_test::method_id_shutdown, + std::bind(&cpu_load_test_service::on_message_shutdown, this, + std::placeholders::_1)); + app_->register_message_handler(cpu_load_test::service_id, + cpu_load_test::instance_id, cpu_load_test::method_id_cpu_measure_start, + std::bind(&cpu_load_test_service::on_message_start_measuring, this, + std::placeholders::_1)); + app_->register_message_handler(cpu_load_test::service_id, + cpu_load_test::instance_id, cpu_load_test::method_id_cpu_measure_stop, + std::bind(&cpu_load_test_service::on_message_stop_measuring, this, + std::placeholders::_1)); + app_->register_state_handler( + std::bind(&cpu_load_test_service::on_state, this, + std::placeholders::_1)); + return true; + } + + void start() + { + VSOMEIP_INFO << "Starting..."; + app_->start(); + } + + void stop() + { + VSOMEIP_INFO << "Stopping..."; + app_->stop_offer_service(cpu_load_test::service_id, cpu_load_test::instance_id); + app_->clear_all_handler(); + app_->stop(); + } + + void on_state(vsomeip::state_type_e _state) + { + VSOMEIP_INFO << "Application " << app_->get_name() << " is " + << (_state == vsomeip::state_type_e::ST_REGISTERED ? "registered." : + "deregistered."); + + if(_state == vsomeip::state_type_e::ST_REGISTERED) + { + if(!is_registered_) + { + is_registered_ = true; + std::lock_guard<std::mutex> its_lock(mutex_); + blocked_ = true; + // "start" the run method thread + condition_.notify_one(); + } + } + else + { + is_registered_ = false; + } + } + + void on_message(const std::shared_ptr<vsomeip::message>& _request) + { + number_of_received_messages_++; + number_of_received_messages_total_++; + // send response + app_->send(vsomeip::runtime::get()->create_response(_request)); + } + + void on_message_start_measuring(const std::shared_ptr<vsomeip::message>& _request) + { + (void)_request; + load_measurer_.start(); + } + + void on_message_stop_measuring(const std::shared_ptr<vsomeip::message>& _request) + { + (void)_request; + load_measurer_.stop(); + VSOMEIP_DEBUG << "Received " << std::setw(4) << std::setfill('0') + << number_of_received_messages_ << " messages. CPU load [%]: " + << std::fixed << std::setprecision(2) + << (std::isfinite(load_measurer_.get_cpu_load()) ? load_measurer_.get_cpu_load() : 0.0); + results_.push_back(std::isfinite(load_measurer_.get_cpu_load()) ? load_measurer_.get_cpu_load() : 0.0); + number_of_received_messages_ = 0; + } + + void on_message_shutdown( + const std::shared_ptr<vsomeip::message>& _request) + { + (void)_request; + VSOMEIP_INFO << "Shutdown method was called, going down now."; + const double average_load(std::accumulate(results_.begin(), results_.end(), 0.0) / static_cast<double>(results_.size())); + VSOMEIP_INFO << "Received: " << number_of_received_messages_total_ + << " in total (excluding control messages). This caused: " + << std::fixed << std::setprecision(2) + << average_load << "% load in average (average of " + << results_.size() << " measurements)."; + + std::vector<double> results_no_zero; + for(const auto &v : results_) { + if(v > 0.0) { + results_no_zero.push_back(v); + } + } + const double average_load_no_zero(std::accumulate(results_no_zero.begin(), results_no_zero.end(), 0.0) / static_cast<double>(results_no_zero.size())); + VSOMEIP_INFO << "Sent: " << number_of_received_messages_total_ + << " messages in total (excluding control messages). This caused: " + << std::fixed << std::setprecision(2) + << average_load_no_zero << "% load in average, if measured cpu load " + << "was greater zero (average of " + << results_no_zero.size() << " measurements)."; + stop(); + } + + void run() + { + std::unique_lock<std::mutex> its_lock(mutex_); + while (!blocked_) { + condition_.wait(its_lock); + } + + app_->offer_service(cpu_load_test::service_id, cpu_load_test::instance_id); + } + +private: + std::shared_ptr<vsomeip::application> app_; + bool is_registered_; + + std::mutex mutex_; + std::condition_variable condition_; + bool blocked_; + std::uint32_t number_of_received_messages_; + std::uint32_t number_of_received_messages_total_; + cpu_load_measurer load_measurer_; + std::vector<double> results_; + std::thread offer_thread_; +}; + + +TEST(someip_payload_test, DISABLED_send_response_for_every_request) +{ + cpu_load_test_service test_service; + if (test_service.init()) { + test_service.start(); + } +} + +#if defined(__linux__) || defined(ANDROID) +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +#endif diff --git a/test/network_tests/cpu_load_tests/cpu_load_test_slave_starter.sh b/test/network_tests/cpu_load_tests/cpu_load_test_slave_starter.sh new file mode 100755 index 0000000..83ef19d --- /dev/null +++ b/test/network_tests/cpu_load_tests/cpu_load_test_slave_starter.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# Copyright (C) 2015-2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Purpose: This script is needed to start the services with +# one command. This is necessary as ctest - which is used to run the +# tests - isn't able to start multiple binaries for one testcase. Therefore +# the testcase simply executes this script. This script then runs the services +# and checks that all exit successfully. + +FAIL=0 + +export VSOMEIP_CONFIGURATION=cpu_load_test_service_slave.json +./cpu_load_test_service & + +# Wait until all applications are finished +for job in $(jobs -p) +do + # Fail gets incremented if one of the binaries exits + # with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + +cat <<End-of-message +******************************************************************************* +******************************************************************************* +** Now switching roles and running client on this host (slave) +******************************************************************************* +******************************************************************************* +End-of-message + +sleep 4 +export VSOMEIP_CONFIGURATION=cpu_load_test_client_slave.json +./cpu_load_test_client --protocol UDP --calls 1000 & + +for job in $(jobs -p) +do + # Fail gets incremented if one of the binaries exits + # with a non-zero exit code + wait $job || FAIL=$(($FAIL+1)) +done + + +# Check if both exited successfully +if [ $FAIL -eq 0 ] +then + exit 0 +else + exit 1 +fi |