From 8097c0c381078d250bbbb6afaa64c0f44c131d31 Mon Sep 17 00:00:00 2001 From: iceseer Date: Fri, 13 Dec 2024 15:42:16 +0300 Subject: [PATCH] [feature] profiler Signed-off-by: iceseer --- core/profiler/common/CMakeLists.txt | 87 +++ core/profiler/common/array_size.hpp | 19 + core/profiler/common/bind.hpp | 87 +++ core/profiler/common/blob.hpp | 87 +++ core/profiler/common/byteutils.hpp | 67 +++ core/profiler/common/cloneable.hpp | 84 +++ .../combine_latest_until_first_completed.hpp | 196 +++++++ .../common/default_constructible_unary_fn.hpp | 68 +++ core/profiler/common/delay.hpp | 188 +++++++ core/profiler/common/files.cpp | 72 +++ core/profiler/common/files.hpp | 46 ++ core/profiler/common/hexutils.hpp | 105 ++++ core/profiler/common/instanceof.hpp | 22 + core/profiler/common/irohad_version.cpp | 23 + core/profiler/common/irohad_version.hpp | 35 ++ core/profiler/common/is_any.hpp | 34 ++ core/profiler/common/macro.hpp | 29 + core/profiler/common/mem_operations.hpp | 26 + core/profiler/common/memory_utils.hpp | 26 + core/profiler/common/murmur2.h | 61 ++ core/profiler/common/obj_utils.hpp | 88 +++ .../common/optional_reference_equal.hpp | 27 + .../profiler/common/permutation_generator.cpp | 78 +++ .../profiler/common/permutation_generator.hpp | 45 ++ core/profiler/common/result.hpp | 532 ++++++++++++++++++ core/profiler/common/result_fwd.hpp | 32 ++ core/profiler/common/ring_buffer.hpp | 176 ++++++ core/profiler/common/run_loop_handler.hpp | 43 ++ core/profiler/common/set.hpp | 73 +++ core/profiler/common/spin_lock.hpp | 79 +++ core/profiler/common/stack.hpp | 77 +++ core/profiler/common/timeout.hpp | 216 +++++++ core/profiler/common/to_string.hpp | 127 +++++ core/profiler/common/visitor.hpp | 99 ++++ core/profiler/profiler/CMakeLists.txt | 11 + .../profiler/function_chain_context.hpp | 65 +++ core/profiler/profiler/function_context.hpp | 79 +++ .../profiler/profiler/function_stack_info.hpp | 96 ++++ core/profiler/profiler/profiler.cpp | 78 +++ core/profiler/profiler/profiler.hpp | 177 ++++++ core/profiler/profiler/profiler_impl.hpp | 119 ++++ core/profiler/profiler/viewer/report.hpp | 48 ++ .../viewer/report_iterator_reader.hpp | 116 ++++ .../profiler/viewer/report_viewer_impl.hpp | 454 +++++++++++++++ 44 files changed, 4297 insertions(+) create mode 100644 core/profiler/common/CMakeLists.txt create mode 100644 core/profiler/common/array_size.hpp create mode 100644 core/profiler/common/bind.hpp create mode 100644 core/profiler/common/blob.hpp create mode 100644 core/profiler/common/byteutils.hpp create mode 100644 core/profiler/common/cloneable.hpp create mode 100644 core/profiler/common/combine_latest_until_first_completed.hpp create mode 100644 core/profiler/common/default_constructible_unary_fn.hpp create mode 100644 core/profiler/common/delay.hpp create mode 100644 core/profiler/common/files.cpp create mode 100644 core/profiler/common/files.hpp create mode 100644 core/profiler/common/hexutils.hpp create mode 100644 core/profiler/common/instanceof.hpp create mode 100644 core/profiler/common/irohad_version.cpp create mode 100644 core/profiler/common/irohad_version.hpp create mode 100644 core/profiler/common/is_any.hpp create mode 100644 core/profiler/common/macro.hpp create mode 100644 core/profiler/common/mem_operations.hpp create mode 100644 core/profiler/common/memory_utils.hpp create mode 100755 core/profiler/common/murmur2.h create mode 100644 core/profiler/common/obj_utils.hpp create mode 100644 core/profiler/common/optional_reference_equal.hpp create mode 100644 core/profiler/common/permutation_generator.cpp create mode 100644 core/profiler/common/permutation_generator.hpp create mode 100644 core/profiler/common/result.hpp create mode 100644 core/profiler/common/result_fwd.hpp create mode 100644 core/profiler/common/ring_buffer.hpp create mode 100644 core/profiler/common/run_loop_handler.hpp create mode 100644 core/profiler/common/set.hpp create mode 100644 core/profiler/common/spin_lock.hpp create mode 100644 core/profiler/common/stack.hpp create mode 100644 core/profiler/common/timeout.hpp create mode 100644 core/profiler/common/to_string.hpp create mode 100644 core/profiler/common/visitor.hpp create mode 100644 core/profiler/profiler/CMakeLists.txt create mode 100644 core/profiler/profiler/function_chain_context.hpp create mode 100644 core/profiler/profiler/function_context.hpp create mode 100644 core/profiler/profiler/function_stack_info.hpp create mode 100644 core/profiler/profiler/profiler.cpp create mode 100644 core/profiler/profiler/profiler.hpp create mode 100644 core/profiler/profiler/profiler_impl.hpp create mode 100644 core/profiler/profiler/viewer/report.hpp create mode 100644 core/profiler/profiler/viewer/report_iterator_reader.hpp create mode 100644 core/profiler/profiler/viewer/report_viewer_impl.hpp diff --git a/core/profiler/common/CMakeLists.txt b/core/profiler/common/CMakeLists.txt new file mode 100644 index 0000000000..035d9661db --- /dev/null +++ b/core/profiler/common/CMakeLists.txt @@ -0,0 +1,87 @@ +# Copyright Soramitsu Co., Ltd. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +add_library(common INTERFACE + # bind.hpp + # blob.hpp + # byteutils.hpp + # cloneable.hpp + # default_constructible_unary_fn.hpp + # instanceof.hpp + # is_any.hpp + # obj_utils.hpp + # result.hpp + # set.hpp + # visitor.hpp + # memory_utils.hpp + # murmur2.hpp + # spin_lock.hpp + ) +target_link_libraries(common INTERFACE + Boost::boost + ) + +add_library(libs_files + files.cpp + ) +target_link_libraries(libs_files + logger + common + Boost::filesystem + ) + +add_library(libs_timeout INTERFACE + # timeout.hpp + ) +target_link_libraries(libs_timeout INTERFACE + common + rxcpp + ) + +add_library(permutation_generator permutation_generator.cpp) + +add_library(irohad_version irohad_version.cpp) + +add_library(libs_to_string INTERFACE + # to_string.hpp + ) +target_link_libraries(libs_to_string INTERFACE + Boost::boost + ) + +# Get the git repo data +set(GIT_REPO_PRETTY_VER "version info unavailable") +if (EXISTS "${PROJECT_SOURCE_DIR}/.git") + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + + if(GIT_EXECUTABLE) + # Get pretty version + execute_process(COMMAND + "${GIT_EXECUTABLE}" + describe --tags --always + WORKING_DIRECTORY + "${PROJECT_SOURCE_DIR}" + RESULT_VARIABLE + res + OUTPUT_VARIABLE + GIT_REPO_PRETTY_VER + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + message(STATUS "Error running `git describe': ${res}") + endif() + message(STATUS "Git repo pretty version: ${GIT_REPO_PRETTY_VER}") + else() + message(STATUS "Git executable not found!") + endif() +endif() + +target_compile_definitions(irohad_version + PRIVATE + -DGIT_REPO_PRETTY_VER="${GIT_REPO_PRETTY_VER}" + -DIROHA_MAJOR_VERSION=${PROJECT_VERSION_MAJOR} + -DIROHA_MINOR_VERSION=${PROJECT_VERSION_MINOR} + -DIROHA_PATCH_VERSION=${PROJECT_VERSION_PATCH} + ) diff --git a/core/profiler/common/array_size.hpp b/core/profiler/common/array_size.hpp new file mode 100644 index 0000000000..ae897847a3 --- /dev/null +++ b/core/profiler/common/array_size.hpp @@ -0,0 +1,19 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_COMMON_ARRAY_SIZE_HPP +#define IROHA_COMMON_ARRAY_SIZE_HPP + +#ifdef IROHA_ARRAY_SIZE +#error IROHA_ARRAY_SIZE already defined. +#endif // IROHA_ARRAY_SIZE + +#ifndef IROHA_ARRAY_SIZE +template +char (&IrohaArraySizeHelper(T (&array)[N]))[N]; +#define IROHA_ARRAY_SIZE(array) (sizeof(IrohaArraySizeHelper(array))) +#endif // IROHA_ARRAY_SIZE + +#endif // IROHA_COMMON_ARRAY_SIZE_HPP diff --git a/core/profiler/common/bind.hpp b/core/profiler/common/bind.hpp new file mode 100644 index 0000000000..1310839b10 --- /dev/null +++ b/core/profiler/common/bind.hpp @@ -0,0 +1,87 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_COMMON_BIND_HPP +#define IROHA_COMMON_BIND_HPP + +#include +#include +#include + +namespace iroha { + + /** + * Bind operator. If argument has value, dereferences argument and calls + * given function, which should return wrapped value + * operator| is used since it has to be binary and left-associative + * Non-void returning specialization + * + * boost::optional f(); + * boost::optional g(int); + * + * boost::optional d = f() + * | g; + * + * std::forward should be used in any reference of arguments because + * operator bool, operator*, and operator() of arguments can have + * different implementation with ref-qualifiers + * + * Trailing return type checks that result of applying function to + * unwrapped value results in non-void type + * + * @tparam T - monadic type + * @tparam Transform - transform function type + * @param t - monadic value + * @param f - function, which takes dereferenced value, and returns + * wrapped value + * @return monadic value, which can be of another type + */ + template + auto operator|(T &&t, Transform &&f) -> std::enable_if_t< + not std::is_same< + decltype(std::forward(f)(*std::forward(t))), + void>::value, + decltype(std::forward(f)(*std::forward(t)))> { + if (std::forward(t)) { + return std::forward(f)(*std::forward(t)); + } + return {}; + } + + /** + * Bind operator. If argument has value, dereferences argument and calls + * given function, which should return wrapped value + * operator| is used since it has to be binary and left-associative + * Void specialization + * + * boost::optional f(); + * void g(int); + * + * f() | g; + * + * std::forward should be used in any reference of arguments because + * operator bool, operator*, and operator() of arguments can have + * different implementation with ref-qualifiers + * + * Trailing return type checks that result of applying function to + * unwrapped value results in void type + * + * @tparam T - monadic type + * @tparam Transform - transform function type + * @param t - monadic value + * @param f - function, which takes dereferenced value, and returns + * wrapped value + */ + template + auto operator|(T &&t, Transform &&f) -> std::enable_if_t< + std::is_same(f)(*std::forward(t))), + void>::value> { + if (std::forward(t)) { + std::forward(f)(*std::forward(t)); + } + } +} // namespace iroha + +#endif // IROHA_COMMON_BIND_HPP diff --git a/core/profiler/common/blob.hpp b/core/profiler/common/blob.hpp new file mode 100644 index 0000000000..9a5d3f08b0 --- /dev/null +++ b/core/profiler/common/blob.hpp @@ -0,0 +1,87 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_COMMON_BLOB_HPP +#define IROHA_COMMON_BLOB_HPP + +#include +#include +#include +#include +#include + +#include "common/hexutils.hpp" +#include "common/result.hpp" + +namespace iroha { + using BadFormatException = std::invalid_argument; + using byte_t = uint8_t; + + /** + * Base type which represents blob of fixed size. + * + * std::string is convenient to use but it is not safe. + * We can not specify the fixed length for string. + * + * For std::array it is possible, so we prefer it over std::string. + */ + template + class blob_t : public std::array { + public: + /** + * Initialize blob value + */ + blob_t() { + this->fill(0); + } + + /** + * In compile-time returns size of current blob. + */ + constexpr static size_t size() { + return size_; + } + + /** + * Converts current blob to std::string + */ + std::string to_string() const noexcept { + return std::string{this->begin(), this->end()}; + } + + /** + * Converts current blob to hex string. + */ + std::string to_hexstring() const noexcept { + return bytestringToHexstring(std::string_view{ + reinterpret_cast(this->data()), this->size()}); + } + + static blob_t from_raw(const byte_t data[size_]) { + blob_t b; + std::copy(data, data + size_, b.begin()); + return b; + } + + static expected::Result, std::string> from_string( + std::string_view data) { + if (data.size() != size_) { + return expected::makeError( + std::string{"blob_t: input string has incorrect length. Found: "} + + std::to_string(data.size()) + + +", required: " + std::to_string(size_)); + } + return from_raw(reinterpret_cast(data.data())); + } + + static expected::Result, std::string> from_hexstring( + std::string_view hex) { + return iroha::hexstringToBytestringResult(hex) | + [](auto &&bytes) { return from_string(bytes); }; + } + }; +} // namespace iroha + +#endif // IROHA_COMMON_BLOB_HPP diff --git a/core/profiler/common/byteutils.hpp b/core/profiler/common/byteutils.hpp new file mode 100644 index 0000000000..9eac839fd0 --- /dev/null +++ b/core/profiler/common/byteutils.hpp @@ -0,0 +1,67 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_BYTEUTILS_H +#define IROHA_BYTEUTILS_H + +#include +#include +#include + +#include + +#include "common/bind.hpp" +#include "common/blob.hpp" +#include "common/hexutils.hpp" + +namespace iroha { + /** + * Convert string to blob vector + * @param source - string for conversion + * @return vector + */ + inline std::vector stringToBytes(const std::string &source) { + return std::vector(source.begin(), source.end()); + } + + /** + * blob vector to string + * @param source - vector for conversion + * @return result string + */ + inline std::string bytesToString(const std::vector &source) { + return std::string(source.begin(), source.end()); + } + + /** + * Create blob_t from string of specified size + * @tparam size - size of blob_t, expected size of string + * @param s - string to convert + * @return blob, if conversion was successful, otherwise nullopt + */ + template + boost::optional> stringToBlob(const std::string &string) { + if (size != string.size()) { + return boost::none; + } + blob_t array; + std::copy(string.begin(), string.end(), array.begin()); + return array; + } + + /** + * Convert hexstring to array of given size + * @tparam size - output array size + * @param string - input string for transform + * @return array of given size if size matches, nullopt otherwise + */ + template + boost::optional> hexstringToArray(const std::string &string) { + return hexstringToBytestring(string) | stringToBlob; + } + +} // namespace iroha + +#endif // IROHA_BYTEUTILS_H diff --git a/core/profiler/common/cloneable.hpp b/core/profiler/common/cloneable.hpp new file mode 100644 index 0000000000..e29f772420 --- /dev/null +++ b/core/profiler/common/cloneable.hpp @@ -0,0 +1,84 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_CLONEABLE_HPP +#define IROHA_CLONEABLE_HPP + +#include + +/** + * Functions and interface for creation cloneable classes with + * smart pointers. + * + * Usage: + * struct Base : object::cloneable { + * // ... other methods + * }; + * + * struct Derived : Base { + * // ... other methods + * protected: + * Derived* clone() const override { + * return new Derived(*this); + * } + * }; + * + * Derived derived; + * auto c1 = clone(derived); + */ + +template +class Cloneable; + +/** + * Function to clone from Cloneable. + * @tparam T - derived from Cloneable + * @param object - object to clone + * @return clone of object + */ +template +std::unique_ptr clone(const T &object) { + using base_type = typename T::base_type; + static_assert(std::is_base_of::value, + "T object has to derived from T::base_type"); + auto ptr = static_cast &>(object).clone(); + return std::unique_ptr(static_cast(ptr)); +} + +/** + * Helper function to copy from pointer to Cloneable. + * @tparam T - derived from Cloneable + * @param object - object to clone + * @return clone of object + */ +template +auto clone(T *object) { + return clone(*object); +} + +/** + * Interface for cloneable classes. + * @tparam T + */ +template +class Cloneable { + public: + using base_type = T; + + virtual ~Cloneable() = default; + + protected: + /** + * Polymorphic clone constructor. + * Method guarantees deep-copy. + * @return pointer to cloned object + */ + virtual T *clone() const = 0; + + template + friend std::unique_ptr clone(const X &); +}; + +#endif // IROHA_CLONEABLE_HPP diff --git a/core/profiler/common/combine_latest_until_first_completed.hpp b/core/profiler/common/combine_latest_until_first_completed.hpp new file mode 100644 index 0000000000..2bef2f7849 --- /dev/null +++ b/core/profiler/common/combine_latest_until_first_completed.hpp @@ -0,0 +1,196 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_COMBINE_LATEST_UNTIL_FIRST_COMPLETED_HPP +#define IROHA_COMBINE_LATEST_UNTIL_FIRST_COMPLETED_HPP + +#include + +namespace iroha { + + /** + * This class is mostly the same as rxcpp::operators::combine_latest, + * the only change is that it completes when the first of observables is + * completed instead of all observables + * For each item from all of the observables select a value to emit from the + * new observable that is returned + * @tparam Coordination the type of the scheduler + * @tparam Selector the type of the aggregation function + * @tparam ObservableN types of source observables + */ + template + struct combine_latest_until_first_completed + : public rxcpp::operators::operator_base>> { + typedef combine_latest_until_first_completed + this_type; + + typedef rxcpp::operators::detail:: + combine_latest_traits + traits; + + typedef typename traits::tuple_source_type tuple_source_type; + typedef typename traits::tuple_source_value_type tuple_source_value_type; + + typedef typename traits::selector_type selector_type; + + typedef typename traits::coordination_type coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + + struct values { + values(tuple_source_type o, selector_type s, coordination_type sf) + : source(std::move(o)), + selector(std::move(s)), + coordination(std::move(sf)) {} + tuple_source_type source; + selector_type selector; + coordination_type coordination; + }; + values initial; + + combine_latest_until_first_completed(coordination_type sf, + selector_type s, + tuple_source_type ts) + : initial(std::move(ts), std::move(s), std::move(sf)) {} + + template + void subscribe_one(std::shared_ptr state) const { + typedef typename std::tuple_element::type::value_type + source_value_type; + + rxcpp::composite_subscription innercs; + + // when the out observer is unsubscribed all the + // inner subscriptions are unsubscribed as well + state->out.add(innercs); + + auto source = on_exception( + [&]() { + return state->coordinator.in(std::get(state->source)); + }, + state->out); + if (source.empty()) { + return; + } + + // this subscribe does not share the observer subscription + // so that when it is unsubscribed the observer can be called + // until the inner subscriptions have finished + auto sink = rxcpp::make_subscriber( + state->out, + innercs, + // on_next + [state](source_value_type st) { + auto &value = std::get(state->latest); + + if (value.empty()) { + ++state->valuesSet; + } + + value.reset(st); + + if (state->valuesSet == sizeof...(ObservableN)) { + auto values = rxcpp::util::surely(state->latest); + auto selectedResult = rxcpp::util::apply(values, state->selector); + state->out.on_next(selectedResult); + } + }, + // on_error + [state](std::exception_ptr e) { state->out.on_error(e); }, + // on_completed + [state]() { state->out.on_completed(); }); + auto selectedSink = on_exception( + [&]() { return state->coordinator.out(sink); }, state->out); + if (selectedSink.empty()) { + return; + } + source->subscribe(std::move(selectedSink.get())); + } + + template + void subscribe_all(std::shared_ptr state, + rxcpp::util::values) const { + bool subscribed[] = {(subscribe_one(state), true)...}; + subscribed[0] = (*subscribed); // silence warning + } + + template + void on_subscribe(Subscriber scbr) const { + static_assert(rxcpp::is_subscriber::value, + "subscribe must be passed a subscriber"); + + typedef Subscriber output_type; + + struct combine_latest_until_first_completed_state_type + : public std::enable_shared_from_this< + combine_latest_until_first_completed_state_type>, + public values { + combine_latest_until_first_completed_state_type(values i, + coordinator_type coor, + output_type oarg) + : values(std::move(i)), + valuesSet(0), + coordinator(std::move(coor)), + out(std::move(oarg)) {} + + mutable int valuesSet; + mutable tuple_source_value_type latest; + coordinator_type coordinator; + output_type out; + }; + + auto coordinator = + initial.coordination.create_coordinator(scbr.get_subscription()); + + // take a copy of the values for each subscription + auto state = + std::make_shared( + initial, std::move(coordinator), std::move(scbr)); + + subscribe_all( + state, + typename rxcpp::util::values_from::type()); + } + }; + + template < + class Coordination, + class Selector, + class Observable, + class... ObservableN, + class Enabled = rxcpp::util::enable_if_all_true_type_t< + rxcpp::is_coordination, + rxcpp::operators::detail:: + is_combine_latest_selector, + rxcpp::all_observables>, + class ResolvedSelector = rxcpp::util::decay_t, + class combine_latest = combine_latest_until_first_completed< + Coordination, + ResolvedSelector, + rxcpp::util::decay_t, + rxcpp::util::decay_t...>, + class Value = rxcpp::util::value_type_t, + class Result = rxcpp::observable> + static Result makeCombineLatestUntilFirstCompleted(Observable &&o, + Coordination &&cn, + Selector &&s, + ObservableN &&... on) { + return Result( + combine_latest(std::forward(cn), + std::forward(s), + std::make_tuple(std::forward(o), + std::forward(on)...))); + } + +} // namespace iroha + +#endif // IROHA_COMBINE_LATEST_UNTIL_FIRST_COMPLETED_HPP diff --git a/core/profiler/common/default_constructible_unary_fn.hpp b/core/profiler/common/default_constructible_unary_fn.hpp new file mode 100644 index 0000000000..68ce12c9a9 --- /dev/null +++ b/core/profiler/common/default_constructible_unary_fn.hpp @@ -0,0 +1,68 @@ +// Boost.Range library +// +// Copyright Neil Groves 2014. Use, modification and +// distribution is subject to the Boost Software License, Version +// 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// For more information, see http://www.boost.org/libs/range/ +// +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef BOOST_RANGE_DETAIL_DEFAULT_CONSTRUCTIBLE_UNARY_FN_HPP_INCLUDED +#define BOOST_RANGE_DETAIL_DEFAULT_CONSTRUCTIBLE_UNARY_FN_HPP_INCLUDED + +#include + +#include +#include +#include + +namespace boost { + namespace range_detail { + + template + class default_constructible_unary_fn_wrapper { + public: + typedef R result_type; + + default_constructible_unary_fn_wrapper() {} + default_constructible_unary_fn_wrapper(const F &source) + : m_impl(source) {} + default_constructible_unary_fn_wrapper( + const default_constructible_unary_fn_wrapper &source) + : m_impl(source.m_impl) {} + default_constructible_unary_fn_wrapper &operator=( + const default_constructible_unary_fn_wrapper &source) { + if (source.m_impl) { + // Lambda are not copy/move assignable. + m_impl.emplace(*source.m_impl); + } else { + m_impl.reset(); + } + return *this; + } + template + R operator()(Arg &&arg) const { + BOOST_ASSERT(m_impl); + return (*m_impl)(std::forward(arg)); + } + + private: + boost::optional m_impl; + }; + + template + struct default_constructible_unary_fn_gen { + typedef typename boost::mpl::if_< + boost::has_trivial_default_constructor, + F, + default_constructible_unary_fn_wrapper >::type type; + }; + + } // namespace range_detail +} // namespace boost + +#endif // include guard diff --git a/core/profiler/common/delay.hpp b/core/profiler/common/delay.hpp new file mode 100644 index 0000000000..1a4fd75ebb --- /dev/null +++ b/core/profiler/common/delay.hpp @@ -0,0 +1,188 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_DELAY_HPP +#define IROHA_DELAY_HPP + +#include + +namespace iroha { + + /** + * This class is mostly the same as rxcpp::operators::delay, + * the only change is that it accepts a selector lambda which generates + * a duration based on observable value instead of a fixed duration + * Return an observable that emits each item emitted by the source observable + * after the specified delay + * Delay is generated with selector from the last received value + * @tparam T value type + * @tparam Selector the type of the transforming function + * which returns time interval + * @tparam Coordination the type of the scheduler + */ + template + struct delay { + typedef rxcpp::util::decay_t source_value_type; + typedef rxcpp::util::decay_t coordination_type; + typedef typename coordination_type::coordinator_type coordinator_type; + typedef rxcpp::util::decay_t select_type; + + struct delay_values { + delay_values(select_type s, coordination_type c) + : selector(std::move(s)), coordination(c) {} + + select_type selector; + coordination_type coordination; + }; + delay_values initial; + + delay(select_type s, coordination_type coordination) + : initial(std::move(s), coordination) {} + + template + struct delay_observer { + typedef delay_observer this_type; + typedef rxcpp::util::decay_t value_type; + typedef rxcpp::util::decay_t dest_type; + typedef rxcpp::observer observer_type; + + struct delay_subscriber_values : public delay_values { + delay_subscriber_values(rxcpp::composite_subscription cs, + dest_type d, + delay_values v, + coordinator_type c) + : delay_values(v), + cs(std::move(cs)), + dest(std::move(d)), + coordinator(std::move(c)), + worker(coordinator.get_worker()), + expected(worker.now()) {} + + rxcpp::composite_subscription cs; + dest_type dest; + coordinator_type coordinator; + rxcpp::schedulers::worker worker; + rxcpp::schedulers::scheduler::clock_type::time_point expected; + }; + std::shared_ptr state; + + delay_observer(rxcpp::composite_subscription cs, + dest_type d, + delay_values v, + coordinator_type c) + : state(std::make_shared( + delay_subscriber_values( + std::move(cs), std::move(d), v, std::move(c)))) { + auto localState = state; + + auto disposer = [=](const rxcpp::schedulers::schedulable &) { + localState->cs.unsubscribe(); + localState->dest.unsubscribe(); + localState->worker.unsubscribe(); + }; + auto selectedDisposer = on_exception( + [&]() { return localState->coordinator.act(disposer); }, + localState->dest); + if (selectedDisposer.empty()) { + return; + } + + localState->dest.add( + [=]() { localState->worker.schedule(selectedDisposer.get()); }); + localState->cs.add( + [=]() { localState->worker.schedule(selectedDisposer.get()); }); + } + + template + void on_next(Value &&v) const { + auto localState = state; + + auto selected = on_exception( + [&]() { return localState->selector(std::forward(v)); }, + localState->dest); + if (selected.empty()) { + return; + } + + auto work = [v, localState](const rxcpp::schedulers::schedulable &) { + localState->dest.on_next(v); + }; + auto selectedWork = + on_exception([&]() { return localState->coordinator.act(work); }, + localState->dest); + if (selectedWork.empty()) { + return; + } + localState->worker.schedule(localState->worker.now() + selected.get(), + selectedWork.get()); + } + + void on_error(std::exception_ptr e) const { + auto localState = state; + auto work = [e, localState](const rxcpp::schedulers::schedulable &) { + localState->dest.on_error(e); + }; + auto selectedWork = + on_exception([&]() { return localState->coordinator.act(work); }, + localState->dest); + if (selectedWork.empty()) { + return; + } + localState->worker.schedule(selectedWork.get()); + } + + void on_completed() const { + auto localState = state; + auto work = [localState](const rxcpp::schedulers::schedulable &) { + localState->dest.on_completed(); + }; + auto selectedWork = + on_exception([&]() { return localState->coordinator.act(work); }, + localState->dest); + if (selectedWork.empty()) { + return; + } + localState->worker.schedule(selectedWork.get()); + } + + static rxcpp::subscriber make(dest_type d, + delay_values v) { + auto cs = rxcpp::composite_subscription(); + auto coordinator = v.coordination.create_coordinator(); + + return rxcpp::make_subscriber( + cs, + observer_type(this_type( + cs, std::move(d), std::move(v), std::move(coordinator)))); + } + }; + + template + auto operator()(Subscriber dest) const + -> decltype(delay_observer::make(std::move(dest), + initial)) { + return delay_observer::make(std::move(dest), initial); + } + }; + + template , + class Duration = decltype(std::declval()( + (std::declval>()))), + class Enabled = rxcpp::util::enable_if_all_true_type_t< + rxcpp::is_coordination, + rxcpp::util::is_duration>, + class Delay = + delay>> + static auto makeDelay(Selector &&s, Coordination &&cn) { + return Delay(std::forward(s), std::forward(cn)); + } + +} // namespace iroha + +#endif // IROHA_DELAY_HPP diff --git a/core/profiler/common/files.cpp b/core/profiler/common/files.cpp new file mode 100644 index 0000000000..c16f2388a9 --- /dev/null +++ b/core/profiler/common/files.cpp @@ -0,0 +1,72 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common/files.hpp" + +#include +#include + +#include +#include +#include "common/result.hpp" +#include "logger/logger.hpp" + +namespace { + template + iroha::expected::Result readFile( + const boost::filesystem::path &path, std::ios_base::openmode mode) { + std::ifstream file(path.string(), mode); + if (!file) { + return iroha::expected::makeError( + fmt::format("File '{}' could not be read.", path.string())); + } + + T contents((std::istreambuf_iterator(file)), + std::istreambuf_iterator()); + return iroha::expected::makeValue(std::move(contents)); + } +} // namespace + +void iroha::remove_dir_contents(const boost::filesystem::path &dir, + const logger::LoggerPtr &log) { + boost::system::error_code error_code; + + bool exists = boost::filesystem::exists(dir, error_code); + if (error_code != boost::system::errc::success) { + log->error("{}", error_code.message()); + return; + } + if (not exists) { + log->error("Directory does not exist '{}'", dir.string()); + return; + } + + bool is_dir = boost::filesystem::is_directory(dir, error_code); + if (error_code != boost::system::errc::success) { + log->error("{}", error_code.message()); + return; + } + if (not is_dir) { + log->error("'{}' is not a directory", dir.string()); + return; + } + + for (auto entry : boost::filesystem::directory_iterator(dir)) { + boost::filesystem::remove_all(entry.path(), error_code); + if (error_code != boost::system::errc::success) + log->error("{}", error_code.message()); + } +} + +iroha::expected::Result iroha::readTextFile( + const boost::filesystem::path &path) { + return readFile(path, std::ios_base::in); +} + +iroha::expected::Result, std::string> +iroha::readBinaryFile(const boost::filesystem::path &path) { + return readFile>( + path, std::ios_base::binary | std::ios_base::in); +} diff --git a/core/profiler/common/files.hpp b/core/profiler/common/files.hpp new file mode 100644 index 0000000000..face00de4d --- /dev/null +++ b/core/profiler/common/files.hpp @@ -0,0 +1,46 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_FILES_HPP +#define IROHA_FILES_HPP + +#include +#include + +#include +#include "common/result_fwd.hpp" +#include "logger/logger_fwd.hpp" + +/** + * This source file contains common methods related to files + */ +namespace iroha { + + /** + * Remove all files and directories inside a folder. + * Keeps the target folder. + * @param dir - target folder + * @param log - a log for local messages + */ + void remove_dir_contents(const boost::filesystem::path &dir, + const logger::LoggerPtr &log); + + /** + * Read file in text mode, and either return its contents as a string + * or return the error as a string + * @param path - path to the file + */ + iroha::expected::Result readTextFile( + const boost::filesystem::path &path); + + /** + * Read file in binary mode, and either return its contents as a byte vector + * or return the error as a string + * @param path - path to the file + */ + iroha::expected::Result, std::string> readBinaryFile( + const boost::filesystem::path &path); +} // namespace iroha +#endif // IROHA_FILES_HPP diff --git a/core/profiler/common/hexutils.hpp b/core/profiler/common/hexutils.hpp new file mode 100644 index 0000000000..b575bc8f30 --- /dev/null +++ b/core/profiler/common/hexutils.hpp @@ -0,0 +1,105 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef IROHA_HEXUTILS_HPP +#define IROHA_HEXUTILS_HPP + +#include +#include + +#include +#include +#include "common/result.hpp" +#include "interfaces/common_objects/byte_range.hpp" + +namespace iroha { + + template + inline auto hexstringToBytestringSize(Container const &c) + -> decltype(c.size()) { + return (c.size() + 1) / 2; + } + + template + inline auto bytestringToHexstringSize(Container const &c) + -> decltype(c.size()) { + return c.size() * 2; + } + + /** + * Convert string of raw bytes to printable hex string + * @param str - raw bytes string to convert + * @return - converted hex string + */ + template + inline void bytestringToHexstringAppend( + shared_model::interface::types::ByteRange input, + OutputContainer &destination) { + static_assert(sizeof(*input.data()) == sizeof(uint8_t), "type mismatch"); + const auto beg = reinterpret_cast(input.data()); + const auto end = beg + input.size(); + destination.reserve(destination.size() + bytestringToHexstringSize(input)); + boost::algorithm::hex_lower(beg, end, std::back_inserter(destination)); + } + + /** + * Convert string of raw bytes to printable hex string + * @param str - raw bytes string to convert + * @return - converted hex string + */ + inline std::string bytestringToHexstring(std::string_view str) { + std::string result; + bytestringToHexstringAppend( + shared_model::interface::types::makeByteRange(str), result); + return result; + } + + /** + * Convert printable hex string to string of raw bytes + * @param str - hex string to convert + * @return - raw bytes converted string or boost::noneif provided string + * was not a correct hex string + */ + inline iroha::expected::Result + hexstringToBytestringResult(std::string_view str) { + using namespace iroha::expected; + if (str.empty()) { + return makeError("Empty hex string."); + } + if (str.size() % 2 != 0) { + return makeError("Hex string contains uneven number of characters."); + } + std::string result; + result.reserve(hexstringToBytestringSize(str)); + try { + boost::algorithm::unhex( + str.begin(), str.end(), std::back_inserter(result)); + } catch (const boost::algorithm::hex_decode_error &e) { + return makeError(e.what()); + } + return iroha::expected::makeValue(std::move(result)); + } + + [[deprecated]] inline boost::optional hexstringToBytestring( + const std::string &str) { + return iroha::expected::resultToOptionalValue( + hexstringToBytestringResult(str)); + } + + /** + * Convert a number to a printable hex string + * @param val - numeric type value + * @return - converted hex string + */ + template ::value>> + inline std::string numToHexstring(const T val) { + std::stringstream ss; + ss << std::hex << std::setfill('0') << std::setw(sizeof(T) * 2) << val; + return ss.str(); + } + +} // namespace iroha + +#endif // IROHA_HEXUTILS_HPP diff --git a/core/profiler/common/instanceof.hpp b/core/profiler/common/instanceof.hpp new file mode 100644 index 0000000000..b16414ec25 --- /dev/null +++ b/core/profiler/common/instanceof.hpp @@ -0,0 +1,22 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_COMMON_INSTANCEOF_HPP +#define IROHA_COMMON_INSTANCEOF_HPP + +#include + +// check the type of the derived class +template +inline bool instanceof (const T *ptr) { + return typeid(Base) == typeid(*ptr); +} + +template +inline bool instanceof (const T &ptr) { + return typeid(Base) == typeid(ptr); +} + +#endif // IROHA_COMMON_INSTANCEOF_HPP diff --git a/core/profiler/common/irohad_version.cpp b/core/profiler/common/irohad_version.cpp new file mode 100644 index 0000000000..d5e2410b64 --- /dev/null +++ b/core/profiler/common/irohad_version.cpp @@ -0,0 +1,23 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "common/irohad_version.hpp" + +#include + +namespace iroha { + + const char *kGitPrettyVersion = GIT_REPO_PRETTY_VER; + + IrohadVersion getIrohadVersion() { + return IrohadVersion{ + IROHA_MAJOR_VERSION, IROHA_MINOR_VERSION, IROHA_PATCH_VERSION}; + } + + bool IrohadVersion::operator==(const IrohadVersion &rhs) const { + return major == rhs.major and minor == rhs.minor and patch == rhs.patch; + } + +} // namespace iroha diff --git a/core/profiler/common/irohad_version.hpp b/core/profiler/common/irohad_version.hpp new file mode 100644 index 0000000000..fd82cfde4e --- /dev/null +++ b/core/profiler/common/irohad_version.hpp @@ -0,0 +1,35 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef LIBS_COMMON_IROHAD_VERSION_HPP +#define LIBS_COMMON_IROHAD_VERSION_HPP + +// disabling GNU macros +#ifdef major +#undef major +#endif + +#ifdef minor +#undef minor +#endif + +namespace iroha { + + /// A string describing current git repository version in a human-readable way + extern const char *kGitPrettyVersion; + + struct IrohadVersion { + unsigned int major; + unsigned int minor; + unsigned int patch; + + bool operator==(const IrohadVersion &) const; + }; + + IrohadVersion getIrohadVersion(); + +} // namespace iroha + +#endif // LIBS_COMMON_IROHAD_VERSION_HPP diff --git a/core/profiler/common/is_any.hpp b/core/profiler/common/is_any.hpp new file mode 100644 index 0000000000..bf2d560719 --- /dev/null +++ b/core/profiler/common/is_any.hpp @@ -0,0 +1,34 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_IS_ANY_HPP +#define IROHA_IS_ANY_HPP + +#include + +namespace iroha { + + template + struct is_any : std::false_type {}; + + template + struct is_any : std::is_same {}; + + /** + * Disjunctive type check. Returns true if first type is contained in provided + * list, false otherwise + * @tparam T first type + * @tparam First head of types list + * @tparam Rest tail of types list + */ + template + struct is_any + : std::integral_constant::value + || is_any::value> {}; + +} // namespace iroha + +#endif // IROHA_IS_ANY_HPP diff --git a/core/profiler/common/macro.hpp b/core/profiler/common/macro.hpp new file mode 100644 index 0000000000..ec4b19f841 --- /dev/null +++ b/core/profiler/common/macro.hpp @@ -0,0 +1,29 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef IROHA_MACRO_HPP +#define IROHA_MACRO_HPP + +#ifdef IROHA_ALIGN +# error IROHA_ALIGN already defined. +#endif//IROHA_ALIGN + +#ifdef IROHA_ALIGN_TYPE +# error IROHA_ALIGN_TYPE already defined. +#endif//IROHA_ALIGN_TYPE + +#if defined(_MSC_VER) +# define IROHA_ALIGN(x) __declspec(align(x)) +#else +# if defined(__GNUC__) +# define IROHA_ALIGN(x) __attribute__ ((aligned(x))) +# endif +#endif +#define IROHA_ALIGN_TYPE(t,x) typedef t IROHA_ALIGN(x) + +#ifndef IROHA_ALIGN_MEM +# define IROHA_ALIGN_MEM(mem,base) ((((size_t)mem) + static_cast(base - 1)) & ~static_cast(base - 1)) +#endif//IROHA_ALIGN_MEM + +#endif//IROHA_MACRO_HPP diff --git a/core/profiler/common/mem_operations.hpp b/core/profiler/common/mem_operations.hpp new file mode 100644 index 0000000000..6c738316aa --- /dev/null +++ b/core/profiler/common/mem_operations.hpp @@ -0,0 +1,26 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_COMMON_MEM_OPERATIONS_HPP +#define IROHA_COMMON_MEM_OPERATIONS_HPP + +#include + +namespace iroha { + + template + void memzero(T &t) { + static_assert(std::is_pod::value, "T must be POD."); + std::memset(&t, 0, sizeof(t)); + } + + template + void memcpy(T &dst, T const &src) { + static_assert(std::is_pod::value, "T must be POD."); + std::memcpy(&dst, &src, sizeof(src)); + } +} // namespace iroha + +#endif // IROHA_COMMON_MEM_OPERATIONS_HPP diff --git a/core/profiler/common/memory_utils.hpp b/core/profiler/common/memory_utils.hpp new file mode 100644 index 0000000000..b7e6473702 --- /dev/null +++ b/core/profiler/common/memory_utils.hpp @@ -0,0 +1,26 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_MEMORY_UTILS_HPP +#define IROHA_MEMORY_UTILS_HPP + +#include +#include + +namespace iroha { namespace memory { + + template void memzero(T& t) { + static_assert(std::is_pod::value, "To zero memory T must be POD!"); + memset(&t, 0, sizeof(t)); + } + + template void memcpy(T& dst, T const &src) { + static_assert(std::is_pod::value, "To plain copy memory T must be POD!"); + std::memcpy(&dst, &src, sizeof(dst)); + } + +}} + +#endif // IROHA_MEMORY_UTILS_HPP diff --git a/core/profiler/common/murmur2.h b/core/profiler/common/murmur2.h new file mode 100755 index 0000000000..b2b5b30209 --- /dev/null +++ b/core/profiler/common/murmur2.h @@ -0,0 +1,61 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_MURMUR_2_H +#define IROHA_MURMUR_2_H + +namespace iroha { namespace ct_hash { + +class Hasher { + static constexpr /* h */uint32_t __init__(uint32_t len) { + return 0 ^ len; + } + + template static constexpr uint32_t __load__(__T& data, uint32_t offset) { + return data[offset + 0] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24); + } + + static constexpr uint32_t __mul__(uint32_t val1, uint32_t val2) { + return val1 * val2; + } + + static constexpr uint32_t __sl__(uint32_t value, uint32_t count) { + return (value << count); + } + + static constexpr uint32_t __sr__(uint32_t value, uint32_t count) { + return (value >> count); + } + + static constexpr uint32_t __xor__(uint32_t h, uint32_t k) { + return h ^ k; + } + + static constexpr uint32_t __xor_with_sr__(uint32_t k, uint32_t r) { + return __xor__(k, __sr__(k, r)); + } + + template static constexpr /* h */uint32_t __proc__(__Type& data, uint32_t len, uint32_t offset, uint32_t h, uint32_t m, uint32_t r) { + return + len >= 4 ? __proc__(data, len - 4, offset + 4, __xor__(__mul__(h, m), __mul__(__xor_with_sr__(__mul__(__load__(data, offset), m), r), m)), m, r) : + len == 3 ? __proc__(data, len - 1, offset, __xor__(h, __sl__(data[offset + 2], 16)), m, r) : + len == 2 ? __proc__(data, len - 1, offset, __xor__(h, __sl__(data[offset + 1], 8)), m, r) : + len == 1 ? __proc__(data, len - 1, offset, __xor__(h, data[offset]) * m, m, r) : + __xor__(__mul__(__xor_with_sr__(h, 13), m), __sr__(__mul__(__xor_with_sr__(h, 13), m), 15)); + } + +public: + template static constexpr uint32_t murmur2(__Type& data, uint32_t len) { + return __proc__(data, len, 0, __init__(len), 0x5bd1e995, 24); + } +}; + +} } + +#ifndef CT_MURMUR2 +# define CT_MURMUR2(x) ::iroha::ct_hash::Hasher::murmur2(x, (sizeof(x) / sizeof(x[0])) - 1) +#endif//CT_MURMUR2 + +#endif//IROHA_MURMUR_2_H \ No newline at end of file diff --git a/core/profiler/common/obj_utils.hpp b/core/profiler/common/obj_utils.hpp new file mode 100644 index 0000000000..acf0e8bde1 --- /dev/null +++ b/core/profiler/common/obj_utils.hpp @@ -0,0 +1,88 @@ +/** + * Copyright Soramitsu Co., Ltd. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef IROHA_COMMON_OBJ_UTILS_HPP +#define IROHA_COMMON_OBJ_UTILS_HPP + +#include + +namespace iroha { + + /** + * Create map get function for value retrieval by key + * @tparam K - map key type + * @tparam V - map value type + * @param map - map for value retrieval + * @return function which takes key, returns value if key exists, + * nullopt otherwise + */ + template + auto makeOptionalGet(C map) { + return [&map](auto key) -> boost::optional { + auto it = map.find(key); + if (it != std::end(map)) { + return it->second; + } + return boost::none; + }; + } + + /** + * Return function which invokes class method by pointer to member with + * provided arguments + * + * class A { + * int f(int, double); + * } + * + * A a; + * int i = makeMethodInvoke(a, 1, 1.0); + * + * @tparam T - provided class type + * @tparam Args - provided arguments types + * @param object - class object + * @param args - function arguments + * @return described function + */ + template + auto makeMethodInvoke(T &object, Args &&... args) { + return [&](auto f) { return (object.*f)(std::forward(args)...); }; + } + + /** + * Assign the value to the object member + * @tparam V - object member type + * @tparam B - object type + * @param object - object value for member assignment + * @param member - pointer to member in block + * @return object with deserialized member on success, nullopt otherwise + */ + template + auto assignObjectField(B object, V B::*member) { + return [=](auto value) mutable { + object.*member = value; + return boost::make_optional(object); + }; + } + + /** + * Assign the value to the object member. Block is wrapped in monad + * @tparam P - monadic type + * @tparam V - object member type + * @tparam B - object type + * @param object - object value for member assignment + * @param member - pointer to member in object + * @return object with deserialized member on success, nullopt otherwise + */ + template