measure.h 11.2 KB
/* ****************************************************************************
 * Copyright 2019 Open Systems Development BV                                 *
 *                                                                            *
 * Permission is hereby granted, free of charge, to any person obtaining a    *
 * copy of this software and associated documentation files (the "Software"), *
 * to deal in the Software without restriction, including without limitation  *
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,   *
 * and/or sell copies of the Software, and to permit persons to whom the      *
 * Software is furnished to do so, subject to the following conditions:       *
 *                                                                            *
 * The above copyright notice and this permission notice shall be included in *
 * all copies or substantial portions of the Software.                        *
 *                                                                            *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   *
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    *
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    *
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        *
 * DEALINGS IN THE SOFTWARE.                                                  *
 * ***************************************************************************/
#ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_MEASURE_H
#define OSDEV_COMPONENTS_MQTT_MEASUREMENT_MEASURE_H

#include "macrodefs.h"
#include "histogramprovider.h"
#include "timemeasurement.h"

namespace osdev {
namespace components {
namespace mqtt {
namespace measurement {

inline std::string createId(const std::string& operationName, const std::string& dynamicId)
{
    return operationName + (dynamicId.empty() ? std::string{} : std::string(" ") + dynamicId);
}

}       // End namespace measurement
}       // End namespace mqtt
}       // End namespace components
}       // End namespace osdev

#define OSDEV_COMPONENTS_MEASUREMENT_LOCATION \
    (OSDEV_COMPONENTS_MANAGEDBASEFILENAME + OSDEV_COMPONENTS_CSTRING(":" OSDEV_COMPONENTS_TOSTRING(__LINE__))).chars

/**
 * @brief Macro helper that sets up a TimeMeasurement on an operation.
 * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements.
 * @param operationName The name of the operation. This is part of the id under which the measurements are registered.
 * @param dynamicId The dynamic identification of this measurement. This id makes different instances of the same operation unique.
 *                  The dynamicId must be an expression that yields a string like object. The dynamic id is part of the id under which the
 *                  measurements are registered.
 * @param operation The operation to perform.
 * @param context The context tag used to identify the HistogramProvider.
 * @param valueType A datatype that can be used by HistogramProvider.
 * @param numBuckets The number of buckets to use for the histogram.
 * @param boolOnDestroy Boolean value that makes the measurement measure on destruction.
 */
#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, dynamicId, operation, context, valueType, numBuckets, boolOnDestroy)       \
    static const std::string OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __) =                                            \
        std::string(OSDEV_COMPONENTS_CSTRING(#operationName).chars);                                                                          \
    osdev::components::mqtt::measurement::TimeMeasurement OSDEV_COMPONENTS_COMBINE(                                                                    \
        OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __)(                                                                                        \
        osdev::components::mqtt::measurement::createId(OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __), dynamicId),                \
        [](const std::string& id, std::chrono::steady_clock::time_point, std::chrono::microseconds, std::chrono::microseconds sinceLast) { \
            osdev::components::mqtt::measurement::HistogramProvider<context, valueType, numBuckets>::instance().addValue(id, sinceLast);            \
        },                                                                                                                                 \
        boolOnDestroy);                                                                                                                    \
    operation;

/**
 * @brief Make a measurement before and after an operation.
 * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements.
 * @param operationName The name under which the measurements are registered.
 * @param operation The operation to perform.
 * @param context The context tag used to identify the HistogramProvider.
 * @param valueType A datatype that can be used by HistogramProvider.
 * @param numBuckets The number of buckets to use for the histogram.
 */
#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE(operationName, operation, context, valueType, numBuckets)                                               \
    OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, OSDEV_COMPONENTS_MEASUREMENT_LOCATION, operation, context, valueType, numBuckets, false) \
    OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __).measure();

