From 40ce96e89595a6ffae0099b41630231daa5326c6 Mon Sep 17 00:00:00 2001 From: Caitlin Ross Date: Wed, 12 Jan 2022 14:39:14 -0500 Subject: [PATCH 01/10] add PluginManager to handle both engine and operator plugins --- source/adios2/CMakeLists.txt | 1 + source/adios2/helper/adiosDynamicBinder.cpp | 3 +- source/adios2/helper/adiosPluginManager.cpp | 287 ++++++++++++++++++++ source/adios2/helper/adiosPluginManager.h | 87 ++++++ 4 files changed, 376 insertions(+), 2 deletions(-) create mode 100644 source/adios2/helper/adiosPluginManager.cpp create mode 100644 source/adios2/helper/adiosPluginManager.h diff --git a/source/adios2/CMakeLists.txt b/source/adios2/CMakeLists.txt index bfd69cc090..40a06fca0d 100644 --- a/source/adios2/CMakeLists.txt +++ b/source/adios2/CMakeLists.txt @@ -31,6 +31,7 @@ add_library(adios2_core helper/adiosMath.cpp helper/adiosMemory.cpp helper/adiosNetwork.cpp + helper/adiosPluginManager.cpp helper/adiosString.cpp helper/adiosString.tcc helper/adiosSystem.cpp helper/adiosType.cpp diff --git a/source/adios2/helper/adiosDynamicBinder.cpp b/source/adios2/helper/adiosDynamicBinder.cpp index 4fe9f8b363..bc500b0122 100644 --- a/source/adios2/helper/adiosDynamicBinder.cpp +++ b/source/adios2/helper/adiosDynamicBinder.cpp @@ -31,9 +31,8 @@ struct DynamicBinder::Impl adios2sys::DynamicLoader::LibraryHandle m_LibraryHandle; }; -DynamicBinder::DynamicBinder(std::string libName) +DynamicBinder::DynamicBinder(std::string libName) : DynamicBinder(libName, "") { - DynamicBinder(libName, ""); } DynamicBinder::DynamicBinder(std::string libName, std::string libPath) diff --git a/source/adios2/helper/adiosPluginManager.cpp b/source/adios2/helper/adiosPluginManager.cpp new file mode 100644 index 0000000000..b4ba10c630 --- /dev/null +++ b/source/adios2/helper/adiosPluginManager.cpp @@ -0,0 +1,287 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + * + * adiosPluginManager.cpp + * + * Created on: Dec 14, 2021 + * Author: Caitlin Ross + */ + +#include "adios2/helper/adiosPluginManager.h" +#include "adios2/common/ADIOSTypes.h" +#include "adios2/core/IO.h" +#include "adios2/engine/plugin/PluginEngineInterface.h" +#include "adios2/helper/adiosComm.h" +#include "adios2/helper/adiosDynamicBinder.h" +#include "adios2/helper/adiosLog.h" +#include "adios2/helper/adiosString.h" + +#include + +#include +#include +#include +#include + +namespace adios2 +{ +namespace plugin +{ + +namespace +{ +const std::string pluginEnvVarName = "ADIOS2_PLUGIN_PATH"; + +struct EnginePluginInfo +{ + std::string m_LibraryName; + std::unique_ptr m_Binder; + PluginManager::EngineCreateFun m_HandleCreate; + PluginManager::EngineDestroyFun m_HandleDestroy; +}; + +struct OperatorPluginInfo +{ + std::string m_LibraryName; + std::unique_ptr m_Binder; + PluginManager::OperatorCreateFun m_HandleCreate; + PluginManager::OperatorDestroyFun m_HandleDestroy; +}; + +} // end anon namespace + +struct PluginManager::Impl +{ + std::unordered_map m_EngineRegistry; + std::unordered_map m_OperatorRegistry; + adios2::Params m_Parameters; + int m_Verbosity = 0; +}; + +PluginManager *PluginManager::m_Instance = nullptr; +bool PluginManager::m_Destroyed = false; + +PluginManager::PluginManager() : m_Impl(new Impl) {} + +PluginManager::~PluginManager() +{ + m_Instance = nullptr; + m_Destroyed = true; +} + +PluginManager &PluginManager::GetInstance() +{ + if (!m_Instance) + { + if (m_Destroyed) + { + throw std::runtime_error( + "Dead reference to PluginManager singleton"); + } + else + { + CreateInstance(); + } + } + return *m_Instance; +} + +void PluginManager::CreateInstance() +{ + static PluginManager theInstance; + m_Instance = &theInstance; +} + +void PluginManager::SetParameters(const Params ¶ms) +{ + helper::GetParameter(params, "verbose", m_Impl->m_Verbosity); +} + +bool PluginManager::LoadPlugin(const std::string &pluginName, + const std::string &pluginLibrary) +{ + if (m_Impl->m_EngineRegistry.find(pluginName) != + m_Impl->m_EngineRegistry.end() || + m_Impl->m_OperatorRegistry.find(pluginName) != + m_Impl->m_OperatorRegistry.end()) + { + return true; + } + + std::string allPluginPaths; + adios2sys::SystemTools::GetEnv(pluginEnvVarName, allPluginPaths); + if (allPluginPaths.empty()) + { + return OpenPlugin(pluginName, pluginLibrary, ""); + } + auto pathsSplit = + adios2sys::SystemTools::SplitString(allPluginPaths, ':', false); + + bool loaded = false; + auto pathIt = pathsSplit.begin(); + while (pathIt != pathsSplit.end() && !loaded) + { + try + { + loaded = OpenPlugin(pluginName, pluginLibrary, *pathIt); + } + catch (...) + { + std::cout << "catch block\n"; + loaded = false; + } + ++pathIt; + } + + if (!loaded) + { + helper::Log("Plugins", "PluginManager", "LoadPlugin", + "The plugin " + pluginLibrary + + " could not be loaded." + " Double check ADIOS2_PLUGIN_PATH or plugin library " + "name is correct." + "\nADIOS2_PLUGIN_PATH: " + + allPluginPaths, + helper::LogMode::WARNING); + } + + return loaded; +} + +bool PluginManager::OpenPlugin(const std::string &pluginName, + const std::string &pluginLibrary, + const std::string &pluginPath) +{ + helper::Log("Plugins", "PluginManager", "OpenPlugin", + "Attempting to open plugin " + pluginLibrary + " at path " + + pluginPath, + 5, m_Impl->m_Verbosity, helper::LogMode::INFO); + std::unique_ptr binder( + new helper::DynamicBinder(pluginLibrary, pluginPath)); + if (auto createHandle = binder->GetSymbol("EngineCreate")) + { + // we have an engine plugin + EnginePluginInfo plugin; + plugin.m_LibraryName = pluginLibrary; + plugin.m_HandleCreate = reinterpret_cast(createHandle); + if (!plugin.m_HandleCreate) + { + helper::Throw("Plugins", "PluginManager", + "OpenPlugin", + "Unable to locate EngineCreate" + " symbol in library " + + pluginLibrary); + } + + plugin.m_HandleDestroy = reinterpret_cast( + binder->GetSymbol("EngineDestroy")); + if (!plugin.m_HandleDestroy) + { + helper::Throw("Plugins", "PluginManager", + "OpenPlugin", + "Unable to locate EngineDestroy" + " symbol in library " + + pluginLibrary); + } + plugin.m_Binder = std::move(binder); + m_Impl->m_EngineRegistry[pluginName] = std::move(plugin); + helper::Log("Plugins", "PluginManager", "OpenPlugin", + "Engine Plugin " + pluginName + " successfully opened", 5, + m_Impl->m_Verbosity, helper::LogMode::INFO); + return true; + } + else if (auto createHandle = binder->GetSymbol("OperatorCreate")) + { + // should be an operator plugin + OperatorPluginInfo plugin; + plugin.m_LibraryName = pluginLibrary; + plugin.m_HandleCreate = + reinterpret_cast(createHandle); + if (!plugin.m_HandleCreate) + { + helper::Throw("Plugins", "PluginManager", + "OpenPlugin", + "Unable to locate OperatorCreate" + " symbol in library " + + pluginLibrary); + } + + plugin.m_HandleDestroy = reinterpret_cast( + binder->GetSymbol("OperatorDestroy")); + if (!plugin.m_HandleDestroy) + { + helper::Throw("Plugins", "PluginManager", + "OpenPlugin", + "Unable to locate OperatorDestroy" + " symbol in library " + + pluginLibrary); + } + plugin.m_Binder = std::move(binder); + m_Impl->m_OperatorRegistry[pluginName] = std::move(plugin); + helper::Log("Plugins", "PluginManager", "OpenPlugin", + "Operator Plugin " + pluginName + " successfully opened", 5, + m_Impl->m_Verbosity, helper::LogMode::INFO); + return true; + } + return false; +} + +PluginManager::EngineCreateFun +PluginManager::GetEngineCreateFun(const std::string &name) +{ + auto pluginIt = m_Impl->m_EngineRegistry.find(name); + if (pluginIt == m_Impl->m_EngineRegistry.end()) + { + helper::Throw( + "Plugins", "PluginManager", "GetEngineCreateFun", + "Couldn't find engine plugin named " + name); + } + + return pluginIt->second.m_HandleCreate; +} + +PluginManager::EngineDestroyFun +PluginManager::GetEngineDestroyFun(const std::string &name) +{ + auto pluginIt = m_Impl->m_EngineRegistry.find(name); + if (pluginIt == m_Impl->m_EngineRegistry.end()) + { + helper::Throw( + "Plugins", "PluginManager", "GetEngineDestroyFun", + "Couldn't find engine plugin named " + name); + } + + return pluginIt->second.m_HandleDestroy; +} + +PluginManager::OperatorCreateFun +PluginManager::GetOperatorCreateFun(const std::string &name) +{ + auto pluginIt = m_Impl->m_OperatorRegistry.find(name); + if (pluginIt == m_Impl->m_OperatorRegistry.end()) + { + helper::Throw( + "Plugins", "PluginManager", "GetOperatorCreateFun", + "Couldn't find operator plugin named " + name); + } + + return pluginIt->second.m_HandleCreate; +} + +PluginManager::OperatorDestroyFun +PluginManager::GetOperatorDestroyFun(const std::string &name) +{ + auto pluginIt = m_Impl->m_OperatorRegistry.find(name); + if (pluginIt == m_Impl->m_OperatorRegistry.end()) + { + helper::Throw( + "Plugins", "PluginManager", "GetOperatorDestroyFun", + "Couldn't find operator plugin named " + name); + } + + return pluginIt->second.m_HandleDestroy; +} + +} // end namespace plugin +} // end namespace adios2 diff --git a/source/adios2/helper/adiosPluginManager.h b/source/adios2/helper/adiosPluginManager.h new file mode 100644 index 0000000000..d503d30e77 --- /dev/null +++ b/source/adios2/helper/adiosPluginManager.h @@ -0,0 +1,87 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + * + * adiosPluginManager.h + * + * Created on: Dec 14, 2021 + * Author: Caitlin Ross + */ + +#ifndef ADIOS2_HELPER_PLUGINMANAGER_H +#define ADIOS2_HELPER_PLUGINMANAGER_H + +#include "adios2/engine/plugin/PluginEngineInterface.h" +#include "adios2/operator/plugin/PluginOperatorInterface.h" + +#include +#include +#include +#include + +namespace adios2 +{ +namespace plugin +{ + +class PluginManager +{ +public: + using EngineCreatePtr = std::add_pointer::type; + using EngineDestroyPtr = + std::add_pointer::type; + using EngineCreateFun = + std::function::type>; + using EngineDestroyFun = + std::function::type>; + + using OperatorCreatePtr = + std::add_pointer::type; + using OperatorDestroyPtr = + std::add_pointer::type; + using OperatorCreateFun = + std::function::type>; + using OperatorDestroyFun = + std::function::type>; + + static PluginManager &GetInstance(); + + void SetParameters(const Params ¶ms); + + /** + * Attempts to load a single plugin specified by pluginName and + * pluginLibrary. + */ + bool LoadPlugin(const std::string &pluginName, + const std::string &pluginLibrary); + + EngineCreateFun GetEngineCreateFun(const std::string &name); + EngineDestroyFun GetEngineDestroyFun(const std::string &name); + + OperatorCreateFun GetOperatorCreateFun(const std::string &name); + OperatorDestroyFun GetOperatorDestroyFun(const std::string &name); + +private: + PluginManager(); + PluginManager(const PluginManager &) = delete; + PluginManager &operator=(const PluginManager &) = delete; + virtual ~PluginManager(); + + static void CreateInstance(); + + bool OpenPlugin(const std::string &pluginName, + const std::string &pluginLibrary, + const std::string &pluginPath); + + static PluginManager *m_Instance; + static bool m_Destroyed; + + struct Impl; + std::unique_ptr m_Impl; +}; + +} // end namespace plugin +} // end namespace adios2 + +#endif /* ADIOS2_HELPER_PLUGINMANAGER_H */ From fa6d5cea3903cc66dff8545eb9126dd8e7b6755a Mon Sep 17 00:00:00 2001 From: Caitlin Ross Date: Tue, 22 Jun 2021 10:04:29 -0400 Subject: [PATCH 02/10] add support for operator plugins --- source/adios2/CMakeLists.txt | 1 + source/adios2/core/Operator.h | 1 + source/adios2/operator/OperatorFactory.cpp | 7 + .../adios2/operator/plugin/PluginOperator.cpp | 144 ++++++++++++++++++ .../adios2/operator/plugin/PluginOperator.h | 61 ++++++++ .../plugin/PluginOperatorInterface.cpp | 24 +++ .../operator/plugin/PluginOperatorInterface.h | 43 ++++++ source/adios2/toolkit/format/bp/BPBase.cpp | 5 +- source/adios2/toolkit/format/bp/BPBase.h | 3 +- 9 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 source/adios2/operator/plugin/PluginOperator.cpp create mode 100644 source/adios2/operator/plugin/PluginOperator.h create mode 100644 source/adios2/operator/plugin/PluginOperatorInterface.cpp create mode 100644 source/adios2/operator/plugin/PluginOperatorInterface.h diff --git a/source/adios2/CMakeLists.txt b/source/adios2/CMakeLists.txt index 40a06fca0d..9999cd5cf5 100644 --- a/source/adios2/CMakeLists.txt +++ b/source/adios2/CMakeLists.txt @@ -59,6 +59,7 @@ add_library(adios2_core engine/nullcore/NullCoreWriter.cpp engine/nullcore/NullCoreWriter.tcc engine/plugin/PluginEngine.cpp engine/plugin/PluginEngineInterface.cpp + operator/plugin/PluginOperator.cpp operator/plugin/PluginOperatorInterface.cpp #toolkit toolkit/format/buffer/Buffer.cpp toolkit/format/buffer/BufferV.cpp diff --git a/source/adios2/core/Operator.h b/source/adios2/core/Operator.h index fc99707d07..0be396dc5e 100644 --- a/source/adios2/core/Operator.h +++ b/source/adios2/core/Operator.h @@ -39,6 +39,7 @@ class Operator COMPRESS_MGARDPLUS = 8, CALLBACK_SIGNATURE1 = 51, CALLBACK_SIGNATURE2 = 52, + PLUGIN_INTERFACE = 53, COMPRESS_NULL = 127, }; diff --git a/source/adios2/operator/OperatorFactory.cpp b/source/adios2/operator/OperatorFactory.cpp index 33a2385092..a266b3d0db 100644 --- a/source/adios2/operator/OperatorFactory.cpp +++ b/source/adios2/operator/OperatorFactory.cpp @@ -11,6 +11,7 @@ #include "OperatorFactory.h" #include "adios2/helper/adiosFunctions.h" #include "adios2/operator/compress/CompressNull.h" +#include "adios2/operator/plugin/PluginOperator.h" #include #ifdef ADIOS2_HAVE_BLOSC @@ -73,6 +74,8 @@ std::string OperatorTypeToString(const Operator::OperatorType type) return "sz"; case Operator::COMPRESS_ZFP: return "zfp"; + case Operator::PLUGIN_INTERFACE: + return "plugin"; default: return "null"; } @@ -139,6 +142,10 @@ std::shared_ptr MakeOperator(const std::string &type, ret = std::make_shared(parameters); #endif } + else if (typeLowerCase == "plugin") + { + ret = std::make_shared(parameters); + } else if (typeLowerCase == "null") { ret = std::make_shared(parameters); diff --git a/source/adios2/operator/plugin/PluginOperator.cpp b/source/adios2/operator/plugin/PluginOperator.cpp new file mode 100644 index 0000000000..b997166727 --- /dev/null +++ b/source/adios2/operator/plugin/PluginOperator.cpp @@ -0,0 +1,144 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + * + * PluginOperator.cpp + * + * Created on: Dec 7, 2021 + * Author: Caitlin Ross + */ + +#include "PluginOperator.h" +#include "PluginOperatorInterface.h" + +#include +#include +#include +#include +#include + +#include "adios2/helper/adiosLog.h" +#include "adios2/helper/adiosPluginManager.h" +#include "adios2/helper/adiosString.h" + +#include + +namespace adios2 +{ +namespace plugin +{ + +/******************************************************************************/ + +struct PluginOperator::Impl +{ + Params m_PluginParams; + PluginManager::OperatorCreateFun m_HandleCreate; + PluginManager::OperatorDestroyFun m_HandleDestroy; + PluginOperatorInterface *m_Plugin = nullptr; + int m_Verbosity = 0; +}; + +/******************************************************************************/ + +PluginOperator::PluginOperator(const Params ¶meters) +: Operator("plugin", PLUGIN_INTERFACE, "plugin", parameters), m_Impl(new Impl) +{ + helper::GetParameter(m_Parameters, "verbose", m_Impl->m_Verbosity); + + // It is possible for 'pluginname' and 'pluginlibrary' to not be in + // m_Parameters (e.g., when using bpls, which will call InverseOperate). + // PluginName and PluginLibrary which will be saved in header info in the + // Operate() call, so that in this situation, the plugin can be loaded + // to perform InverseOperate. + auto pluginNameIt = m_Parameters.find("pluginname"); + auto pluginLibIt = m_Parameters.find("pluginlibrary"); + if (pluginNameIt != m_Parameters.end() && pluginLibIt != m_Parameters.end()) + { + m_Impl->m_PluginParams["PluginName"] = pluginNameIt->second; + m_Impl->m_PluginParams["PluginLibrary"] = pluginLibIt->second; + PluginInit(pluginNameIt->second, pluginLibIt->second); + } +} + +PluginOperator::~PluginOperator() { m_Impl->m_HandleDestroy(m_Impl->m_Plugin); } + +void PluginOperator::PluginInit(const std::string &pluginName, + const std::string &pluginLibrary) +{ + if (m_Impl->m_Plugin) + { + return; + } + + auto &pluginManager = PluginManager::GetInstance(); + pluginManager.LoadPlugin(pluginName, pluginLibrary); + + m_Impl->m_HandleCreate = pluginManager.GetOperatorCreateFun(pluginName); + m_Impl->m_HandleDestroy = pluginManager.GetOperatorDestroyFun(pluginName); + m_Impl->m_Plugin = m_Impl->m_HandleCreate(m_Parameters); +} + +size_t PluginOperator::Operate(const char *dataIn, const Dims &blockStart, + const Dims &blockCount, const DataType type, + char *bufferOut) +{ + // handle common header first + size_t offset = 0; + const uint8_t bufferVersion = 1; + MakeCommonHeader(bufferOut, offset, bufferVersion); + + // plugin specific header, this enables InverseOperate to work in situations + // where user parameters aren't passed such as bpls + PutParameters(bufferOut, offset, m_Impl->m_PluginParams); + + // add offset to the bufferOut pointer, so that way the plugin doesn't + // need to know anything about the plugin header. + size_t pluginSize = m_Impl->m_Plugin->Operate( + dataIn, blockStart, blockCount, type, bufferOut + offset); + return offset + pluginSize; +} + +size_t PluginOperator::InverseOperate(const char *bufferIn, const size_t sizeIn, + char *dataOut) +{ + size_t offset = 4; // skip 4 bytes for the common header + + // now handle plugin specific header + m_Impl->m_PluginParams = GetParameters(bufferIn, offset); + + auto paramPluginNameIt = m_Impl->m_PluginParams.find("PluginName"); + if (paramPluginNameIt == m_Impl->m_PluginParams.end()) + { + helper::Throw( + "Plugins", "PluginOperator", "InverseOperate", + "PluginName could not be found in the plugin header"); + } + std::string pluginName = paramPluginNameIt->second; + + auto paramPluginLibraryIt = m_Impl->m_PluginParams.find("PluginLibrary"); + if (paramPluginLibraryIt == m_Impl->m_PluginParams.end()) + { + helper::Throw( + "Plugins", "PluginOperator", "InverseOperate", + "PluginLibrary could not be found in the plugin header"); + } + const std::string &pluginLibrary = paramPluginLibraryIt->second; + + // now set up the plugin if it hasn't already + PluginInit(pluginName, pluginLibrary); + + // add offset to bufferIn, so plugin doesn't have to worry about plugin + // header or common header + size_t pluginSize = m_Impl->m_Plugin->InverseOperate( + bufferIn + offset, sizeIn - offset, dataOut); + return pluginSize; +} + +bool PluginOperator::IsDataTypeValid(const DataType type) const +{ + return m_Impl->m_Plugin->IsDataTypeValid(type); +} + +} // end namespace plugin +} // end namespace adios2 diff --git a/source/adios2/operator/plugin/PluginOperator.h b/source/adios2/operator/plugin/PluginOperator.h new file mode 100644 index 0000000000..4bd24a338d --- /dev/null +++ b/source/adios2/operator/plugin/PluginOperator.h @@ -0,0 +1,61 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + * + * PluginOperator.h Support for an operator implemented outside libadios2 + * + * Created on: Dec 7, 2021 + * Author: Caitlin Ross + */ + +#ifndef ADIOS2_OPERATOR_PLUGIN_PLUGINOPERATOR_H_ +#define ADIOS2_OPERATOR_PLUGIN_PLUGINOPERATOR_H_ + +#include "PluginOperatorInterface.h" + +#include // for function +#include // for unique_ptr +#include // for string +#include // for add_pointer +#include // for vector + +#include "adios2/common/ADIOSMacros.h" +#include "adios2/common/ADIOSTypes.h" +#include "adios2/core/IO.h" +#include "adios2/core/Operator.h" +#include "adios2/helper/adiosComm.h" + +namespace adios2 +{ +namespace plugin +{ + +/** A front-end wrapper for an operator implemented outside of libadios2 */ +class PluginOperator : public core::Operator +{ +public: + PluginOperator(const Params ¶meters); + virtual ~PluginOperator(); + + size_t Operate(const char *dataIn, const Dims &blockStart, + const Dims &blockCount, const DataType type, + char *bufferOut) override; + + size_t InverseOperate(const char *bufferIn, const size_t sizeIn, + char *dataOut) override; + + bool IsDataTypeValid(const DataType type) const override; + +protected: + void PluginInit(const std::string &pluginName, + const std::string &pluginLibrary); + +private: + struct Impl; + std::unique_ptr m_Impl; +}; + +} // end namespace plugin +} // end namespace adios2 + +#endif /* ADIOS2_OPERATOR_PLUGIN_PLUGINOPERATOR_H_ */ diff --git a/source/adios2/operator/plugin/PluginOperatorInterface.cpp b/source/adios2/operator/plugin/PluginOperatorInterface.cpp new file mode 100644 index 0000000000..865614d3fd --- /dev/null +++ b/source/adios2/operator/plugin/PluginOperatorInterface.cpp @@ -0,0 +1,24 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + * + * PluginOperatorInterface.cxx + * + * Created on: Dec 7, 2021 + * Author: Caitlin Ross + */ + +#include "PluginOperatorInterface.h" + +namespace adios2 +{ +namespace plugin +{ + +PluginOperatorInterface::PluginOperatorInterface(const Params ¶meters) +: Operator("PluginInterface", PLUGIN_INTERFACE, "plugin", parameters) +{ +} + +} // end namespace plugin +} // end namespace adios2 diff --git a/source/adios2/operator/plugin/PluginOperatorInterface.h b/source/adios2/operator/plugin/PluginOperatorInterface.h new file mode 100644 index 0000000000..c0e78a8402 --- /dev/null +++ b/source/adios2/operator/plugin/PluginOperatorInterface.h @@ -0,0 +1,43 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + * + * PluginOperatorInterface.h Operators using the plugin interface should derive + * from this class. + * + * Created on: Dec 7, 2021 + * Author: Caitlin Ross + */ + +#ifndef ADIOS2_OPERATOR_PLUGIN_PLUGINOPERATORINTERFACE_H_ +#define ADIOS2_OPERATOR_PLUGIN_PLUGINOPERATORINTERFACE_H_ + +/// \cond EXCLUDE_FROM_DOXYGEN +/// \endcond + +#include "adios2/common/ADIOSConfig.h" +#include "adios2/common/ADIOSTypes.h" +#include "adios2/core/IO.h" +#include "adios2/core/Operator.h" +#include "adios2/helper/adiosComm.h" + +namespace adios2 +{ +namespace plugin +{ + +/** An operator interface to be used by the plugin infrastructure */ +class PluginOperatorInterface : public core::Operator +{ + // Give the plugin operator access to everything + friend class PluginOperator; + +public: + PluginOperatorInterface(const Params ¶meters); + virtual ~PluginOperatorInterface() = default; +}; + +} // end namespace plugin +} // end namespace adios2 + +#endif /* ADIOS2_OPERATOR_PLUGIN_PLUGINOPERATORINTERFACE_H_ */ diff --git a/source/adios2/toolkit/format/bp/BPBase.cpp b/source/adios2/toolkit/format/bp/BPBase.cpp index 53d0318818..8099080e2b 100644 --- a/source/adios2/toolkit/format/bp/BPBase.cpp +++ b/source/adios2/toolkit/format/bp/BPBase.cpp @@ -444,7 +444,7 @@ std::string BPBase::ReadBPString(const std::vector &buffer, // static members const std::set BPBase::m_TransformTypes = { {"unknown", "none", "identity", "bzip2", "sz", "zfp", "mgard", "png", - "blosc", "sirius", "mgardplus"}}; + "blosc", "sirius", "mgardplus", "plugin"}}; const std::map BPBase::m_TransformTypesToNames = { {transform_unknown, "unknown"}, @@ -457,7 +457,8 @@ const std::map BPBase::m_TransformTypesToNames = { {transform_bzip2, "bzip2"}, {transform_blosc, "blosc"}, {transform_sirius, "sirius"}, - {transform_mgardplus, "mgardplus"}}; + {transform_mgardplus, "mgardplus"}, + {transform_plugin, "plugin"}}; BPBase::TransformTypes BPBase::TransformTypeEnum(const std::string transformType) const noexcept diff --git a/source/adios2/toolkit/format/bp/BPBase.h b/source/adios2/toolkit/format/bp/BPBase.h index 2dfb497c83..7daa7cce48 100644 --- a/source/adios2/toolkit/format/bp/BPBase.h +++ b/source/adios2/toolkit/format/bp/BPBase.h @@ -453,7 +453,8 @@ class BPBase transform_mgard = 12, transform_png = 13, transform_sirius = 14, - transform_mgardplus = 15 + transform_mgardplus = 15, + transform_plugin = 16 }; /** Supported transform types */ From 076442895de51e67d982b3687ae9d5e1473d4d7d Mon Sep 17 00:00:00 2001 From: Caitlin Ross Date: Tue, 7 Dec 2021 16:50:48 -0500 Subject: [PATCH 03/10] add operator plugin example --- examples/plugins/CMakeLists.txt | 1 + examples/plugins/operator/CMakeLists.txt | 22 ++ .../plugins/operator/EncryptionOperator.cpp | 182 +++++++++++ .../plugins/operator/EncryptionOperator.h | 61 ++++ examples/plugins/operator/FindSodium.cmake | 297 ++++++++++++++++++ .../operator/examplePluginOperator_read.cpp | 106 +++++++ .../operator/examplePluginOperator_write.cpp | 95 ++++++ .../plugins/operator/example_operator.xml | 40 +++ 8 files changed, 804 insertions(+) create mode 100644 examples/plugins/operator/CMakeLists.txt create mode 100644 examples/plugins/operator/EncryptionOperator.cpp create mode 100644 examples/plugins/operator/EncryptionOperator.h create mode 100644 examples/plugins/operator/FindSodium.cmake create mode 100644 examples/plugins/operator/examplePluginOperator_read.cpp create mode 100644 examples/plugins/operator/examplePluginOperator_write.cpp create mode 100644 examples/plugins/operator/example_operator.xml diff --git a/examples/plugins/CMakeLists.txt b/examples/plugins/CMakeLists.txt index 3f65197ad1..5fed9662d6 100644 --- a/examples/plugins/CMakeLists.txt +++ b/examples/plugins/CMakeLists.txt @@ -4,3 +4,4 @@ #------------------------------------------------------------------------------# add_subdirectory(engine) +add_subdirectory(operator) diff --git a/examples/plugins/operator/CMakeLists.txt b/examples/plugins/operator/CMakeLists.txt new file mode 100644 index 0000000000..b11e256abf --- /dev/null +++ b/examples/plugins/operator/CMakeLists.txt @@ -0,0 +1,22 @@ +#------------------------------------------------------------------------------# +# Distributed under the OSI-approved Apache License, Version 2.0. See +# accompanying file Copyright.txt for details. +#------------------------------------------------------------------------------# + +list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}) +find_package(Sodium 1.8 REQUIRED) + +add_library(EncryptionOperator + EncryptionOperator.cpp +) +target_link_libraries(EncryptionOperator adios2::cxx11 adios2_core sodium) + +add_executable(exampleOperatorPlugin_write + examplePluginOperator_write.cpp +) +target_link_libraries(exampleOperatorPlugin_write adios2::cxx11) + +add_executable(exampleOperatorPlugin_read + examplePluginOperator_read.cpp +) +target_link_libraries(exampleOperatorPlugin_read adios2::cxx11) diff --git a/examples/plugins/operator/EncryptionOperator.cpp b/examples/plugins/operator/EncryptionOperator.cpp new file mode 100644 index 0000000000..5af5bbe9cc --- /dev/null +++ b/examples/plugins/operator/EncryptionOperator.cpp @@ -0,0 +1,182 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + * + * EncryptionOperator.cpp + * + * Created on: Dec 7, 2021 + * Author: Caitlin Ross + */ + +#include "EncryptionOperator.h" + +#include +#include + +#include + +namespace adios2 +{ +namespace plugin +{ + +struct EncryptionOperator::EncryptImpl +{ + std::string KeyFilename; + unsigned char Key[crypto_secretbox_KEYBYTES]; + bool KeyValid = false; + + ~EncryptImpl() + { + // unlocks the memory location as well as zeroing out the data + sodium_munlock(Key, crypto_secretbox_KEYBYTES); + } + + void GenerateOrReadKey() + { + // if the key file already exists, we'll use that key, + // otherwise we'll generate one and write it out + std::fstream keyFile(KeyFilename.c_str()); + if (keyFile) + { + keyFile.read(reinterpret_cast(&Key), + crypto_secretbox_KEYBYTES); + keyFile.close(); + } + else + { + keyFile.open(KeyFilename.c_str(), std::fstream::out); + if (!keyFile) + { + throw std::runtime_error("couldn't open file to write key"); + } + crypto_secretbox_keygen(Key); + keyFile.write(reinterpret_cast(&Key), + crypto_secretbox_KEYBYTES); + keyFile.close(); + } + + // lock the key to avoid swapping to disk + if (sodium_mlock(Key, crypto_secretbox_KEYBYTES) == -1) + { + throw std::runtime_error( + "Unable to lock memory location of secret key," + " due to system limit on amount of memory that can be locked " + "by a process."); + } + KeyValid = true; + } +}; + +EncryptionOperator::EncryptionOperator(const Params ¶meters) +: PluginOperatorInterface(parameters), Impl(new EncryptImpl) +{ + if (sodium_init() < 0) + { + throw std::runtime_error("libsodium could not be initialized"); + } + + // in the case "secretkeyfile" is found, so we know the operator should + // calling Operate(). If "secretkeyfile" is not found, then the operator + // should be calling InverseOperate(), due to ADIOS calling InverseOperate() + // not allowing Parameters to be passed. + auto skFileIt = m_Parameters.find("secretkeyfile"); + if (skFileIt != m_Parameters.end()) + { + Impl->KeyFilename = skFileIt->second; + Impl->GenerateOrReadKey(); + } +} + +EncryptionOperator::~EncryptionOperator() {} + +size_t EncryptionOperator::Operate(const char *dataIn, const Dims &blockStart, + const Dims &blockCount, const DataType type, + char *bufferOut) +{ + if (!Impl->KeyValid) + { + throw std::runtime_error("EncryptionOperator::Operate was called, but" + " a valid secret key has not been generated. " + "Did you add the SecretKeyFile" + " param when setting up the operator?"); + } + + // offset for writing into bufferOut + size_t offset = 0; + + // write any parameters we need to save for the InverseOperate() call + // In this case, we just write out the size of the data + size_t sizeIn = + helper::GetTotalSize(blockCount, helper::GetDataTypeSize(type)); + PutParameter(bufferOut, offset, sizeIn); + + // create the nonce directly in the output buffer, since we'll need it for + // decryption + unsigned char *nonce = + reinterpret_cast(bufferOut + offset); + randombytes_buf(nonce, crypto_secretbox_NONCEBYTES); + offset += crypto_secretbox_NONCEBYTES; + + // encrypt data directly into the output buffer + size_t cipherTextSize = sizeIn + crypto_secretbox_MACBYTES; + unsigned char *cipherText = + reinterpret_cast(bufferOut + offset); + crypto_secretbox_easy(cipherText, + reinterpret_cast(dataIn), + sizeIn, nonce, Impl->Key); + offset += cipherTextSize; + + // need to return the size of data in the buffer + return offset; +} + +size_t EncryptionOperator::InverseOperate(const char *bufferIn, + const size_t sizeIn, char *dataOut) +{ + size_t offset = 0; + + // need to grab any parameter(s) we saved in Operate() + const size_t dataBytes = GetParameter(bufferIn, offset); + + // grab the nonce ptr + const unsigned char *nonce = + reinterpret_cast(bufferIn + offset); + offset += crypto_secretbox_NONCEBYTES; + + // grab the cipher text ptr + size_t cipherTextSize = dataBytes + crypto_secretbox_MACBYTES; + const unsigned char *cipherText = + reinterpret_cast(bufferIn + offset); + offset += cipherTextSize; + + // decrypt directly into dataOut buffer + if (crypto_secretbox_open_easy(reinterpret_cast(dataOut), + cipherText, cipherTextSize, nonce, + Impl->Key) != 0) + { + throw std::runtime_error("message forged!"); + } + + // return the size of the data + return dataBytes; +} + +bool EncryptionOperator::IsDataTypeValid(const DataType type) const +{ + return true; +} + +} // end namespace plugin +} // end namespace adios2 + +extern "C" { + +adios2::plugin::EncryptionOperator * +OperatorCreate(const adios2::Params ¶meters) +{ + return new adios2::plugin::EncryptionOperator(parameters); +} + +void OperatorDestroy(adios2::plugin::EncryptionOperator *obj) { delete obj; } +} diff --git a/examples/plugins/operator/EncryptionOperator.h b/examples/plugins/operator/EncryptionOperator.h new file mode 100644 index 0000000000..2558f39627 --- /dev/null +++ b/examples/plugins/operator/EncryptionOperator.h @@ -0,0 +1,61 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + * + * EncryptionOperator.h + * + * Created on: Dec 7, 2021 + * Author: Caitlin Ross + */ + +#ifndef ENCRYPTIONOPERATOR_H_ +#define ENCRYPTIONOPERATOR_H_ + +#include + +#include "adios2/common/ADIOSTypes.h" +#include "adios2/operator/plugin/PluginOperatorInterface.h" + +namespace adios2 +{ +namespace plugin +{ + +/** EncryptionOperator that uses libsodium. Secret-key encryption is used. + * The user must provide the 'SecretKeyFile' param with a path to the secret + * key. If this file exists, it will use the key in the file (which should + * have been generated using libsodium's crypto_secretbox_keygen().) + * If the file doesn't exist, the operator will generate it using this call + * and write it out to the specified file. + */ +class EncryptionOperator : public PluginOperatorInterface +{ +public: + EncryptionOperator(const Params ¶meters); + virtual ~EncryptionOperator(); + + size_t Operate(const char *dataIn, const Dims &blockStart, + const Dims &blockCount, const DataType type, + char *bufferOut) override; + + size_t InverseOperate(const char *bufferIn, const size_t sizeIn, + char *dataOut) override; + + bool IsDataTypeValid(const DataType type) const override; + +private: + struct EncryptImpl; + std::unique_ptr Impl; +}; + +} // end namespace plugin +} // end namespace adios2 + +extern "C" { + +adios2::plugin::EncryptionOperator * +OperatorCreate(const adios2::Params ¶meters); +void OperatorDestroy(adios2::plugin::EncryptionOperator *obj); +} + +#endif /* ENCYRPTIONOPERATOR_H_ */ diff --git a/examples/plugins/operator/FindSodium.cmake b/examples/plugins/operator/FindSodium.cmake new file mode 100644 index 0000000000..3c3f1245c1 --- /dev/null +++ b/examples/plugins/operator/FindSodium.cmake @@ -0,0 +1,297 @@ +# Written in 2016 by Henrik Steffen Gaßmann +# +# To the extent possible under law, the author(s) have dedicated all +# copyright and related and neighboring rights to this software to the +# public domain worldwide. This software is distributed without any warranty. +# +# You should have received a copy of the CC0 Public Domain Dedication +# along with this software. If not, see +# +# http://creativecommons.org/publicdomain/zero/1.0/ +# +######################################################################## +# Tries to find the local libsodium installation. +# +# On Windows the sodium_DIR environment variable is used as a default +# hint which can be overridden by setting the corresponding cmake variable. +# +# Once done the following variables will be defined: +# +# sodium_FOUND +# sodium_INCLUDE_DIR +# sodium_LIBRARY_DEBUG +# sodium_LIBRARY_RELEASE +# +# +# Furthermore an imported "sodium" target is created. +# + +if (CMAKE_C_COMPILER_ID STREQUAL "GNU" + OR CMAKE_C_COMPILER_ID STREQUAL "Clang") + set(_GCC_COMPATIBLE 1) +endif() + +# static library option +if (NOT DEFINED sodium_USE_STATIC_LIBS) + option(sodium_USE_STATIC_LIBS "enable to statically link against sodium" OFF) +endif() +if(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST)) + unset(sodium_LIBRARY CACHE) + unset(sodium_LIBRARY_DEBUG CACHE) + unset(sodium_LIBRARY_RELEASE CACHE) + unset(sodium_DLL_DEBUG CACHE) + unset(sodium_DLL_RELEASE CACHE) + set(sodium_USE_STATIC_LIBS_LAST ${sodium_USE_STATIC_LIBS} CACHE INTERNAL "internal change tracking variable") +endif() + + +######################################################################## +# UNIX +if (UNIX) + # import pkg-config + find_package(PkgConfig QUIET) + if (PKG_CONFIG_FOUND) + pkg_check_modules(sodium_PKG QUIET libsodium) + endif() + + if(sodium_USE_STATIC_LIBS) + foreach(_libname ${sodium_PKG_STATIC_LIBRARIES}) + if (NOT _libname MATCHES "^lib.*\\.a$") # ignore strings already ending with .a + list(INSERT sodium_PKG_STATIC_LIBRARIES 0 "lib${_libname}.a") + endif() + endforeach() + list(REMOVE_DUPLICATES sodium_PKG_STATIC_LIBRARIES) + + # if pkgconfig for libsodium doesn't provide + # static lib info, then override PKG_STATIC here.. + if (NOT sodium_PKG_STATIC_FOUND) + set(sodium_PKG_STATIC_LIBRARIES libsodium.a) + endif() + + set(XPREFIX sodium_PKG_STATIC) + else() + if (NOT sodium_PKG_FOUND) + set(sodium_PKG_LIBRARIES sodium) + endif() + + set(XPREFIX sodium_PKG) + endif() + + find_path(sodium_INCLUDE_DIR sodium.h + HINTS ${${XPREFIX}_INCLUDE_DIRS} + ) + find_library(sodium_LIBRARY_DEBUG NAMES ${${XPREFIX}_LIBRARIES} + HINTS ${${XPREFIX}_LIBRARY_DIRS} + ) + find_library(sodium_LIBRARY_RELEASE NAMES ${${XPREFIX}_LIBRARIES} + HINTS ${${XPREFIX}_LIBRARY_DIRS} + ) + + +######################################################################## +# Windows +elseif (WIN32) + set(sodium_DIR "$ENV{sodium_DIR}" CACHE FILEPATH "sodium install directory") + mark_as_advanced(sodium_DIR) + + find_path(sodium_INCLUDE_DIR sodium.h + HINTS ${sodium_DIR} + PATH_SUFFIXES include + ) + + if (MSVC) + # detect target architecture + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp" [=[ + #if defined _M_IX86 + #error ARCH_VALUE x86_32 + #elif defined _M_X64 + #error ARCH_VALUE x86_64 + #endif + #error ARCH_VALUE unknown + ]=]) + try_compile(_UNUSED_VAR "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp" + OUTPUT_VARIABLE _COMPILATION_LOG + ) + string(REGEX REPLACE ".*ARCH_VALUE ([a-zA-Z0-9_]+).*" "\\1" _TARGET_ARCH "${_COMPILATION_LOG}") + + # construct library path + if (_TARGET_ARCH STREQUAL "x86_32") + string(APPEND _PLATFORM_PATH "Win32") + elseif(_TARGET_ARCH STREQUAL "x86_64") + string(APPEND _PLATFORM_PATH "x64") + else() + message(FATAL_ERROR "the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake.") + endif() + string(APPEND _PLATFORM_PATH "/$$CONFIG$$") + + if (MSVC_VERSION LESS 1900) + math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60") + else() + math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50") + endif() + string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}") + + if (sodium_USE_STATIC_LIBS) + string(APPEND _PLATFORM_PATH "/static") + else() + string(APPEND _PLATFORM_PATH "/dynamic") + endif() + + string(REPLACE "$$CONFIG$$" "Debug" _DEBUG_PATH_SUFFIX "${_PLATFORM_PATH}") + string(REPLACE "$$CONFIG$$" "Release" _RELEASE_PATH_SUFFIX "${_PLATFORM_PATH}") + + find_library(sodium_LIBRARY_DEBUG libsodium.lib + HINTS ${sodium_DIR} + PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX} + ) + find_library(sodium_LIBRARY_RELEASE libsodium.lib + HINTS ${sodium_DIR} + PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX} + ) + if (NOT sodium_USE_STATIC_LIBS) + set(CMAKE_FIND_LIBRARY_SUFFIXES_BCK ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll") + find_library(sodium_DLL_DEBUG libsodium + HINTS ${sodium_DIR} + PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX} + ) + find_library(sodium_DLL_RELEASE libsodium + HINTS ${sodium_DIR} + PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX} + ) + set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BCK}) + endif() + + elseif(_GCC_COMPATIBLE) + if (sodium_USE_STATIC_LIBS) + find_library(sodium_LIBRARY_DEBUG libsodium.a + HINTS ${sodium_DIR} + PATH_SUFFIXES lib + ) + find_library(sodium_LIBRARY_RELEASE libsodium.a + HINTS ${sodium_DIR} + PATH_SUFFIXES lib + ) + else() + find_library(sodium_LIBRARY_DEBUG libsodium.dll.a + HINTS ${sodium_DIR} + PATH_SUFFIXES lib + ) + find_library(sodium_LIBRARY_RELEASE libsodium.dll.a + HINTS ${sodium_DIR} + PATH_SUFFIXES lib + ) + + file(GLOB _DLL + LIST_DIRECTORIES false + RELATIVE "${sodium_DIR}/bin" + "${sodium_DIR}/bin/libsodium*.dll" + ) + find_library(sodium_DLL_DEBUG ${_DLL} libsodium + HINTS ${sodium_DIR} + PATH_SUFFIXES bin + ) + find_library(sodium_DLL_RELEASE ${_DLL} libsodium + HINTS ${sodium_DIR} + PATH_SUFFIXES bin + ) + endif() + else() + message(FATAL_ERROR "this platform is not supported by FindSodium.cmake") + endif() + + +######################################################################## +# unsupported +else() + message(FATAL_ERROR "this platform is not supported by FindSodium.cmake") +endif() + + +######################################################################## +# common stuff + +# extract sodium version +if (sodium_INCLUDE_DIR) + set(_VERSION_HEADER "${_INCLUDE_DIR}/sodium/version.h") + if (EXISTS _VERSION_HEADER) + file(READ "${_VERSION_HEADER}" _VERSION_HEADER_CONTENT) + string(REGEX REPLACE ".*#[ \t]*define[ \t]*SODIUM_VERSION_STRING[ \t]*\"([^\n]*)\".*" "\\1" + sodium_VERSION "${_VERSION_HEADER_CONTENT}") + set(sodium_VERSION "${sodium_VERSION}" PARENT_SCOPE) + endif() +endif() + +# communicate results +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + Sodium # The name must be either uppercase or match the filename case. + REQUIRED_VARS + sodium_LIBRARY_RELEASE + sodium_LIBRARY_DEBUG + sodium_INCLUDE_DIR + VERSION_VAR + sodium_VERSION +) + +if(Sodium_FOUND) + set(sodium_LIBRARIES + optimized ${sodium_LIBRARY_RELEASE} debug ${sodium_LIBRARY_DEBUG}) +endif() + +# mark file paths as advanced +mark_as_advanced(sodium_INCLUDE_DIR) +mark_as_advanced(sodium_LIBRARY_DEBUG) +mark_as_advanced(sodium_LIBRARY_RELEASE) +if (WIN32) + mark_as_advanced(sodium_DLL_DEBUG) + mark_as_advanced(sodium_DLL_RELEASE) +endif() + +# create imported target +if(sodium_USE_STATIC_LIBS) + set(_LIB_TYPE STATIC) +else() + set(_LIB_TYPE SHARED) +endif() + +if(NOT TARGET sodium) + add_library(sodium ${_LIB_TYPE} IMPORTED) +endif() + +set_target_properties(sodium PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${sodium_INCLUDE_DIR}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" +) + +if (sodium_USE_STATIC_LIBS) + set_target_properties(sodium PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "SODIUM_STATIC" + IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}" + IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}" + ) +else() + if (UNIX) + set_target_properties(sodium PROPERTIES + IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}" + IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}" + ) + elseif (WIN32) + set_target_properties(sodium PROPERTIES + IMPORTED_IMPLIB "${sodium_LIBRARY_RELEASE}" + IMPORTED_IMPLIB_DEBUG "${sodium_LIBRARY_DEBUG}" + ) + if (NOT (sodium_DLL_DEBUG MATCHES ".*-NOTFOUND")) + set_target_properties(sodium PROPERTIES + IMPORTED_LOCATION_DEBUG "${sodium_DLL_DEBUG}" + ) + endif() + if (NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND")) + set_target_properties(sodium PROPERTIES + IMPORTED_LOCATION_RELWITHDEBINFO "${sodium_DLL_RELEASE}" + IMPORTED_LOCATION_MINSIZEREL "${sodium_DLL_RELEASE}" + IMPORTED_LOCATION_RELEASE "${sodium_DLL_RELEASE}" + ) + endif() + endif() +endif() diff --git a/examples/plugins/operator/examplePluginOperator_read.cpp b/examples/plugins/operator/examplePluginOperator_read.cpp new file mode 100644 index 0000000000..d9e42c52cf --- /dev/null +++ b/examples/plugins/operator/examplePluginOperator_read.cpp @@ -0,0 +1,106 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + * + * examplePluginOperator_read.cpp example showing how to use EncryptionOperator + * plugin + * + * Created on: July 5, 2021 + * Author: Caitlin Ross + */ + +#include //std::ios_base::failure +#include //std::cout +#include //std::invalid_argument std::exception +#include + +#include "adios2.h" + +int main(int argc, char *argv[]) +{ + std::string config; + if (argc > 1) + { + config = std::string(argv[1]); + } + + /** Application variable */ + std::vector myDoubles = { + 0.0001, 1.0001, 2.0001, 3.0001, 4.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 1.0001, 2.0001, 3.0001, 4.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 2.0001, 3.0001, 4.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 3.0001, 4.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 4.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 4.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 4.0001, 3.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 4.0001, 3.0001, 2.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 4.0001, 3.0001, 2.0001, 1.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 4.0001, 3.0001, 2.0001, 1.0001, + 0.0001, + }; + + try + { + /** ADIOS class factory of IO class objects */ + adios2::ADIOS adios(config); + + /*** IO class object: settings and factory of Settings: Variables, + * Parameters, Transports, and Execution: Engines */ + adios2::IO io = adios.DeclareIO("reader"); + + /** global array: name, { shape (total dimensions) }, { start (local) }, + * { count (local) }, all are constant dimensions */ + adios2::Engine reader = io.Open("testOperator.bp", adios2::Mode::Read); + auto var = io.InquireVariable("data"); + if (!var) + { + std::cout << "variable does not exist" << std::endl; + } + + if (config.empty()) + { + io.SetEngine("BP4"); + /* PluginName -> is required. If your operator needs + * other parameters, they can be passed in here as well. */ + adios2::Params params; + params["PluginName"] = "MyOperator"; + params["PluginLibrary"] = "EncryptionOperator"; + params["SecretKeyFile"] = "test-key"; + var.AddOperation("plugin", params); + } + + std::vector readDoubles; + reader.Get(var, readDoubles); + reader.PerformGets(); + + if (readDoubles == myDoubles) + { + std::cout << "data was read correctly!" << std::endl; + } + else + { + std::cout << "data was not read correctly!" << std::endl; + } + + /** Engine becomes unreachable after this*/ + reader.Close(); + } + catch (std::invalid_argument &e) + { + std::cout << "Invalid argument exception, STOPPING PROGRAM\n"; + std::cout << e.what() << "\n"; + } + catch (std::ios_base::failure &e) + { + std::cout << "IO System base failure exception, STOPPING PROGRAM\n"; + std::cout << e.what() << "\n"; + } + catch (std::exception &e) + { + std::cout << "Exception, STOPPING PROGRAM from rank\n"; + std::cout << e.what() << "\n"; + } + + return 0; +} diff --git a/examples/plugins/operator/examplePluginOperator_write.cpp b/examples/plugins/operator/examplePluginOperator_write.cpp new file mode 100644 index 0000000000..a49c9d2241 --- /dev/null +++ b/examples/plugins/operator/examplePluginOperator_write.cpp @@ -0,0 +1,95 @@ +/* + * Distributed under the OSI-approved Apache License, Version 2.0. See + * accompanying file Copyright.txt for details. + * + * examplePluginOperator_write.cpp example showing how to use EncryptionOperator + * plugin + * + * Created on: July 5, 2021 + * Author: Caitlin Ross + */ + +#include //std::ios_base::failure +#include //std::cout +#include //std::invalid_argument std::exception +#include + +#include "adios2.h" + +int main(int argc, char *argv[]) +{ + std::string config; + if (argc > 1) + { + config = std::string(argv[1]); + } + + /** Application variable */ + std::vector myDoubles = { + 0.0001, 1.0001, 2.0001, 3.0001, 4.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 1.0001, 2.0001, 3.0001, 4.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 2.0001, 3.0001, 4.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 3.0001, 4.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 4.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 5.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 4.0001, 6.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 4.0001, 3.0001, 7.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 4.0001, 3.0001, 2.0001, 8.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 4.0001, 3.0001, 2.0001, 1.0001, + 9.0001, 8.0001, 7.0001, 6.0001, 5.0001, 4.0001, 3.0001, 2.0001, 1.0001, + 0.0001, + }; + const std::size_t Nx = myDoubles.size(); + + try + { + /** ADIOS class factory of IO class objects */ + adios2::ADIOS adios(config); + + /*** IO class object: settings and factory of Settings: Variables, + * Parameters, Transports, and Execution: Engines */ + adios2::IO io = adios.DeclareIO("writer"); + + /** global array: name, { shape (total dimensions) }, { start (local) }, + * { count (local) }, all are constant dimensions */ + adios2::Variable var = io.DefineVariable( + "data", {}, {}, {Nx}, adios2::ConstantDims); + + if (config.empty()) + { + io.SetEngine("BP4"); + /* PluginName -> is required. If your operator needs + * other parameters, they can be passed in here as well. */ + adios2::Params params; + params["PluginName"] = "MyOperator"; + params["PluginLibrary"] = "EncryptionOperator"; + params["SecretKeyFile"] = "test-key"; + var.AddOperation("plugin", params); + } + + adios2::Engine writer = io.Open("testOperator.bp", adios2::Mode::Write); + + writer.Put(var, myDoubles.data()); + writer.PerformPuts(); + + /** Engine becomes unreachable after this*/ + writer.Close(); + } + catch (std::invalid_argument &e) + { + std::cout << "Invalid argument exception, STOPPING PROGRAM\n"; + std::cout << e.what() << "\n"; + } + catch (std::ios_base::failure &e) + { + std::cout << "IO System base failure exception, STOPPING PROGRAM\n"; + std::cout << e.what() << "\n"; + } + catch (std::exception &e) + { + std::cout << "Exception, STOPPING PROGRAM from rank\n"; + std::cout << e.what() << "\n"; + } + + return 0; +} diff --git a/examples/plugins/operator/example_operator.xml b/examples/plugins/operator/example_operator.xml new file mode 100644 index 0000000000..7b8af9c575 --- /dev/null +++ b/examples/plugins/operator/example_operator.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 384effebf5502d055bc5d9741afd452af4a5f95a Mon Sep 17 00:00:00 2001 From: Caitlin Ross Date: Wed, 12 Jan 2022 14:40:07 -0500 Subject: [PATCH 04/10] update PluginEngine to use PluginManager --- source/adios2/core/IO.cpp | 4 +- source/adios2/engine/plugin/PluginEngine.cpp | 103 ++++++------------ source/adios2/engine/plugin/PluginEngine.h | 37 ++----- .../engine/plugin/PluginEngineInterface.cpp | 10 +- .../engine/plugin/PluginEngineInterface.h | 13 +-- 5 files changed, 56 insertions(+), 111 deletions(-) diff --git a/source/adios2/core/IO.cpp b/source/adios2/core/IO.cpp index 70cba588cf..d4d832149e 100644 --- a/source/adios2/core/IO.cpp +++ b/source/adios2/core/IO.cpp @@ -131,8 +131,8 @@ std::unordered_map Factory = { {IO::NoEngine("ERROR: nullcore engine does not support read mode"), IO::MakeEngine}}, {"plugin", - {IO::MakeEngine, - IO::MakeEngine}}, + {IO::MakeEngine, + IO::MakeEngine}}, }; // Synchronize access to the factory in case one thread is diff --git a/source/adios2/engine/plugin/PluginEngine.cpp b/source/adios2/engine/plugin/PluginEngine.cpp index 8ec268372a..3fa0f1b86b 100644 --- a/source/adios2/engine/plugin/PluginEngine.cpp +++ b/source/adios2/engine/plugin/PluginEngine.cpp @@ -17,37 +17,52 @@ #include #include -#include "adios2/helper/adiosDynamicBinder.h" #include "adios2/helper/adiosLog.h" - -#include +#include "adios2/helper/adiosPluginManager.h" namespace adios2 { -namespace core -{ -namespace engine +namespace plugin { /******************************************************************************/ struct PluginEngine::Impl { - std::string m_PluginName = "UserPlugin"; - std::unique_ptr m_Binder; - EngineCreateFun m_HandleCreate; - EngineDestroyFun m_HandleDestroy; + PluginManager::EngineCreateFun m_HandleCreate; + PluginManager::EngineDestroyFun m_HandleDestroy; PluginEngineInterface *m_Plugin = nullptr; }; /******************************************************************************/ -PluginEngine::PluginEngine(IO &io, const std::string &name, const Mode mode, - helper::Comm comm) +PluginEngine::PluginEngine(core::IO &io, const std::string &name, + const Mode mode, helper::Comm comm) : Engine("Plugin", io, name, mode, comm.Duplicate()), m_Impl(new Impl) { - Init(); - m_Impl->m_Plugin = m_Impl->m_HandleCreate(io, m_Impl->m_PluginName, mode, + auto pluginNameIt = m_IO.m_Parameters.find("PluginName"); + if (pluginNameIt == m_IO.m_Parameters.end()) + { + helper::Throw( + "Plugins", "PluginEngine", "PluginEngine", + "PluginName must be specified in the engine parameters"); + } + + auto pluginLibIt = m_IO.m_Parameters.find("PluginLibrary"); + if (pluginLibIt == m_IO.m_Parameters.end()) + { + helper::Throw( + "Plugins", "PluginEngine", "PluginEngine", + "PluginLibrary must be specified in the engine parameters"); + } + + auto &pluginManager = PluginManager::GetInstance(); + pluginManager.LoadPlugin(pluginNameIt->second, pluginLibIt->second); + m_Impl->m_HandleCreate = + pluginManager.GetEngineCreateFun(pluginNameIt->second); + m_Impl->m_HandleDestroy = + pluginManager.GetEngineDestroyFun(pluginNameIt->second); + m_Impl->m_Plugin = m_Impl->m_HandleCreate(io, pluginNameIt->second, mode, comm.Duplicate()); } @@ -64,68 +79,21 @@ void PluginEngine::PerformGets() { m_Impl->m_Plugin->PerformGets(); } void PluginEngine::EndStep() { m_Impl->m_Plugin->EndStep(); } -void PluginEngine::Init() -{ - auto paramPluginNameIt = m_IO.m_Parameters.find("PluginName"); - if (paramPluginNameIt != m_IO.m_Parameters.end()) - { - m_Impl->m_PluginName = paramPluginNameIt->second; - } - - std::string pluginPath; - adios2sys::SystemTools::GetEnv("ADIOS2_PLUGIN_PATH", pluginPath); - - auto paramPluginLibraryIt = m_IO.m_Parameters.find("PluginLibrary"); - if (paramPluginLibraryIt == m_IO.m_Parameters.end()) - { - helper::Throw( - "Engine", "PluginEngine", "Init", - "PluginLibrary must be specified in " - "engine parameters if no PluginName " - "is specified"); - } - std::string &pluginLibrary = paramPluginLibraryIt->second; - - m_Impl->m_Binder.reset( - new helper::DynamicBinder(pluginLibrary, pluginPath)); - - m_Impl->m_HandleCreate = reinterpret_cast( - m_Impl->m_Binder->GetSymbol("EngineCreate")); - if (!m_Impl->m_HandleCreate) - { - helper::Throw( - "Engine", "PluginEngine", "Init", - "Unable to locate " - "EngineCreate symbol in specified plugin " - "library"); - } - - m_Impl->m_HandleDestroy = reinterpret_cast( - m_Impl->m_Binder->GetSymbol("EngineDestroy")); - if (!m_Impl->m_HandleDestroy) - { - helper::Throw( - "Engine", "PluginEngine", "Init", - "Unable to locate " - "EngineDestroy symbol in specified plugin " - "library"); - } -} - #define declare(T) \ - void PluginEngine::DoPutSync(Variable &variable, const T *values) \ + void PluginEngine::DoPutSync(core::Variable &variable, const T *values) \ { \ m_Impl->m_Plugin->DoPutSync(variable, values); \ } \ - void PluginEngine::DoPutDeferred(Variable &variable, const T *values) \ + void PluginEngine::DoPutDeferred(core::Variable &variable, \ + const T *values) \ { \ m_Impl->m_Plugin->DoPutDeferred(variable, values); \ } \ - void PluginEngine::DoGetSync(Variable &variable, T *values) \ + void PluginEngine::DoGetSync(core::Variable &variable, T *values) \ { \ m_Impl->m_Plugin->DoGetSync(variable, values); \ } \ - void PluginEngine::DoGetDeferred(Variable &variable, T *values) \ + void PluginEngine::DoGetDeferred(core::Variable &variable, T *values) \ { \ m_Impl->m_Plugin->DoGetDeferred(variable, values); \ } @@ -138,6 +106,5 @@ void PluginEngine::DoClose(const int transportIndex) m_Impl->m_Plugin->Close(transportIndex); } -} // end namespace engine -} // end namespace core +} // end namespace plugin } // end namespace adios2 diff --git a/source/adios2/engine/plugin/PluginEngine.h b/source/adios2/engine/plugin/PluginEngine.h index 74c74ce370..f3217cc3c4 100644 --- a/source/adios2/engine/plugin/PluginEngine.h +++ b/source/adios2/engine/plugin/PluginEngine.h @@ -14,10 +14,8 @@ #include "PluginEngineInterface.h" -#include // for function -#include // for unique_ptr -#include // for string -#include // for add_pointer +#include // for unique_ptr +#include // for string #include "adios2/common/ADIOSMacros.h" #include "adios2/common/ADIOSTypes.h" @@ -28,26 +26,14 @@ namespace adios2 { -namespace core -{ -namespace engine +namespace plugin { /** A front-end wrapper for an engine implemented outside of libadios2 */ -class PluginEngine : public Engine +class PluginEngine : public core::Engine { public: - // Function pointers used for the plugin factory methods - using EngineCreatePtr = std::add_pointer::type; - using EngineDestroyPtr = - std::add_pointer::type; - using EngineCreateFun = - std::function::type>; - using EngineDestroyFun = - std::function::type>; - - PluginEngine(IO &io, const std::string &name, const Mode mode, + PluginEngine(core::IO &io, const std::string &name, const Mode mode, helper::Comm comm); virtual ~PluginEngine(); @@ -58,13 +44,11 @@ class PluginEngine : public Engine void EndStep() override; protected: - void Init() override; - #define declare(T) \ - void DoPutSync(Variable &, const T *) override; \ - void DoPutDeferred(Variable &, const T *) override; \ - void DoGetSync(Variable &, T *) override; \ - void DoGetDeferred(Variable &, T *) override; + void DoPutSync(core::Variable &, const T *) override; \ + void DoPutDeferred(core::Variable &, const T *) override; \ + void DoGetSync(core::Variable &, T *) override; \ + void DoGetDeferred(core::Variable &, T *) override; ADIOS2_FOREACH_STDTYPE_1ARG(declare) #undef declare @@ -76,8 +60,7 @@ class PluginEngine : public Engine std::unique_ptr m_Impl; }; -} // end namespace engine -} // end namespace core +} // end namespace plugin } // end namespace adios2 #endif /* ADIOS2_ENGINE_PLUGIN_PLUGINENGINE_H_ */ diff --git a/source/adios2/engine/plugin/PluginEngineInterface.cpp b/source/adios2/engine/plugin/PluginEngineInterface.cpp index 18b84b7586..e0ebd3d28f 100644 --- a/source/adios2/engine/plugin/PluginEngineInterface.cpp +++ b/source/adios2/engine/plugin/PluginEngineInterface.cpp @@ -13,17 +13,15 @@ namespace adios2 { -namespace core -{ -namespace engine +namespace plugin { -PluginEngineInterface::PluginEngineInterface(IO &io, const std::string &name, +PluginEngineInterface::PluginEngineInterface(core::IO &io, + const std::string &name, const Mode mode, helper::Comm comm) : Engine("PluginInterface", io, name, mode, std::move(comm)) { } -} // end namespace engine -} // end namespace core +} // end namespace plugin } // end namespace adios2 diff --git a/source/adios2/engine/plugin/PluginEngineInterface.h b/source/adios2/engine/plugin/PluginEngineInterface.h index 9a8054d304..b5688391cc 100644 --- a/source/adios2/engine/plugin/PluginEngineInterface.h +++ b/source/adios2/engine/plugin/PluginEngineInterface.h @@ -24,25 +24,22 @@ namespace adios2 { -namespace core -{ -namespace engine +namespace plugin { /** An engine interface to be used by the plugin infrastructure */ -class PluginEngineInterface : public Engine +class PluginEngineInterface : public core::Engine { // Give the plugin engine access to everything friend class PluginEngine; public: - PluginEngineInterface(IO &io, const std::string &name, const Mode mode, - helper::Comm comm); + PluginEngineInterface(core::IO &io, const std::string &name, + const Mode mode, helper::Comm comm); virtual ~PluginEngineInterface() = default; }; -} // end namespace engine -} // end namespace core +} // end namespace plugin } // end namespace adios2 #endif /* ADIOS2_ENGINE_PLUGIN_PLUGINENGINEINTERFACE_H_ */ From c88b86e7537afba7d48220a4782a621367b1c550 Mon Sep 17 00:00:00 2001 From: Caitlin Ross Date: Wed, 12 Jan 2022 14:41:04 -0500 Subject: [PATCH 05/10] update plugin engine example --- examples/plugins/engine/CMakeLists.txt | 8 ++-- examples/plugins/engine/ExampleReadPlugin.cpp | 28 ++++++------ examples/plugins/engine/ExampleReadPlugin.h | 26 +++++------ examples/plugins/engine/ExampleReadPlugin.tcc | 45 ++++++++++--------- .../plugins/engine/ExampleWritePlugin.cpp | 31 ++++++------- examples/plugins/engine/ExampleWritePlugin.h | 26 +++++------ .../plugins/engine/ExampleWritePlugin.tcc | 10 ++--- .../engine/examplePluginEngine_read.cpp | 26 +++++++---- .../engine/examplePluginEngine_write.cpp | 26 +++++++---- examples/plugins/engine/example_engine.xml | 28 ++++++++++++ 10 files changed, 146 insertions(+), 108 deletions(-) create mode 100644 examples/plugins/engine/example_engine.xml diff --git a/examples/plugins/engine/CMakeLists.txt b/examples/plugins/engine/CMakeLists.txt index e49ccb6d55..4d175b377a 100644 --- a/examples/plugins/engine/CMakeLists.txt +++ b/examples/plugins/engine/CMakeLists.txt @@ -3,15 +3,15 @@ # accompanying file Copyright.txt for details. #------------------------------------------------------------------------------# -add_library(PluginEngineWriteExample +add_library(PluginEngineWrite ExampleWritePlugin.cpp ) -target_link_libraries(PluginEngineWriteExample adios2::cxx11 adios2_core) +target_link_libraries(PluginEngineWrite adios2::cxx11 adios2_core) -add_library(PluginEngineReadExample +add_library(PluginEngineRead ExampleReadPlugin.cpp ) -target_link_libraries(PluginEngineReadExample adios2::cxx11 adios2_core) +target_link_libraries(PluginEngineRead adios2::cxx11 adios2_core) add_executable(examplePluginEngine_write examplePluginEngine_write.cpp diff --git a/examples/plugins/engine/ExampleReadPlugin.cpp b/examples/plugins/engine/ExampleReadPlugin.cpp index e0a1d8b2a6..8b36148d91 100644 --- a/examples/plugins/engine/ExampleReadPlugin.cpp +++ b/examples/plugins/engine/ExampleReadPlugin.cpp @@ -15,12 +15,10 @@ namespace adios2 { -namespace core -{ -namespace engine +namespace plugin { -ExampleReadPlugin::ExampleReadPlugin(IO &io, const std::string &name, +ExampleReadPlugin::ExampleReadPlugin(core::IO &io, const std::string &name, const Mode mode, helper::Comm comm) : PluginEngineInterface(io, name, mode, comm.Duplicate()) { @@ -105,11 +103,12 @@ void ExampleReadPlugin::Init() } #define declare(T) \ - void ExampleReadPlugin::DoGetSync(Variable &variable, T *values) \ + void ExampleReadPlugin::DoGetSync(core::Variable &variable, T *values) \ { \ ReadVariable(variable, values); \ } \ - void ExampleReadPlugin::DoGetDeferred(Variable &variable, T *values) \ + void ExampleReadPlugin::DoGetDeferred(core::Variable &variable, \ + T *values) \ { \ ReadVariable(variable, values); \ } @@ -130,20 +129,19 @@ void ExampleReadPlugin::EndStep() { m_CurrentStep++; } void ExampleReadPlugin::DoClose(const int transportIndex) {} -} // end namespace engine -} // end namespace core +} // end namespace plugin } // end namespace adios2 extern "C" { -adios2::core::engine::ExampleReadPlugin *EngineCreate(adios2::core::IO &io, - const std::string &name, - const adios2::Mode mode, - adios2::helper::Comm comm) +adios2::plugin::ExampleReadPlugin *EngineCreate(adios2::core::IO &io, + const std::string &name, + const adios2::Mode mode, + adios2::helper::Comm comm) { - return new adios2::core::engine::ExampleReadPlugin(io, name, mode, - comm.Duplicate()); + return new adios2::plugin::ExampleReadPlugin(io, name, mode, + comm.Duplicate()); } -void EngineDestroy(adios2::core::engine::ExampleReadPlugin *obj) { delete obj; } +void EngineDestroy(adios2::plugin::ExampleReadPlugin *obj) { delete obj; } } diff --git a/examples/plugins/engine/ExampleReadPlugin.h b/examples/plugins/engine/ExampleReadPlugin.h index 620fc1486c..3a56d0c5c1 100644 --- a/examples/plugins/engine/ExampleReadPlugin.h +++ b/examples/plugins/engine/ExampleReadPlugin.h @@ -27,17 +27,15 @@ namespace adios2 { -namespace core -{ -namespace engine +namespace plugin { /** An engine interface to be used by the plugin infrastructure */ class ExampleReadPlugin : public PluginEngineInterface { public: - ExampleReadPlugin(IO &io, const std::string &name, const Mode openMode, - helper::Comm comm); + ExampleReadPlugin(core::IO &io, const std::string &name, + const Mode openMode, helper::Comm comm); virtual ~ExampleReadPlugin(); /** Indicates beginning of a step **/ @@ -57,8 +55,8 @@ class ExampleReadPlugin : public PluginEngineInterface void Init() override; #define declare(T) \ - void DoGetSync(Variable &variable, T *values) override; \ - void DoGetDeferred(Variable &variable, T *values) override; + void DoGetSync(core::Variable &variable, T *values) override; \ + void DoGetDeferred(core::Variable &variable, T *values) override; ADIOS2_FOREACH_STDTYPE_1ARG(declare) #undef declare @@ -74,19 +72,19 @@ class ExampleReadPlugin : public PluginEngineInterface Dims count); template - void ReadVariable(Variable &variable, T *values); + void ReadVariable(core::Variable &variable, T *values); }; -} // end namespace engine -} // end namespace core +} // end namespace plugin } // end namespace adios2 extern "C" { -adios2::core::engine::ExampleReadPlugin * -EngineCreate(adios2::core::IO &io, const std::string &name, - const adios2::Mode mode, adios2::helper::Comm comm); -void EngineDestroy(adios2::core::engine::ExampleReadPlugin *obj); +adios2::plugin::ExampleReadPlugin *EngineCreate(adios2::core::IO &io, + const std::string &name, + const adios2::Mode mode, + adios2::helper::Comm comm); +void EngineDestroy(adios2::plugin::ExampleReadPlugin *obj); } #endif /* EXAMPLEREADPLUGIN_H_ */ diff --git a/examples/plugins/engine/ExampleReadPlugin.tcc b/examples/plugins/engine/ExampleReadPlugin.tcc index 3a103a29b5..eef62f09d5 100644 --- a/examples/plugins/engine/ExampleReadPlugin.tcc +++ b/examples/plugins/engine/ExampleReadPlugin.tcc @@ -15,9 +15,7 @@ namespace adios2 { -namespace core -{ -namespace engine +namespace plugin { template @@ -32,7 +30,8 @@ void ExampleReadPlugin::AddVariable(const std::string &name, Dims shape, } template -inline void ExampleReadPlugin::ReadVariable(Variable &variable, T *values) +inline void ExampleReadPlugin::ReadVariable(core::Variable &variable, + T *values) { while (m_DataFile.good()) { @@ -66,8 +65,9 @@ inline void ExampleReadPlugin::ReadVariable(Variable &variable, T *values) } template <> -inline void ExampleReadPlugin::ReadVariable(Variable &variable, - std::string *values) +inline void +ExampleReadPlugin::ReadVariable(core::Variable &variable, + std::string *values) { while (m_DataFile.good()) { @@ -82,7 +82,7 @@ inline void ExampleReadPlugin::ReadVariable(Variable &variable, } template <> -inline void ExampleReadPlugin::ReadVariable(Variable &variable, +inline void ExampleReadPlugin::ReadVariable(core::Variable &variable, char *values) { while (m_DataFile.good()) @@ -101,8 +101,9 @@ inline void ExampleReadPlugin::ReadVariable(Variable &variable, } template <> -inline void ExampleReadPlugin::ReadVariable(Variable &variable, - unsigned char *values) +inline void +ExampleReadPlugin::ReadVariable(core::Variable &variable, + unsigned char *values) { while (m_DataFile.good()) { @@ -122,8 +123,9 @@ inline void ExampleReadPlugin::ReadVariable(Variable &variable, } template <> -inline void ExampleReadPlugin::ReadVariable(Variable &variable, - signed char *values) +inline void +ExampleReadPlugin::ReadVariable(core::Variable &variable, + signed char *values) { while (m_DataFile.good()) { @@ -143,7 +145,7 @@ inline void ExampleReadPlugin::ReadVariable(Variable &variable, } template <> -inline void ExampleReadPlugin::ReadVariable(Variable &variable, +inline void ExampleReadPlugin::ReadVariable(core::Variable &variable, short *values) { while (m_DataFile.good()) @@ -164,8 +166,9 @@ inline void ExampleReadPlugin::ReadVariable(Variable &variable, } template <> -inline void ExampleReadPlugin::ReadVariable(Variable &variable, - unsigned short *values) +inline void +ExampleReadPlugin::ReadVariable(core::Variable &variable, + unsigned short *values) { while (m_DataFile.good()) { @@ -185,8 +188,9 @@ inline void ExampleReadPlugin::ReadVariable(Variable &variable, } template <> -inline void ExampleReadPlugin::ReadVariable(Variable &variable, - long double *values) +inline void +ExampleReadPlugin::ReadVariable(core::Variable &variable, + long double *values) { while (m_DataFile.good()) { @@ -215,7 +219,7 @@ inline void ExampleReadPlugin::ReadVariable(Variable &variable, template <> inline void -ExampleReadPlugin::ReadVariable(Variable> &variable, +ExampleReadPlugin::ReadVariable(core::Variable> &variable, std::complex *values) { throw std::invalid_argument( @@ -224,13 +228,12 @@ ExampleReadPlugin::ReadVariable(Variable> &variable, template <> inline void -ExampleReadPlugin::ReadVariable(Variable> &variable, +ExampleReadPlugin::ReadVariable(core::Variable> &variable, std::complex *values) { throw std::invalid_argument( "ERROR: std::complex not supported in this engine"); } -} -} -} +} // end namespace plugin +} // end namespace adios2 #endif /* EXAMPLEREADPLUGIN_TCC_ */ diff --git a/examples/plugins/engine/ExampleWritePlugin.cpp b/examples/plugins/engine/ExampleWritePlugin.cpp index cc18042e7e..9a969b3f3a 100644 --- a/examples/plugins/engine/ExampleWritePlugin.cpp +++ b/examples/plugins/engine/ExampleWritePlugin.cpp @@ -16,13 +16,10 @@ namespace adios2 { -namespace core +namespace plugin { -namespace engine -{ - -ExampleWritePlugin::ExampleWritePlugin(IO &io, const std::string &name, +ExampleWritePlugin::ExampleWritePlugin(core::IO &io, const std::string &name, const Mode mode, helper::Comm comm) : PluginEngineInterface(io, name, mode, comm.Duplicate()) { @@ -74,11 +71,12 @@ size_t ExampleWritePlugin::CurrentStep() const { return m_CurrentStep; } void ExampleWritePlugin::EndStep() { m_CurrentStep++; } #define declare(T) \ - void ExampleWritePlugin::DoPutSync(Variable &variable, const T *values) \ + void ExampleWritePlugin::DoPutSync(core::Variable &variable, \ + const T *values) \ { \ WriteArray(variable, values); \ } \ - void ExampleWritePlugin::DoPutDeferred(Variable &variable, \ + void ExampleWritePlugin::DoPutDeferred(core::Variable &variable, \ const T *values) \ { \ WriteArray(variable, values); \ @@ -110,22 +108,19 @@ void ExampleWritePlugin::WriteVarsFromIO() } } -} // end namespace engine -} // end namespace core +} // end namespace plugin } // end namespace adios2 extern "C" { -adios2::core::engine::ExampleWritePlugin * -EngineCreate(adios2::core::IO &io, const std::string &name, - const adios2::Mode mode, adios2::helper::Comm comm) +adios2::plugin::ExampleWritePlugin *EngineCreate(adios2::core::IO &io, + const std::string &name, + const adios2::Mode mode, + adios2::helper::Comm comm) { - return new adios2::core::engine::ExampleWritePlugin(io, name, mode, - comm.Duplicate()); + return new adios2::plugin::ExampleWritePlugin(io, name, mode, + comm.Duplicate()); } -void EngineDestroy(adios2::core::engine::ExampleWritePlugin *obj) -{ - delete obj; -} +void EngineDestroy(adios2::plugin::ExampleWritePlugin *obj) { delete obj; } } diff --git a/examples/plugins/engine/ExampleWritePlugin.h b/examples/plugins/engine/ExampleWritePlugin.h index e13eecb184..6b98cc17da 100644 --- a/examples/plugins/engine/ExampleWritePlugin.h +++ b/examples/plugins/engine/ExampleWritePlugin.h @@ -27,17 +27,15 @@ namespace adios2 { -namespace core -{ -namespace engine +namespace plugin { /** An engine interface to be used by the plugin infrastructure */ class ExampleWritePlugin : public PluginEngineInterface { public: - ExampleWritePlugin(IO &io, const std::string &name, const Mode openMode, - helper::Comm comm); + ExampleWritePlugin(core::IO &io, const std::string &name, + const Mode openMode, helper::Comm comm); virtual ~ExampleWritePlugin(); /** Indicates beginning of a step **/ @@ -57,8 +55,8 @@ class ExampleWritePlugin : public PluginEngineInterface void Init() override; #define declare(T) \ - void DoPutSync(Variable &variable, const T *values) override; \ - void DoPutDeferred(Variable &variable, const T *values) override; + void DoPutSync(core::Variable &variable, const T *values) override; \ + void DoPutDeferred(core::Variable &variable, const T *values) override; ADIOS2_FOREACH_STDTYPE_1ARG(declare) #undef declare @@ -75,19 +73,19 @@ class ExampleWritePlugin : public PluginEngineInterface void WriteVariableInfo(core::Variable &variable); template - void WriteArray(Variable &variable, const T *values); + void WriteArray(core::Variable &variable, const T *values); }; -} // end namespace engine -} // end namespace core +} // end namespace plugin } // end namespace adios2 extern "C" { -adios2::core::engine::ExampleWritePlugin * -EngineCreate(adios2::core::IO &io, const std::string &name, - const adios2::Mode mode, adios2::helper::Comm comm); -void EngineDestroy(adios2::core::engine::ExampleWritePlugin *obj); +adios2::plugin::ExampleWritePlugin *EngineCreate(adios2::core::IO &io, + const std::string &name, + const adios2::Mode mode, + adios2::helper::Comm comm); +void EngineDestroy(adios2::plugin::ExampleWritePlugin *obj); } #endif /* EXAMPLEWRITEPLUGIN_H_ */ diff --git a/examples/plugins/engine/ExampleWritePlugin.tcc b/examples/plugins/engine/ExampleWritePlugin.tcc index 3848631a33..4a6fdc737f 100644 --- a/examples/plugins/engine/ExampleWritePlugin.tcc +++ b/examples/plugins/engine/ExampleWritePlugin.tcc @@ -16,9 +16,7 @@ namespace adios2 { -namespace core -{ -namespace engine +namespace plugin { template @@ -31,7 +29,8 @@ void ExampleWritePlugin::WriteVariableInfo(core::Variable &variable) } template -void ExampleWritePlugin::WriteArray(Variable &variable, const T *values) +void ExampleWritePlugin::WriteArray(core::Variable &variable, + const T *values) { /** Write variable name and step to file, followed by the actual data on the * next line **/ @@ -50,7 +49,6 @@ void ExampleWritePlugin::WriteArray(Variable &variable, const T *values) } } -} // end namespace engine -} // end namespace core +} // end namespace plugin } // end namespace adios2 #endif /* EXAMPLEWRITEPLUGIN_TCC_ */ diff --git a/examples/plugins/engine/examplePluginEngine_read.cpp b/examples/plugins/engine/examplePluginEngine_read.cpp index 68e980c798..bfab5a9dd1 100644 --- a/examples/plugins/engine/examplePluginEngine_read.cpp +++ b/examples/plugins/engine/examplePluginEngine_read.cpp @@ -47,10 +47,16 @@ void testStreaming(adios2::Engine &reader, std::vector &myFloats, int main(int argc, char *argv[]) { - bool streaming = false; + std::string config; if (argc > 1) { - streaming = std::atoi(argv[1]) == 1; + config = std::string(argv[1]); + } + + bool streaming = false; + if (argc > 2) + { + streaming = std::atoi(argv[2]) == 1; } std::vector myFloats = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; @@ -58,16 +64,20 @@ int main(int argc, char *argv[]) try { /** ADIOS class factory of IO class objects */ - adios2::ADIOS adios; + adios2::ADIOS adios(config); /*** IO class object: settings and factory of Settings: Variables, * Parameters, Transports, and Execution: Engines */ - adios2::IO io = adios.DeclareIO("PluginIO"); + adios2::IO io = adios.DeclareIO("reader"); - /** Engine derived class, spawned to start IO operations */ - io.SetEngine("Plugin"); - io.SetParameters({{"PluginName", "ReadPlugin"}}); - io.SetParameters({{"PluginLibrary", "PluginEngineRead"}}); + if (config.empty()) + { + io.SetEngine("Plugin"); + adios2::Params params; + params["PluginName"] = "ReadPlugin"; + params["PluginLibrary"] = "PluginEngineRead"; + io.SetParameters(params); + } adios2::Engine reader = io.Open("TestPlugin", adios2::Mode::Read); auto var = io.InquireVariable("data"); diff --git a/examples/plugins/engine/examplePluginEngine_write.cpp b/examples/plugins/engine/examplePluginEngine_write.cpp index 9110995cd3..94041efb24 100644 --- a/examples/plugins/engine/examplePluginEngine_write.cpp +++ b/examples/plugins/engine/examplePluginEngine_write.cpp @@ -36,10 +36,16 @@ void testStreaming(adios2::Engine &writer, std::vector &myFloats, int main(int argc, char *argv[]) { - bool streaming = false; + std::string config; if (argc > 1) { - streaming = std::atoi(argv[1]) == 1; + config = std::string(argv[1]); + } + + bool streaming = false; + if (argc > 2) + { + streaming = std::atoi(argv[2]) == 1; } /** Application variable */ @@ -49,21 +55,25 @@ int main(int argc, char *argv[]) try { /** ADIOS class factory of IO class objects */ - adios2::ADIOS adios; + adios2::ADIOS adios(config); /*** IO class object: settings and factory of Settings: Variables, * Parameters, Transports, and Execution: Engines */ - adios2::IO io = adios.DeclareIO("PluginIO"); + adios2::IO io = adios.DeclareIO("writer"); /** global array: name, { shape (total dimensions) }, { start (local) }, * { count (local) }, all are constant dimensions */ adios2::Variable var = io.DefineVariable( "data", {}, {}, {Nx}, adios2::ConstantDims); - /** Engine derived class, spawned to start IO operations */ - io.SetEngine("Plugin"); - io.SetParameters({{"PluginName", "WritePlugin"}}); - io.SetParameters({{"PluginLibrary", "PluginEngineWrite"}}); + if (config.empty()) + { + io.SetEngine("Plugin"); + adios2::Params params; + params["PluginName"] = "WritePlugin"; + params["PluginLibrary"] = "PluginEngineWrite"; + io.SetParameters(params); + } adios2::Engine writer = io.Open("TestPlugin", adios2::Mode::Write); if (streaming) diff --git a/examples/plugins/engine/example_engine.xml b/examples/plugins/engine/example_engine.xml new file mode 100644 index 0000000000..412c1a9684 --- /dev/null +++ b/examples/plugins/engine/example_engine.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + From 898c47278389d97810a9cf0f78a1f6eb9fc0b04e Mon Sep 17 00:00:00 2001 From: Caitlin Ross Date: Tue, 18 Jan 2022 16:37:53 -0500 Subject: [PATCH 06/10] add operator plugin install test --- source/adios2/CMakeLists.txt | 6 ++++ testing/install/plugin/CMakeLists.txt | 44 ++++++++++++++++++++++----- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/source/adios2/CMakeLists.txt b/source/adios2/CMakeLists.txt index 9999cd5cf5..7a88e97850 100644 --- a/source/adios2/CMakeLists.txt +++ b/source/adios2/CMakeLists.txt @@ -391,6 +391,12 @@ install(DIRECTORY helper/ PATTERN "*.inl" ) +install(DIRECTORY operator/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/adios2/operator COMPONENT adios2_core-development + FILES_MATCHING PATTERN "*/*.h" + PATTERN "*/*.inl" +) + install(DIRECTORY toolkit/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/adios2/toolkit COMPONENT adios2_core-development FILES_MATCHING PATTERN "*/*.h" diff --git a/testing/install/plugin/CMakeLists.txt b/testing/install/plugin/CMakeLists.txt index 2757112dba..6a0615cb2a 100644 --- a/testing/install/plugin/CMakeLists.txt +++ b/testing/install/plugin/CMakeLists.txt @@ -11,6 +11,8 @@ find_package(adios2 REQUIRED) option(BUILD_SHARED_LIBS "build shared libs" ON) +#---------- Engine Plugin Tests + add_library(PluginEngineWrite ../../../examples/plugins/engine/ExampleWritePlugin.cpp ) @@ -22,17 +24,43 @@ add_library(PluginEngineRead target_link_libraries(PluginEngineRead adios2::cxx11 adios2::core) # add write test -add_executable(adios_plugin_write_test +add_executable(adios_plugin_engine_write_test ../../../examples/plugins/engine/examplePluginEngine_write.cpp ) -target_link_libraries(adios_plugin_write_test adios2::cxx11) -add_test(NAME adios_plugin_write_test COMMAND adios_plugin_write_test) +target_link_libraries(adios_plugin_engine_write_test adios2::cxx11) +add_test(NAME adios_plugin_engine_write_test COMMAND adios_plugin_engine_write_test) # add read test -add_executable(adios_plugin_read_test +add_executable(adios_plugin_engine_read_test ../../../examples/plugins/engine/examplePluginEngine_read.cpp ) -target_link_libraries(adios_plugin_read_test adios2::cxx11) -add_test(NAME adios_plugin_read_test COMMAND adios_plugin_read_test) -set_tests_properties(adios_plugin_read_test PROPERTIES - DEPENDS adios_plugin_write_test) +target_link_libraries(adios_plugin_engine_read_test adios2::cxx11) +add_test(NAME adios_plugin_engine_read_test COMMAND adios_plugin_engine_read_test) +set_tests_properties(adios_plugin_engine_read_test PROPERTIES + DEPENDS adios_plugin_engine_write_test) + +#---------- Operator Plugin Tests + +list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/../../../examples/plugins/operator) +find_package(Sodium 1.8 REQUIRED) + +add_library(EncryptionOperator + ../../../examples/plugins/operator/EncryptionOperator.cpp +) +target_link_libraries(EncryptionOperator adios2::cxx11 adios2::core sodium) + +# add write test +add_executable(adios_plugin_operator_write_test + ../../../examples/plugins/operator/examplePluginOperator_write.cpp +) +target_link_libraries(adios_plugin_operator_write_test adios2::cxx11) +add_test(NAME adios_plugin_operator_write_test COMMAND adios_plugin_operator_write_test) + +# add read test +add_executable(adios_plugin_operator_read_test + ../../../examples/plugins/operator/examplePluginOperator_read.cpp +) +target_link_libraries(adios_plugin_operator_read_test adios2::cxx11) +add_test(NAME adios_plugin_operator_read_test COMMAND adios_plugin_operator_read_test) +set_tests_properties(adios_plugin_operator_read_test PROPERTIES + DEPENDS adios_plugin_operator_write_test) From b7e6966f3ca3258d6d49632ed92062aacbf078f7 Mon Sep 17 00:00:00 2001 From: Caitlin Ross Date: Mon, 31 Jan 2022 10:33:33 -0500 Subject: [PATCH 07/10] add documentation for creating/using plugins --- docs/user_guide/source/advanced/plugins.rst | 223 +++++++++++++++++++- docs/user_guide/source/engines/engines.rst | 2 - docs/user_guide/source/engines/plugin.rst | 101 --------- 3 files changed, 222 insertions(+), 104 deletions(-) delete mode 100644 docs/user_guide/source/engines/plugin.rst diff --git a/docs/user_guide/source/advanced/plugins.rst b/docs/user_guide/source/advanced/plugins.rst index bebad04a7e..10584ffd10 100644 --- a/docs/user_guide/source/advanced/plugins.rst +++ b/docs/user_guide/source/advanced/plugins.rst @@ -2,4 +2,225 @@ Plugins ######### -TODO +ADIOS now has the ability for users to load their own engines and operators through the plugin interface. +The basic steps for doing this are: + +1. Write your plugin class, which needs to inherit from the appropriate ``Plugin*Interface`` class. +2. Build as a shared library and add the path to your shared library to the ``ADIOS2_PLUGIN_PATH`` environment variable. +3. Start using your plugin in your application. + +These steps are discussed in further detail below. + +************************* +Writing Your Plugin Class +************************* + + +Engine Plugin +------------- + +Your engine plugin class needs to inherit from the ``PluginEngineInterface`` class in the ``adios2/engine/plugin/PluginEngineInterface.h`` header. +Depending on the type of engine you want to implement, you'll need to override a number of methods that are inherited from the ``adios2::core::Engine`` class. +These are briefly described in the following table. +More detailed documentation can be found in ``adios2/core/Engine.h``. + +========================= ===================== =========================================================== + **Method** **Engine Type** **Description** +========================= ===================== =========================================================== +``BeginStep()`` Read/Write Indicates the beginning of a step +``EndStep()`` Read/Write Indicates the end of a step +``CurrentStep()`` Read/Write Returns current step info +``DoClose()`` Read/Write Close a particular transport +``Init()`` Read/Write Engine initialization +``InitParameters()`` Read/Write Initialize parameters +``InitTransports()`` Read/Write Initialize transports +``PerformPuts()`` Write Execute all deferred mode ``Put`` +``Flush()`` Write Flushes data and metadata to a transport +``DoPut()`` Write Implementation for ``Put`` +``DoPutSync()`` Write Implementation for ``Put`` (Sync mode) +``DoPutDeferred()`` Write Implementation for ``Put`` (Deferred Mode) +``PerformGets()`` Read Execute all deferred mode ``Get`` +``DoGetSync()`` Read Implementation for ``Get`` (Sync mode) +``DoGetDeferred()`` Read Implementation for ``Get`` (Deferred Mode) +========================= ===================== =========================================================== + +Examples showing how to implement an engine plugin can be found in ``examples/plugins/engine``. +An example write engine is ``ExampleWritePlugin.h``, while an example read engine is in ``ExampleReadPlugin.h``. +The writer is a simple file writing engine that creates a directory (called ``ExamplePlugin`` by default) and writes variable information to vars.txt and actual data to data.txt. +The reader example reads the files output by the writer example. + +In addition to implementing the methods above, you'll need to implement ``EngineCreate()`` and ``EngineDestroy()`` functions so ADIOS can create/destroy the engine object. +Because of C++ name mangling, you'll need to use ``extern "C"``. +Looking at ``ExampleWritePlugin.h``, this looks like: + +.. code-block:: c++ + + extern "C" { + + adios2::plugin::ExampleWritePlugin * + EngineCreate(adios2::core::IO &io, const std::string &name, + const adios2::Mode mode, adios2::helper::Comm comm) + { + return new adios2::plugin::ExampleWritePlugin(io, name, mode, + comm.Duplicate()); + } + + void EngineDestroy(adios2::plugin::ExampleWritePlugin * obj) + { + delete obj; + } + + } + +Operator Plugin +--------------- + +Your operator plugin class needs to inherit from the ``PluginOperatorInterface`` class in the ``adios2/operator/plugin/PluginOperatorInterface.h`` header. +There's three methods that you'll need to override from the ``adios2::core::Operator`` class, which are described below. + +========================= =========================================================== + **Method** **Description** +========================= =========================================================== +``Operate()`` Performs the operation, e.g., compress data +``InverseOperate()`` Performs the inverse operation, e.g., decompress data +``IsDataTypeValid()`` Checks that a given data type can be processed +========================= =========================================================== + +An example showing how to implement an operator plugin can be found at ``plugins/EncryptionOperator.h`` and ``plugins/EncryptionOperator.cpp``. +This operator uses `libsodium `_ for encrypting and decrypting data. + +In addition to implementing the methods above, you'll need to implement ``OperatorCreate()`` and ``OperatorDestroy()`` functions so ADIOS can create/destroy the operator object. +Because of C++ name mangling, you'll need to use ``extern "C"``. +Looking at ``EncryptionOperator``, this looks like: + +.. code-block:: c++ + + extern "C" { + + adios2::plugin::EncryptionOperator * + OperatorCreate(const adios2::Params ¶meters) + { + return new adios2::plugin::EncryptionOperator(parameters); + } + + void OperatorDestroy(adios2::plugin::EncryptionOperator * obj) + { + delete obj; + } + + } + +******************** +Build Shared Library +******************** + +To build your plugin, your CMake should look something like the following (using the plugin engine example described above): + +.. code-block:: cmake + + find_package(ADIOS2 REQUIRED) + set(BUILD_SHARED_LIBS ON) + add_library(PluginEngineWrite + ExampleWritePlugin.cpp + ) + target_link_libraries(PluginEngineWrite adios2::cxx11 adios2::core) + +When using the Plugin Engine, ADIOS will check for your plugin at the path specified in the ``ADIOS2_PLUGIN_PATH`` environment variable. +If ``ADIOS2_PLUGIN_PATH`` is not set, and a path is not specified when loading your plugin (see below steps for using a plugin in your application), then the usual ``dlopen`` search is performed (see the `dlopen man page `_). + +.. note:: + The ``ADIOS2_PLUGIN_PATH`` environment variable can contain multiple paths, which must be separated with a ``:``. + +*********************************** +Using Your Plugin in an Application +*********************************** + +For both types of plugins, loading the plugin is done by setting the ``PluginName`` and ``PluginLibrary`` parameters in an ``adios2::Params`` object or ```` XML tag. + +Engine Plugins +-------------- + +For engine plugins, this looks like: + +.. code-block:: c++ + + adios2::ADIOS adios; + adios2::IO io = adios.DeclareIO("writer"); + io.SetEngine("Plugin"); + adios2::Params params; + params["PluginName"] = "WritePlugin"; + params["PluginLibrary"] = "PluginEngineWrite"; + // If the engine plugin has any other parameters, these can be added to + // the same params object and they will be forwarded to the engine + io.SetParameters(params); + +Where "WritePlugin" is the name that ADIOS will use to keep track of the plugin, and "PluginEngineWrite" is the shared library name. +At this point you can open the engine and use it as you would any other ADIOS engine. +You also shouldn't need to make any changes to your CMake files for your application. + +The second option is using an ADIOS XML config file. If you'd like to load your plugins through an XML config file, the following shows an example XML when using Engine Plugins: + +.. code-block:: xml + + + + + + + + + + + + + + + + + + +The examples ``examples/plugins/engine/examplePluginEngine_write.cpp`` and ``examples/plugins/engine/examplePluginEngine_read.cpp`` are an example of how to use the engine plugins described above. + + +Operator Plugins +---------------- + +For operator plugins, the code to use your plugin looks like: + +.. code-block:: c++ + + // for an adios2::Variable var + adios2::Params params; + params["PluginName"] = "MyOperator"; + params["PluginLibrary"] = "EncryptionOperator"; + // example param required for the EncryptionOperator + params["SecretKeyFile"] = "test-key"; + var.AddOperation("plugin", params); + +If you'd like to load your operator plugin through an XML config file, the following shows an example: + +.. code-block:: xml + + + + + + + + + + + + + + + + +The examples ``examples/plugins/operator/examplePluginOperator_write.cpp`` and ``examples/plugins/engine/examplePluginOperator_read.cpp`` show an example of how to use the ``EncryptionOperator`` plugin described above. + +.. note:: + You don't need to add the ``lib`` prefix or the shared library ending (e.g., ``.so``, ``.dll``, etc.) when + setting ``PluginLibrary``. + ADIOS will add these when searching for your plugin library. + If you do add the prefix/suffix, ADIOS will still be able to find your plugin. + It's also possible to put the full path to the shared library here, instead of using ``ADIOS2_PLUGIN_PATH``. diff --git a/docs/user_guide/source/engines/engines.rst b/docs/user_guide/source/engines/engines.rst index 68457400ee..8265e3a18f 100644 --- a/docs/user_guide/source/engines/engines.rst +++ b/docs/user_guide/source/engines/engines.rst @@ -25,5 +25,3 @@ Parameters are passed at: .. include:: dataman.rst .. include:: inline.rst .. include:: null.rst -.. include:: plugin.rst - diff --git a/docs/user_guide/source/engines/plugin.rst b/docs/user_guide/source/engines/plugin.rst deleted file mode 100644 index 5f9f38f388..0000000000 --- a/docs/user_guide/source/engines/plugin.rst +++ /dev/null @@ -1,101 +0,0 @@ -************* -Plugin Engine -************* - -The ``Plugin`` engine enables the ability to load an engine located in a separate library. -Your plugin class needs to inherit from the ``PluginEngineInterface`` class in the ``adios2/engine/plugin/PluginEngineInterface.h`` header. -Depending on the type of engine you want to implement, you'll need to override a number of methods that are inherited from the ``adios2::core::Engine`` class. -These are briefly described in the following table. -More detailed documentation can be found in ``adios2/core/Engine.h``. - -========================= ===================== =========================================================== - **Method** **Engine Type** **Description** -========================= ===================== =========================================================== -``BeginStep()`` Read/Write Indicates the beginning of a step -``EndStep()`` Read/Write Indicates the end of a step -``CurrentStep()`` Read/Write Returns current step info -``DoClose()`` Read/Write Close a particular transport -``Init()`` Read/Write Engine initialization -``InitParameters()`` Read/Write Initialize parameters -``InitTransports()`` Read/Write Initialize transports -``PerformPuts()`` Write Execute all deferred mode ``Put`` -``Flush()`` Write Flushes data and metadata to a transport -``DoPut()`` Write Implementation for ``Put`` -``DoPutSync()`` Write Implementation for ``Put`` (Sync mode) -``DoPutDeferred()`` Write Implementation for ``Put`` (Deferred Mode) -``PerformGets()`` Read Execute all deferred mode ``Get`` -``DoGetSync()`` Read Implementation for ``Get`` (Sync mode) -``DoGetDeferred()`` Read Implementation for ``Get`` (Deferred Mode) -========================= ===================== =========================================================== - -Examples showing how to implement an engine plugin can be found in ``examples/plugins/engine``. -An example write engine is ``ExampleWritePlugin.h``, while an example read engine is in ``ExampleReadPlugin.h``. -The writer is a simple file writing engine that creates a directory (called ``ExamplePlugin`` by default) and writes variable information to vars.txt and actual data to data.txt. -The reader example reads the files output by the writer example. - -In addition to implementing the methods above, you'll need to implement ``EngineCreate()`` and ``EngineDestroy`` functions so the Plugin Engine can create/destroy the engine object. -Because of C++ name mangling, you'll need to use ``extern "C"``. -Looking at ``ExampleWritePlugin.h``, this looks like: - -.. code-block:: c++ - - extern "C" { - - adios2::core::engine::ExampleWritePlugin * - EngineCreate(adios2::core::IO &io, const std::string &name, - const adios2::Mode mode, adios2::helper::Comm comm) - { - return new adios2::core::engine::ExampleWritePlugin(io, name, mode, - comm.Duplicate()); - } - - void EngineDestroy(adios2::core::engine::ExampleWritePlugin * obj) - { - delete obj; - } - - } - -To build your plugin, your CMake should look something like: - -.. code-block:: cmake - - find_package(ADIOS2 REQUIRED) - set(BUILD_SHARED_LIBS ON) - add_library(PluginEngineWrite - ExampleWritePlugin.cpp - ) - target_link_libraries(PluginEngineWrite adios2::cxx11 adios2::core) - -When using the Plugin Engine, ADIOS will check for your plugin at the path specified in the ``ADIOS2_PLUGIN_PATH`` environment variable. -If ``ADIOS2_PLUGIN_PATH`` is not set, and a path is not specified in the settings for the Plugin Engine (see below steps for using a plugin in your application), then the usual ``dlopen`` search is performed (see `dlopen man page `_). - -The following steps show how to use your engine plugin in your application. -``examplePluginEngine_write.cpp`` and ``examplePluginEngine_read.cpp`` are an example of how to use the engine plugins described above. -The key steps to use your plugin are: - -1. Set engine to ``Plugin``. i.e.: - -.. code-block:: c++ - - io.SetEngine("Plugin"); - -2. Set ``PluginName`` (optional) and ``PluginLibrary`` (required) parameters. - If you don't set ``PluginName``, the Plugin Engine will give your plugin a default name of ``UserPlugin``. - In the write example, this looks like - -.. code-block:: c++ - - io.SetParameters({{"PluginName", "WritePlugin"}}); - io.SetParameters({{"PluginLibrary", "PluginEngineWrite"}}); - // also possible to use the path here instead of setting ADIOS2_PLUGIN_PATH - // io.SetParameters({{"PluginLibrary", "/path/to/libPluginEngineWrite.so"}}) - -.. note:: - You don't need to add the ``lib`` prefix or the shared library ending (e.g., ``.so``, ``.dll``, etc.). - ADIOS will add these when searching for your plugin library. - If you do at the prefix/suffix, ADIOS should still be able to find your plugin. - - -At this point you can open the engine and use it as you would any other ADIOS engine. -You also shouldn't need to make any changes to your CMake files for your application. From 03c71d365e53e753cba69486d051937052d507d0 Mon Sep 17 00:00:00 2001 From: Caitlin Ross Date: Mon, 31 Jan 2022 15:28:01 -0500 Subject: [PATCH 08/10] enable operator plugins to pass user parameters when reading --- .../adios2/toolkit/format/bp/bp3/BP3Deserializer.tcc | 12 +++++++++++- .../adios2/toolkit/format/bp/bp4/BP4Deserializer.tcc | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/source/adios2/toolkit/format/bp/bp3/BP3Deserializer.tcc b/source/adios2/toolkit/format/bp/bp3/BP3Deserializer.tcc index 82a601472a..ba7f36eb39 100644 --- a/source/adios2/toolkit/format/bp/bp3/BP3Deserializer.tcc +++ b/source/adios2/toolkit/format/bp/bp3/BP3Deserializer.tcc @@ -528,7 +528,17 @@ void BP3Deserializer::PostDataRead( char *preOpData = m_ThreadBuffers[threadID][0].data(); const char *postOpData = m_ThreadBuffers[threadID][1].data(); - core::Decompress(postOpData, blockOperationInfo.PayloadSize, preOpData); + std::shared_ptr op = nullptr; + for (auto &o : blockInfo.Operations) + { + if (o->m_Category == "compress" || o->m_Category == "plugin") + { + op = o; + break; + } + } + core::Decompress(postOpData, blockOperationInfo.PayloadSize, preOpData, + op); // clip block to match selection helper::ClipVector(m_ThreadBuffers[threadID][0], diff --git a/source/adios2/toolkit/format/bp/bp4/BP4Deserializer.tcc b/source/adios2/toolkit/format/bp/bp4/BP4Deserializer.tcc index 7e5ce9c9a5..b09ee68eaf 100644 --- a/source/adios2/toolkit/format/bp/bp4/BP4Deserializer.tcc +++ b/source/adios2/toolkit/format/bp/bp4/BP4Deserializer.tcc @@ -535,7 +535,7 @@ void BP4Deserializer::PostDataRead( std::shared_ptr op = nullptr; for (auto &o : blockInfo.Operations) { - if (o->m_Category == "compress") + if (o->m_Category == "compress" || o->m_Category == "plugin") { op = o; break; From 3d75381d7233bdfed2258f395d137668b6298956 Mon Sep 17 00:00:00 2001 From: Caitlin Ross Date: Tue, 22 Feb 2022 16:15:25 -0500 Subject: [PATCH 09/10] move EncryptionOperator to plugins dir --- CMakeLists.txt | 8 ++++- cmake/DetectOptions.cmake | 10 ++++++ .../operator => cmake}/FindSodium.cmake | 0 cmake/adios2-config-common.cmake.in | 5 +++ examples/plugins/operator/CMakeLists.txt | 9 ------ plugins/CMakeLists.txt | 16 ++++++++++ .../EncryptionOperator.cpp | 0 .../operator => plugins}/EncryptionOperator.h | 0 testing/install/CMakeLists.txt | 5 ++- .../install/EncryptionOperator/CMakeLists.txt | 32 +++++++++++++++++++ .../{plugin => EnginePlugin}/CMakeLists.txt | 25 --------------- 11 files changed, 74 insertions(+), 36 deletions(-) rename {examples/plugins/operator => cmake}/FindSodium.cmake (100%) create mode 100644 plugins/CMakeLists.txt rename {examples/plugins/operator => plugins}/EncryptionOperator.cpp (100%) rename {examples/plugins/operator => plugins}/EncryptionOperator.h (100%) create mode 100644 testing/install/EncryptionOperator/CMakeLists.txt rename testing/install/{plugin => EnginePlugin}/CMakeLists.txt (58%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b4802916e..b7f0f44a1d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,6 +161,7 @@ adios_option(Fortran "Enable support for Fortran bindings" AUTO) adios_option(SysVShMem "Enable support for SysV Shared Memory IPC on *NIX" AUTO) adios_option(Profiling "Enable support for profiling" AUTO) adios_option(Endian_Reverse "Enable support for Little/Big Endian Interoperability" AUTO) +adios_option(Sodium "Enable support for Sodium for encryption" AUTO) include(${PROJECT_SOURCE_DIR}/cmake/DetectOptions.cmake) if(ADIOS2_HAVE_CUDA) @@ -221,7 +222,7 @@ endif() set(ADIOS2_CONFIG_OPTS - BP5 DataMan DataSpaces HDF5 HDF5_VOL MHS SST CUDA Fortran MPI Python Blosc BZip2 LIBPRESSIO MGARD PNG SZ ZFP DAOS IME O_DIRECT SysVShMem ZeroMQ Profiling Endian_Reverse + BP5 DataMan DataSpaces HDF5 HDF5_VOL MHS SST CUDA Fortran MPI Python Blosc BZip2 LIBPRESSIO MGARD PNG SZ ZFP DAOS IME O_DIRECT SysVShMem ZeroMQ Profiling Endian_Reverse Sodium ) GenerateADIOSHeaderConfig(${ADIOS2_CONFIG_OPTS}) @@ -286,6 +287,11 @@ add_subdirectory(source) #------------------------------------------------------------------------------# add_subdirectory(bindings) +#------------------------------------------------------------------------------# +# Plugins +#------------------------------------------------------------------------------# +add_subdirectory(plugins) + #------------------------------------------------------------------------------# # Examples #------------------------------------------------------------------------------# diff --git a/cmake/DetectOptions.cmake b/cmake/DetectOptions.cmake index 18e15695a5..4aae71c5ca 100644 --- a/cmake/DetectOptions.cmake +++ b/cmake/DetectOptions.cmake @@ -403,6 +403,16 @@ if(ADIOS2_USE_Endian_Reverse STREQUAL ON) set(ADIOS2_HAVE_Endian_Reverse TRUE) endif() +# Sodium for EncryptionOperator +if(ADIOS2_USE_Sodium STREQUAL AUTO) + find_package(Sodium) +elseif(ADIOS2_USE_Sodium) + find_package(Sodium REQUIRED) +endif() +if(Sodium_FOUND) + set(ADIOS2_HAVE_Sodium TRUE) +endif() + # Multithreading find_package(Threads REQUIRED) diff --git a/examples/plugins/operator/FindSodium.cmake b/cmake/FindSodium.cmake similarity index 100% rename from examples/plugins/operator/FindSodium.cmake rename to cmake/FindSodium.cmake diff --git a/cmake/adios2-config-common.cmake.in b/cmake/adios2-config-common.cmake.in index 1f5ec2c4bf..aeab8558e1 100644 --- a/cmake/adios2-config-common.cmake.in +++ b/cmake/adios2-config-common.cmake.in @@ -127,6 +127,11 @@ if(NOT @BUILD_SHARED_LIBS@) find_dependency(HDF5 COMPONENTS C) endif() + set(ADIOS2_HAVE_Sodium @ADIOS2_HAVE_Sodium@) + if(ADIOS2_HAVE_Sodium) + find_dependency(Sodium) + endif() + adios2_add_thirdparty_target(pugixml) set(ADIOS2_USE_EXTERNAL_PUGIXML @ADIOS2_USE_EXTERNAL_PUGIXML@) if(ADIOS2_USE_EXTERNAL_PUGIXML) diff --git a/examples/plugins/operator/CMakeLists.txt b/examples/plugins/operator/CMakeLists.txt index b11e256abf..8881bb8f16 100644 --- a/examples/plugins/operator/CMakeLists.txt +++ b/examples/plugins/operator/CMakeLists.txt @@ -2,15 +2,6 @@ # Distributed under the OSI-approved Apache License, Version 2.0. See # accompanying file Copyright.txt for details. #------------------------------------------------------------------------------# - -list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}) -find_package(Sodium 1.8 REQUIRED) - -add_library(EncryptionOperator - EncryptionOperator.cpp -) -target_link_libraries(EncryptionOperator adios2::cxx11 adios2_core sodium) - add_executable(exampleOperatorPlugin_write examplePluginOperator_write.cpp ) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt new file mode 100644 index 0000000000..b115218c0f --- /dev/null +++ b/plugins/CMakeLists.txt @@ -0,0 +1,16 @@ +#------------------------------------------------------------------------------# +# Distributed under the OSI-approved Apache License, Version 2.0. See +# accompanying file Copyright.txt for details. +#------------------------------------------------------------------------------# + +if(ADIOS2_HAVE_Sodium) + add_library(EncryptionOperator + EncryptionOperator.cpp + ) + target_link_libraries(EncryptionOperator adios2_core sodium) + install(TARGETS EncryptionOperator EXPORT adios2Exports + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT adios2_core-runtime + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT adios2_core-libraries NAMELINK_COMPONENT adios2_core-development + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT adios2_core-development +) +endif() diff --git a/examples/plugins/operator/EncryptionOperator.cpp b/plugins/EncryptionOperator.cpp similarity index 100% rename from examples/plugins/operator/EncryptionOperator.cpp rename to plugins/EncryptionOperator.cpp diff --git a/examples/plugins/operator/EncryptionOperator.h b/plugins/EncryptionOperator.h similarity index 100% rename from examples/plugins/operator/EncryptionOperator.h rename to plugins/EncryptionOperator.h diff --git a/testing/install/CMakeLists.txt b/testing/install/CMakeLists.txt index 9f4e5cf599..d94dd3ba88 100644 --- a/testing/install/CMakeLists.txt +++ b/testing/install/CMakeLists.txt @@ -110,4 +110,7 @@ if(NOT WIN32) endif() endif() -add_install_cmake_test(plugin) +add_install_cmake_test(EnginePlugin) +if(ADIOS2_HAVE_Sodium) + add_install_cmake_test(EncryptionOperator) +endif() diff --git a/testing/install/EncryptionOperator/CMakeLists.txt b/testing/install/EncryptionOperator/CMakeLists.txt new file mode 100644 index 0000000000..ed4fae524b --- /dev/null +++ b/testing/install/EncryptionOperator/CMakeLists.txt @@ -0,0 +1,32 @@ +#------------------------------------------------------------------------------# +# Distributed under the OSI-approved Apache License, Version 2.0. See +# accompanying file Copyright.txt for details. +#------------------------------------------------------------------------------# + +cmake_minimum_required(VERSION 3.6) +project(adios_operator_plugin_test CXX) +enable_testing() + +find_package(adios2 REQUIRED) + +option(BUILD_SHARED_LIBS "build shared libs" ON) + +set(ENV{ADIOS2_PLUGIN_PATH} "${adios2_DIR}/../../") + +#---------- Operator Plugin Tests + +# add write test +add_executable(adios_plugin_operator_write_test + ../../../examples/plugins/operator/examplePluginOperator_write.cpp +) +target_link_libraries(adios_plugin_operator_write_test adios2::cxx11) +add_test(NAME adios_plugin_operator_write_test COMMAND adios_plugin_operator_write_test) + +# add read test +add_executable(adios_plugin_operator_read_test + ../../../examples/plugins/operator/examplePluginOperator_read.cpp +) +target_link_libraries(adios_plugin_operator_read_test adios2::cxx11) +add_test(NAME adios_plugin_operator_read_test COMMAND adios_plugin_operator_read_test) +set_tests_properties(adios_plugin_operator_read_test PROPERTIES + DEPENDS adios_plugin_operator_write_test) diff --git a/testing/install/plugin/CMakeLists.txt b/testing/install/EnginePlugin/CMakeLists.txt similarity index 58% rename from testing/install/plugin/CMakeLists.txt rename to testing/install/EnginePlugin/CMakeLists.txt index 6a0615cb2a..9f03aa09a1 100644 --- a/testing/install/plugin/CMakeLists.txt +++ b/testing/install/EnginePlugin/CMakeLists.txt @@ -39,28 +39,3 @@ add_test(NAME adios_plugin_engine_read_test COMMAND adios_plugin_engine_read_tes set_tests_properties(adios_plugin_engine_read_test PROPERTIES DEPENDS adios_plugin_engine_write_test) -#---------- Operator Plugin Tests - -list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/../../../examples/plugins/operator) -find_package(Sodium 1.8 REQUIRED) - -add_library(EncryptionOperator - ../../../examples/plugins/operator/EncryptionOperator.cpp -) -target_link_libraries(EncryptionOperator adios2::cxx11 adios2::core sodium) - -# add write test -add_executable(adios_plugin_operator_write_test - ../../../examples/plugins/operator/examplePluginOperator_write.cpp -) -target_link_libraries(adios_plugin_operator_write_test adios2::cxx11) -add_test(NAME adios_plugin_operator_write_test COMMAND adios_plugin_operator_write_test) - -# add read test -add_executable(adios_plugin_operator_read_test - ../../../examples/plugins/operator/examplePluginOperator_read.cpp -) -target_link_libraries(adios_plugin_operator_read_test adios2::cxx11) -add_test(NAME adios_plugin_operator_read_test COMMAND adios_plugin_operator_read_test) -set_tests_properties(adios_plugin_operator_read_test PROPERTIES - DEPENDS adios_plugin_operator_write_test) From 6e3cbdf39fb5922cce8d29d9b7d33d0c8931be81 Mon Sep 17 00:00:00 2001 From: Caitlin Ross Date: Sat, 26 Feb 2022 16:26:03 -0500 Subject: [PATCH 10/10] disable msan for some functions that make libsodium calls --- plugins/EncryptionOperator.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/EncryptionOperator.cpp b/plugins/EncryptionOperator.cpp index 5af5bbe9cc..fc002202dd 100644 --- a/plugins/EncryptionOperator.cpp +++ b/plugins/EncryptionOperator.cpp @@ -23,7 +23,7 @@ namespace plugin struct EncryptionOperator::EncryptImpl { std::string KeyFilename; - unsigned char Key[crypto_secretbox_KEYBYTES]; + unsigned char Key[crypto_secretbox_KEYBYTES] = {0}; bool KeyValid = false; ~EncryptImpl() @@ -90,6 +90,12 @@ EncryptionOperator::EncryptionOperator(const Params ¶meters) EncryptionOperator::~EncryptionOperator() {} +#if defined(__clang__) +#if __has_feature(memory_sanitizer) +// Memory Sanitizer has an issue with some libsodium calls. +__attribute__((no_sanitize("memory"))) +#endif +#endif size_t EncryptionOperator::Operate(const char *dataIn, const Dims &blockStart, const Dims &blockCount, const DataType type, char *bufferOut) @@ -131,6 +137,12 @@ size_t EncryptionOperator::Operate(const char *dataIn, const Dims &blockStart, return offset; } +#if defined(__clang__) +#if __has_feature(memory_sanitizer) +// Memory Sanitizer has an issue with some libsodium calls. +__attribute__((no_sanitize("memory"))) +#endif +#endif size_t EncryptionOperator::InverseOperate(const char *bufferIn, const size_t sizeIn, char *dataOut) {