Add stress tests for sdbus-c++

This commit is contained in:
Stanislav Angelovic
2019-03-26 08:59:50 +01:00
parent 7c968e78cb
commit 6f79c5bf14
12 changed files with 663 additions and 2 deletions

View File

@ -51,6 +51,22 @@ set(INTEGRATIONTESTS_SRCS
${INTEGRATIONTESTS_SOURCE_DIR}/TestingAdaptor.h
${INTEGRATIONTESTS_SOURCE_DIR}/TestingProxy.h)
set(PERFTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/perftests)
set(STRESSTESTS_CLIENT_SRCS
${PERFTESTS_SOURCE_DIR}/client.cpp
${PERFTESTS_SOURCE_DIR}/perftest-proxy.h)
set(STRESSTESTS_SERVER_SRCS
${PERFTESTS_SOURCE_DIR}/server.cpp
${PERFTESTS_SOURCE_DIR}/perftest-adaptor.h)
set(STRESSTESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/stresstests)
set(STRESSTESTS_SRCS
${STRESSTESTS_SOURCE_DIR}/stresstests.cpp
${STRESSTESTS_SOURCE_DIR}/fahrenheit-thermometer-adaptor.h
${STRESSTESTS_SOURCE_DIR}/fahrenheit-thermometer-proxy.h
${STRESSTESTS_SOURCE_DIR}/concatenator-adaptor.h
${STRESSTESTS_SOURCE_DIR}/concatenator-proxy.h)
#-------------------------------
# GENERAL COMPILER CONFIGURATION
#-------------------------------
@ -74,12 +90,22 @@ target_link_libraries(libsdbus-c++_integrationtests sdbus-c++ gmock gmock_main)
# Manual performance tests
option(ENABLE_PERFTESTS "Build and install manual performance tests (default OFF)" OFF)
if(ENABLE_PERFTESTS)
add_executable(libsdbus-c++_perftests_client perftests/client.cpp perftests/perftest-proxy.h)
add_executable(libsdbus-c++_perftests_client ${STRESSTESTS_CLIENT_SRCS})
target_link_libraries(libsdbus-c++_perftests_client sdbus-c++)
add_executable(libsdbus-c++_perftests_server perftests/server.cpp perftests/perftest-adaptor.h)
add_executable(libsdbus-c++_perftests_server ${STRESSTESTS_SERVER_SRCS})
target_link_libraries(libsdbus-c++_perftests_server sdbus-c++)
endif()
# Manual stress tests
option(ENABLE_STRESSTESTS "Build and install manual stress tests (default OFF)" ON)
if(ENABLE_STRESSTESTS)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
add_executable(sdbus-c++-stress-tests ${STRESSTESTS_SRCS})
target_link_libraries(sdbus-c++-stress-tests sdbus-c++ Threads::Threads)
endif()
#----------------------------------
# INSTALLATION
#----------------------------------
@ -94,6 +120,11 @@ if(ENABLE_PERFTESTS)
install(FILES perftests/files/org.sdbuscpp.perftest.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/dbus-1/system.d)
endif()
if(ENABLE_STRESSTESTS)
install(TARGETS sdbus-c++-stress-tests DESTINATION /opt/test/bin)
install(FILES perftests/files/org.sdbuscpp.stresstest.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/dbus-1/system.d)
endif()
#----------------------------------
# RUNNING THE TESTS UPON BUILD
#----------------------------------

View File