/**
 * @brief Make a measurement on a return operation.
 * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements.
 * @param operationName The name under which the measurements are registered.
 * @param operation The operation to perform.
 * @param context The context tag used to identify the HistogramProvider.
 * @param valueType A datatype that can be used by HistogramProvider.
 * @param numBuckets The number of buckets to use for the histogram.
 */
#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_ONDESTROY(operationName, operation, context, valueType, numBuckets) \
    OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, OSDEV_COMPONENTS_MEASUREMENT_LOCATION, operation, context, valueType, numBuckets, true)

/**
 * @brief Make a measurement on a return operation.
 * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements.
 * @param operationName The name under which the measurements are registered.
 * @param dynamicId An extra identification to discern between instances of the same operation. The dynamicId must be an expression that
 *                  yields a string like object.
 * @param operation The operation to perform.
 * @param context The context tag used to identify the HistogramProvider.
 * @param valueType A datatype that can be used by HistogramProvider.
 * @param numBuckets The number of buckets to use for the histogram.
 */
#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC_ONDESTROY(operationName, dynamicId, operation, context, valueType, numBuckets) \
    OSDEV_COMPONENTS_MEASUREMENT_MEASURE_HELPER(operationName, dynamicId, operation, context, valueType, numBuckets, true)

/**
 * @brief Nop version that only performs the operation.
 */
#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_ONDESTROY_NOP(operationName, operation, context, valueType, numBuckets) operation;

/**
 * @brief Nop version that only performs the operation.
 */
#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_NOP(operationName, operation, context, valueType, numBuckets) operation;

/**
 * @brief Nop version that only performs the operation.
 */
#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC_ONDESTROY_NOP(operationName, dynamicId, operation, context, valueType, numBuckets) operation;

/**
 * @brief Make a measurement before and after an operation for a specific instance of the operation.
 * The operation must have observable side effects otherwise the compiler might move the operation from in between the time measurements.
 * @param dynamicId An extra identification to discern between instances of the same operation.
 * @param operationName The name under which the measurements are registered.
 * @param operation The operation to perform.
 * @param context The context tag used to identify the HistogramProvider.
 * @param valueType A datatype that can be used by HistogramProvider.
 * @param numBuckets The number of buckets to use for the histogram.
 */
#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC(dynamicId, operationName, operation, context, valueType, numBuckets)                    \
    auto OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPREFIX__, __LINE__), __) =                                                          \
        OSDEV_COMPONENTS_MANAGEDBASEFILENAME + OSDEV_COMPONENTS_CSTRING(":" OSDEV_COMPONENTS_TOSTRING(__LINE__) ", function ");                     \
    auto OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPOSTFIX__, __LINE__), __) = OSDEV_COMPONENTS_CSTRING(", " #operationName " ");         \
    static const std::string OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __) =                                            \
        std::string(OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPREFIX__, __LINE__), __).chars) +                                        \
        std::string(__func__) +                                                                                                            \
        OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(IDPOSTFIX__, __LINE__), __).chars;                                                     \
    osdev::components::mqtt::measurement::TimeMeasurement OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __)(                         \
        OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(ID__, __LINE__), __) + dynamicId,                                                      \
        [](const std::string& id, std::chrono::steady_clock::time_point, std::chrono::microseconds, std::chrono::microseconds sinceLast) { \
            osdev::components::mqtt::measurement::HistogramProvider<context, valueType, numBuckets>::instance().addValue(id, sinceLast);            \
        },                                                                                                                                 \
        false);                                                                                                                            \
    operation;                                                                                                                             \
    OSDEV_COMPONENTS_COMBINE(OSDEV_COMPONENTS_COMBINE(TM__, __LINE__), __).measure();

/**
 * @brief Nop version that only performs the operation.
 */
#define OSDEV_COMPONENTS_MEASUREMENT_MEASURE_SPECIFIC_NOP(dynamicId, operationName, operation, context, valueType, numBuckets) operation;

#endif // OSDEV_COMPONENTS_MEASUREMENT_MEASUREMENT_H