@ -0,0 +1,39 @@
/*
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__celsius_thermometer_adaptor_h__adaptor__H__
#define __sdbuscpp__celsius_thermometer_adaptor_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
namespace stresstest {
namespace celsius {
class thermometer_adaptor
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstest.celsius.thermometer";
protected:
thermometer_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("getCurrentTemperature").onInterface(interfaceName).implementedAs([this](){ return this->getCurrentTemperature(); });
}
private:
virtual uint32_t getCurrentTemperature() = 0;
private:
sdbus::IObject& object_;
};
}}}} // namespaces
#endif

View File

@ -0,0 +1,43 @@
/*
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__celsius_thermometer_proxy_h__proxy__H__
#define __sdbuscpp__celsius_thermometer_proxy_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
namespace stresstest {
namespace celsius {
class thermometer_proxy
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstest.celsius.thermometer";
protected:
thermometer_proxy(sdbus::IObjectProxy& object)
: object_(object)
{
}
public:
uint32_t getCurrentTemperature()
{
uint32_t result;
object_.callMethod("getCurrentTemperature").onInterface(interfaceName).storeResultsTo(result);
return result;
}
private:
sdbus::IObjectProxy& object_;
};
}}}} // namespaces
#endif

View File

@ -0,0 +1,45 @@
/*
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__concatenator_adaptor_h__adaptor__H__
#define __sdbuscpp__concatenator_adaptor_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
namespace stresstest {
class concatenator_adaptor
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstest.concatenator";
protected:
concatenator_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("concatenate").onInterface(interfaceName).implementedAs([this](sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params){ this->concatenate(std::move(result), std::move(params)); });
object_.registerSignal("concatenatedSignal").onInterface(interfaceName).withParameters<std::string>();
}
public:
void concatenatedSignal(const std::string& concatenatedString)
{
object_.emitSignal("concatenatedSignal").onInterface(interfaceName).withArguments(concatenatedString);
}
private:
virtual void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) = 0;
private:
sdbus::IObject& object_;
};
}}} // namespaces
#endif

View File

@ -0,0 +1,45 @@
/*
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__concatenator_proxy_h__proxy__H__
#define __sdbuscpp__concatenator_proxy_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
namespace stresstest {
class concatenator_proxy
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstest.concatenator";
protected:
concatenator_proxy(sdbus::IObjectProxy& object)
: object_(object)
{
object_.uponSignal("concatenatedSignal").onInterface(interfaceName).call([this](const std::string& concatenatedString){ this->onConcatenatedSignal(concatenatedString); });
}
virtual void onConcatenatedSignal(const std::string& concatenatedString) = 0;
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) = 0;
public:
void concatenate(const std::map<std::string, sdbus::Variant>& params)
{
object_.callMethodAsync("concatenate").onInterface(interfaceName).withArguments(params).uponReplyInvoke([this](const sdbus::Error* error, const std::string& result){ this->onConcatenateReply(result, error); });
}
private:
sdbus::IObjectProxy& object_;
};
}}} // namespaces
#endif

View File

@ -0,0 +1,39 @@
/*
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__fahrenheit_thermometer_adaptor_h__adaptor__H__
#define __sdbuscpp__fahrenheit_thermometer_adaptor_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
namespace stresstest {
namespace fahrenheit {
class thermometer_adaptor
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstest.fahrenheit.thermometer";
protected:
thermometer_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("getCurrentTemperature").onInterface(interfaceName).implementedAs([this](){ return this->getCurrentTemperature(); });
}
private:
virtual uint32_t getCurrentTemperature() = 0;
private:
sdbus::IObject& object_;
};
}}}} // namespaces
#endif

View File

@ -0,0 +1,43 @@
/*
* This file was automatically generated by sdbuscpp-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__fahrenheit_thermometer_proxy_h__proxy__H__
#define __sdbuscpp__fahrenheit_thermometer_proxy_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace sdbuscpp {
namespace stresstest {
namespace fahrenheit {
class thermometer_proxy
{
public:
static constexpr const char* interfaceName = "org.sdbuscpp.stresstest.fahrenheit.thermometer";
protected:
thermometer_proxy(sdbus::IObjectProxy& object)
: object_(object)
{
}
public:
uint32_t getCurrentTemperature()
{
uint32_t result;
object_.callMethod("getCurrentTemperature").onInterface(interfaceName).storeResultsTo(result);
return result;
}
private:
sdbus::IObjectProxy& object_;
};
}}}} // namespaces
#endif

View File

@ -0,0 +1,22 @@
<!-- This configuration file specifies the required security policies
for the Kistler DBUS example to run core daemon to work. -->
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<!-- ../system.conf have denied everything, so we just punch some holes -->
<policy context="default">
<allow own="org.sdbuscpp.stresstest.service1"/>
<allow send_destination="org.sdbuscpp.stresstest.service1"/>
<allow send_interface="org.sdbuscpp.stresstest.service1"/>
</policy>
<policy context="default">
<allow own="org.sdbuscpp.stresstest.service2"/>
<allow send_destination="org.sdbuscpp.stresstest.service2"/>
<allow send_interface="org.sdbuscpp.stresstest.service2"/>
</policy>
</busconfig>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<node name="/org/sdbuscpp/stresstest/celsius/thermometer">
<interface name="org.sdbuscpp.stresstest.celsius.thermometer">
<method name="getCurrentTemperature">
<arg type="u" name="result" direction="out" />
</method>
</interface>
</node>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<node name="/org/sdbuscpp/stresstest/concatenator">
<interface name="org.sdbuscpp.stresstest.concatenator">
<method name="concatenate">
<arg type="a{sv}" name="params" direction="in" />
<arg type="s" name="result" direction="out" />
<annotation name="org.freedesktop.DBus.Method.Async" value="clientserver" />
</method>
<signal name="concatenatedSignal">
<arg type="s" name="concatenatedString" />
</signal>
</interface>
</node>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<node name="/org/sdbuscpp/stresstest/fahrenheit/thermometer">
<interface name="org.sdbuscpp.stresstest.fahrenheit.thermometer">
<method name="getCurrentTemperature">
<arg type="u" name="result" direction="out" />
</method>
</interface>
</node>

322
test/stresstests/stresstests.cpp Executable file
View File

@ -0,0 +1,322 @@
/**
* (C) 2019 KISTLER INSTRUMENTE AG, Winterthur, Switzerland
*
* @file stresstests.cpp
*
* Created on: Jan 25, 2019
* Project: sdbus-c++
* Description: High-level D-Bus IPC C++ library based on sd-bus
*
* This file is part of sdbus-c++.
*
* sdbus-c++ is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* sdbus-c++ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with sdbus-c++. If not, see <http://www.gnu.org/licenses/>.
*/
#include "celsius-thermometer-adaptor.h"
#include "celsius-thermometer-proxy.h"
#include "fahrenheit-thermometer-adaptor.h"
#include "fahrenheit-thermometer-proxy.h"
#include "concatenator-adaptor.h"
#include "concatenator-proxy.h"
#include <sdbus-c++/sdbus-c++.h>
#include <vector>
#include <string>
#include <iostream>
#include <unistd.h>
#include <thread>
#include <chrono>
#include <cassert>
#include <atomic>
#include <sstream>
#include <mutex>
#include <condition_variable>
#include <queue>
using namespace std::chrono_literals;
using namespace std::string_literals;
#define SERVICE_1_BUS_NAME "org.sdbuscpp.stresstest.service1"
#define SERVICE_2_BUS_NAME "org.sdbuscpp.stresstest.service2"
#define CELSIUS_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstest/celsius/thermometer"
#define FAHRENHEIT_THERMOMETER_OBJECT_PATH "/org/sdbuscpp/stresstest/fahrenheit/thermometer"
#define CONCATENATOR_OBJECT_PATH "/org/sdbuscpp/stresstest/concatenator"
class CelsiusThermometerAdaptor : public sdbus::Interfaces<org::sdbuscpp::stresstest::celsius::thermometer_adaptor>
{
public:
using sdbus::Interfaces<org::sdbuscpp::stresstest::celsius::thermometer_adaptor>::Interfaces;
protected:
virtual uint32_t getCurrentTemperature() override
{
return m_currentTemperature++;
}
private:
uint32_t m_currentTemperature{};
};
class CelsiusThermometerProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::celsius::thermometer_proxy>
{
public:
using sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::celsius::thermometer_proxy>::ProxyInterfaces;
};
class FahrenheitThermometerAdaptor : public sdbus::Interfaces<org::sdbuscpp::stresstest::fahrenheit::thermometer_adaptor>
{
public:
FahrenheitThermometerAdaptor(sdbus::IConnection& connection, std::string objectPath)
: sdbus::Interfaces<org::sdbuscpp::stresstest::fahrenheit::thermometer_adaptor>(connection, std::move(objectPath))
, celsiusProxy_(connection, SERVICE_2_BUS_NAME, CELSIUS_THERMOMETER_OBJECT_PATH)
{
}
protected:
virtual uint32_t getCurrentTemperature() override
{
// In this D-Bus call, make yet another D-Bus call to another service over the same connection
return static_cast<uint32_t>(celsiusProxy_.getCurrentTemperature() * 1.8 + 32.);
}
private:
CelsiusThermometerProxy celsiusProxy_;
};
class FahrenheitThermometerProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::fahrenheit::thermometer_proxy>
{
public:
using sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::fahrenheit::thermometer_proxy>::ProxyInterfaces;
};
class ConcatenatorAdaptor : public sdbus::Interfaces<org::sdbuscpp::stresstest::concatenator_adaptor>
{
public:
ConcatenatorAdaptor(sdbus::IConnection& connection, std::string objectPath)
: sdbus::Interfaces<org::sdbuscpp::stresstest::concatenator_adaptor>(connection, std::move(objectPath))
{
unsigned int workers = std::thread::hardware_concurrency();
if (workers < 4)
workers = 4;
for (unsigned int i = 0; i < workers; ++i)
workers_.emplace_back([this]()
{
//std::cout << "Created worker thread 0x" << std::hex << std::this_thread::get_id() << std::dec << std::endl;
while(!exit_)
{
// Pop a work item from the queue
std::unique_lock<std::mutex> lock(mutex_);
cond_.wait(lock, [this]{return !requests_.empty() || exit_;});
if (exit_)
break;
auto request = std::move(requests_.front());
requests_.pop();
lock.unlock();
// Do concatenation work, return results and fire signal
auto aString = request.input.at("key1").get<std::string>();
auto aNumber = request.input.at("key2").get<uint32_t>();
auto resultString = aString + " " + std::to_string(aNumber);
request.result.returnResults(resultString);
concatenatedSignal(resultString);
}
});
}
~ConcatenatorAdaptor()
{
exit_ = true;
cond_.notify_all();
for (auto& worker : workers_)
worker.join();
}
protected:
virtual void concatenate(sdbus::Result<std::string>&& result, std::map<std::string, sdbus::Variant> params) override
{
std::unique_lock<std::mutex> lock(mutex_);
requests_.push(WorkItem{std::move(params), std::move(result)});
lock.unlock();
cond_.notify_one();
}
private:
struct WorkItem
{
std::map<std::string, sdbus::Variant> input;
sdbus::Result<std::string> result;
};
std::mutex mutex_;
std::condition_variable cond_;
std::queue<WorkItem> requests_;
std::vector<std::thread> workers_;
std::atomic<bool> exit_{};
};
class ConcatenatorProxy : public sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::concatenator_proxy>
{
public:
using sdbus::ProxyInterfaces<org::sdbuscpp::stresstest::concatenator_proxy>::ProxyInterfaces;
private:
virtual void onConcatenateReply(const std::string& result, const sdbus::Error* error) override
{
assert(error == nullptr);
std::stringstream str(result);
std::string aString;
str >> aString;
assert(aString == "sdbus-c++-stress-tests");
uint32_t aNumber;
str >> aNumber;
assert(aNumber >= 0);
++repliesReceived_;
}
virtual void onConcatenatedSignal(const std::string& concatenatedString) override
{
std::stringstream str(concatenatedString);
std::string aString;
str >> aString;
assert(aString == "sdbus-c++-stress-tests");
uint32_t aNumber;
str >> aNumber;
assert(aNumber >= 0);
++signalsReceived_;
}
public:
std::atomic<uint32_t> repliesReceived_;
std::atomic<uint32_t> signalsReceived_;
};
//-----------------------------------------
int main(int /*argc*/, char */*argv*/[])
{
auto service2Connection = sdbus::createSystemBusConnection(SERVICE_2_BUS_NAME);
std::thread service2Thread([&con = *service2Connection]()
{
CelsiusThermometerAdaptor thermometer(con, CELSIUS_THERMOMETER_OBJECT_PATH);
con.enterProcessingLoop();
});
auto service1Connection = sdbus::createSystemBusConnection(SERVICE_1_BUS_NAME);
std::thread service1Thread([&con = *service1Connection]()
{
ConcatenatorAdaptor concatenator(con, CONCATENATOR_OBJECT_PATH);
FahrenheitThermometerAdaptor thermometer(con, FAHRENHEIT_THERMOMETER_OBJECT_PATH);
con.enterProcessingLoop();
});
std::this_thread::sleep_for(100ms);
std::atomic<uint32_t> concatenationCallsMade{0};
std::atomic<uint32_t> concatenationRepliesReceived{0};
std::atomic<uint32_t> concatenationSignalsReceived{0};
std::atomic<uint32_t> thermometerCallsMade{0};
auto clientConnection = sdbus::createSystemBusConnection();
std::thread clientThread([&, &con = *clientConnection]()
{
std::atomic<bool> stopClients{false};
std::thread concatenatorThread([&]()
{
ConcatenatorProxy concatenator(con, SERVICE_1_BUS_NAME, CONCATENATOR_OBJECT_PATH);
uint32_t localCounter{};
// Issue async concatenate calls densely one after another
while (!stopClients)
{
std::map<std::string, sdbus::Variant> param;
param["key1"] = "sdbus-c++-stress-tests";
param["key2"] = localCounter++;
concatenator.concatenate(param);
if ((localCounter % 10) == 0)
{
// Make sure the system is catching up with our async requests,
// otherwise sleep a bit to slow down flooding the server.
assert(localCounter >= concatenator.repliesReceived_);
while ((localCounter - concatenator.repliesReceived_) > 20 && !stopClients)
std::this_thread::sleep_for(2ms);
// Update statistics
concatenationCallsMade = localCounter;
concatenationRepliesReceived = (uint32_t)concatenator.repliesReceived_;
concatenationSignalsReceived = (uint32_t)concatenator.signalsReceived_;
}
}
});
std::thread thermometerThread([&]()
{
FahrenheitThermometerProxy thermometer(con, SERVICE_1_BUS_NAME, FAHRENHEIT_THERMOMETER_OBJECT_PATH);
uint32_t localCounter{};
uint32_t previousTemperature{};
while (!stopClients)
{
localCounter++;
auto temperature = thermometer.getCurrentTemperature();
assert(temperature >= previousTemperature); // The temperature shall rise continually
previousTemperature = temperature;
std::this_thread::sleep_for(5ms);
if ((localCounter % 10) == 0)
thermometerCallsMade = localCounter;
}
});
con.enterProcessingLoop();
stopClients = true;
concatenatorThread.join();
thermometerThread.join();
});
std::atomic<bool> exitLogger{};
std::thread loggerThread([&]()
{
while (!exitLogger)
{
std::this_thread::sleep_for(1s);
std::cout << "Made " << concatenationCallsMade << " concatenation calls, received " << concatenationRepliesReceived << " replies and " << concatenationSignalsReceived << " signals so far." << std::endl;
std::cout << "Made " << thermometerCallsMade << " thermometer calls so far." << std::endl << std::endl;
}
});
getchar();
exitLogger = true;
loggerThread.join();
clientConnection->leaveProcessingLoop();
clientThread.join();
service1Connection->leaveProcessingLoop();
service1Thread.join();
service2Connection->leaveProcessingLoop();
service2Thread.join();
return 0;
}