clock-correlation-validator: add clock correlation validation util
authorSimon Marchi <simon.marchi@efficios.com>
Fri, 1 Mar 2024 15:59:51 +0000 (10:59 -0500)
committerSimon Marchi <simon.marchi@efficios.com>
Tue, 26 Mar 2024 18:56:36 +0000 (14:56 -0400)
`lib/graph/iterator.c` and `plugins/utils/muxer/msg-iter.cpp` have
equivalent code to verify that the various clock classes they see are
correlatable.  Add a util to factor out this logic.

Since the lib is written in C and the muxer component in C++, we need
both interfaces.  I chose to write the core of the library with a proper
C++ interface (since that's the future!) and make a small C wrapper
around it.  If/when we migrate some parts of the lib to C++, we will be
able to drop this C wrapper.

Change-Id: Iab8968ef33c9f7d4e2e8e06e0942de749a090841
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/11753
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
Tested-by: jenkins <jenkins@lttng.org>
src/Makefile.am
src/clock-correlation-validator/clock-correlation-validator.cpp [new file with mode: 0644]
src/clock-correlation-validator/clock-correlation-validator.h [new file with mode: 0644]
src/clock-correlation-validator/clock-correlation-validator.hpp [new file with mode: 0644]
src/common/macros.h

index 7df63580b931cdda8d661378e2f4ed40a1f4ee6a..d42b5e155a3f4ae346588257b31496a8292166c9 100644 (file)
@@ -51,6 +51,7 @@ DISTCLEANFILES = common/version.i
 noinst_LTLIBRARIES = \
        argpar/libargpar.la \
        autodisc/libautodisc.la \
+       clock-correlation-validator/libclock-correlation-validator.la \
        common/libcommon.la \
        compat/libcompat.la \
        cpp-common/libcpp-common.la \
@@ -74,6 +75,11 @@ autodisc_libautodisc_la_SOURCES = \
        autodisc/autodisc.c \
        autodisc/autodisc.h
 
+clock_correlation_validator_libclock_correlation_validator_la_SOURCES = \
+       clock-correlation-validator/clock-correlation-validator.cpp \
+       clock-correlation-validator/clock-correlation-validator.h \
+       clock-correlation-validator/clock-correlation-validator.hpp
+
 common_libcommon_la_SOURCES = \
        common/align.h \
        common/assert.c \
diff --git a/src/clock-correlation-validator/clock-correlation-validator.cpp b/src/clock-correlation-validator/clock-correlation-validator.cpp
new file mode 100644 (file)
index 0000000..a116c36
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2024 EfficiOS, Inc.
+ */
+
+#include "cpp-common/bt2/clock-class.hpp"
+#include "cpp-common/bt2/message.hpp"
+#include "cpp-common/bt2/wrap.hpp"
+
+#include "clock-correlation-validator.h"
+#include "clock-correlation-validator.hpp"
+
+namespace bt2ccv {
+
+void ClockCorrelationValidator::_validate(const bt2::ConstMessage msg)
+{
+    bt2::OptionalBorrowedObject<bt2::ConstClockClass> clockCls;
+    bt2::OptionalBorrowedObject<bt2::ConstStreamClass> streamCls;
+
+    switch (msg.type()) {
+    case bt2::MessageType::STREAM_BEGINNING:
+        streamCls = msg.asStreamBeginning().stream().cls();
+        clockCls = streamCls->defaultClockClass();
+        break;
+
+    case bt2::MessageType::MESSAGE_ITERATOR_INACTIVITY:
+        clockCls = msg.asMessageIteratorInactivity().clockSnapshot().clockClass();
+        break;
+
+    default:
+        bt_common_abort();
+    }
+
+    switch (_mExpectation) {
+    case PropsExpectation::UNSET:
+        /*
+         * This is the first analysis of a message with a clock
+         * snapshot: record the properties of that clock, against which
+         * we'll compare the clock properties of the following messages.
+         */
+        if (!clockCls) {
+            _mExpectation = PropsExpectation::NONE;
+        } else if (clockCls->originIsUnixEpoch()) {
+            _mExpectation = PropsExpectation::ORIGIN_UNIX;
+        } else if (const auto uuid = clockCls->uuid()) {
+            _mExpectation = PropsExpectation::ORIGIN_OTHER_UUID;
+            _mUuid = *uuid;
+        } else {
+            _mExpectation = PropsExpectation::ORIGIN_OTHER_NO_UUID;
+            _mClockClass = clockCls->shared();
+        }
+
+        break;
+
+    case PropsExpectation::NONE:
+        if (clockCls) {
+            throw ClockCorrelationError {
+                ClockCorrelationError::Type::EXPECTING_NO_CLOCK_CLASS_GOT_ONE,
+                bt2s::nullopt,
+                *clockCls,
+                {},
+                streamCls};
+        }
+
+        break;
+
+    case PropsExpectation::ORIGIN_UNIX:
+        if (!clockCls) {
+            throw ClockCorrelationError {
+                ClockCorrelationError::Type::EXPECTING_ORIGIN_UNIX_GOT_NONE,
+                bt2s::nullopt,
+                {},
+                {},
+                streamCls};
+        }
+
+        if (!clockCls->originIsUnixEpoch()) {
+            throw ClockCorrelationError {
+                ClockCorrelationError::Type::EXPECTING_ORIGIN_UNIX_GOT_OTHER,
+                bt2s::nullopt,
+                *clockCls,
+                {},
+                streamCls};
+        }
+
+        break;
+
+    case PropsExpectation::ORIGIN_OTHER_UUID:
+    {
+        if (!clockCls) {
+            throw ClockCorrelationError {
+                ClockCorrelationError::Type::EXPECTING_ORIGIN_UUID_GOT_NONE,
+                bt2s::nullopt,
+                {},
+                {},
+                streamCls};
+        }
+
+        if (clockCls->originIsUnixEpoch()) {
+            throw ClockCorrelationError {
+                ClockCorrelationError::Type::EXPECTING_ORIGIN_UUID_GOT_UNIX,
+                bt2s::nullopt,
+                *clockCls,
+                {},
+                streamCls};
+        }
+
+        const auto uuid = clockCls->uuid();
+
+        if (!uuid) {
+            throw ClockCorrelationError {
+                ClockCorrelationError::Type::EXPECTING_ORIGIN_UUID_GOT_NO_UUID,
+                bt2s::nullopt,
+                *clockCls,
+                {},
+                streamCls};
+        }
+
+        if (*uuid != _mUuid) {
+            throw ClockCorrelationError {
+                ClockCorrelationError::Type::EXPECTING_ORIGIN_UUID_GOT_OTHER_UUID,
+                _mUuid,
+                *clockCls,
+                {},
+                streamCls};
+        }
+
+        break;
+    }
+
+    case PropsExpectation::ORIGIN_OTHER_NO_UUID:
+        if (!clockCls) {
+            throw ClockCorrelationError {
+                ClockCorrelationError::Type::EXPECTING_ORIGIN_NO_UUID_GOT_NONE,
+                bt2s::nullopt,
+                {},
+                {},
+                streamCls};
+        }
+
+        if (clockCls->libObjPtr() != _mClockClass->libObjPtr()) {
+            throw ClockCorrelationError {
+                ClockCorrelationError::Type::EXPECTING_ORIGIN_NO_UUID_GOT_OTHER, bt2s::nullopt,
+                *clockCls, *_mClockClass, streamCls};
+        }
+
+        break;
+
+    default:
+        bt_common_abort();
+    }
+}
+
+} /* namespace bt2ccv */
+
+bt_clock_correlation_validator *bt_clock_correlation_validator_create() noexcept
+{
+    try {
+        return reinterpret_cast<bt_clock_correlation_validator *>(
+            new bt2ccv::ClockCorrelationValidator);
+    } catch (const std::bad_alloc&) {
+        return nullptr;
+    }
+}
+
+bool bt_clock_correlation_validator_validate_message(
+    bt_clock_correlation_validator * const validator, const bt_message * const msg,
+    bt_clock_correlation_validator_error_type * const type, bt_uuid * const expectedUuidOut,
+    const bt_clock_class ** const actualClockClsOut,
+    const bt_clock_class ** const expectedClockClsOut) noexcept
+{
+    try {
+        reinterpret_cast<bt2ccv::ClockCorrelationValidator *>(validator)->validate(bt2::wrap(msg));
+        return true;
+    } catch (const bt2ccv::ClockCorrelationError& error) {
+        *type = static_cast<bt_clock_correlation_validator_error_type>(error.type());
+
+        if (error.expectedUuid()) {
+            *expectedUuidOut = error.expectedUuid()->data();
+        } else {
+            *expectedUuidOut = nullptr;
+        }
+
+        if (error.actualClockCls()) {
+            *actualClockClsOut = error.actualClockCls()->libObjPtr();
+        } else {
+            *actualClockClsOut = nullptr;
+        }
+
+        if (error.expectedClockCls()) {
+            *expectedClockClsOut = error.expectedClockCls()->libObjPtr();
+        } else {
+            *expectedClockClsOut = nullptr;
+        }
+
+        return false;
+    }
+}
+
+void bt_clock_correlation_validator_destroy(
+    bt_clock_correlation_validator * const validator) noexcept
+{
+    delete reinterpret_cast<bt2ccv::ClockCorrelationValidator *>(validator);
+}
diff --git a/src/clock-correlation-validator/clock-correlation-validator.h b/src/clock-correlation-validator/clock-correlation-validator.h
new file mode 100644 (file)
index 0000000..4ccb759
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2024 EfficiOS, Inc.
+ */
+
+#ifndef CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_H
+#define CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_H
+
+#include <glib.h>
+#include <stdbool.h>
+#include <babeltrace2/babeltrace.h>
+
+#include "common/macros.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct bt_clock_class;
+struct bt_message;
+
+enum bt_clock_correlation_validator_error_type
+{
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_NO_CLOCK_CLASS_GOT_ONE,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UNIX_GOT_NONE,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UNIX_GOT_OTHER,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_NONE,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_UNIX,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_NO_UUID,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_OTHER_UUID,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_NO_UUID_GOT_NONE,
+       BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_NO_UUID_GOT_OTHER,
+};
+
+struct bt_clock_correlation_validator *bt_clock_correlation_validator_create(
+       void) BT_NOEXCEPT;
+
+bool bt_clock_correlation_validator_validate_message(
+       struct bt_clock_correlation_validator *validator,
+       const struct bt_message *msg,
+       enum bt_clock_correlation_validator_error_type *type,
+       bt_uuid *expected_uuid,
+       const struct bt_clock_class ** const actual_clock_cls,
+       const struct bt_clock_class ** const expected_clock_cls) BT_NOEXCEPT;
+
+void bt_clock_correlation_validator_destroy(
+       struct bt_clock_correlation_validator *validator) BT_NOEXCEPT;
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_H */
diff --git a/src/clock-correlation-validator/clock-correlation-validator.hpp b/src/clock-correlation-validator/clock-correlation-validator.hpp
new file mode 100644 (file)
index 0000000..773c97c
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * SPDX-License-Identifier: MIT
+ *
+ * Copyright 2024 EfficiOS, Inc.
+ */
+
+#ifndef CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_HPP
+#define CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_HPP
+
+#include "cpp-common/bt2/message.hpp"
+
+#include "clock-correlation-validator/clock-correlation-validator.h"
+
+namespace bt2ccv {
+
+class ClockCorrelationError final : public std::runtime_error
+{
+public:
+    enum class Type
+    {
+        EXPECTING_NO_CLOCK_CLASS_GOT_ONE =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_NO_CLOCK_CLASS_GOT_ONE,
+        EXPECTING_ORIGIN_UNIX_GOT_NONE =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UNIX_GOT_NONE,
+        EXPECTING_ORIGIN_UNIX_GOT_OTHER =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UNIX_GOT_OTHER,
+        EXPECTING_ORIGIN_UUID_GOT_NONE =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_NONE,
+        EXPECTING_ORIGIN_UUID_GOT_UNIX =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_UNIX,
+        EXPECTING_ORIGIN_UUID_GOT_NO_UUID =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_NO_UUID,
+        EXPECTING_ORIGIN_UUID_GOT_OTHER_UUID =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_UUID_GOT_OTHER_UUID,
+        EXPECTING_ORIGIN_NO_UUID_GOT_NONE =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_NO_UUID_GOT_NONE,
+        EXPECTING_ORIGIN_NO_UUID_GOT_OTHER =
+            BT_CLOCK_CORRELATION_VALIDATOR_ERROR_TYPE_EXPECTING_ORIGIN_NO_UUID_GOT_OTHER,
+    };
+
+    explicit ClockCorrelationError(
+        Type type, const bt2s::optional<bt2c::UuidView> expectedUuid,
+        const bt2::OptionalBorrowedObject<bt2::ConstClockClass> actualClockCls,
+        const bt2::OptionalBorrowedObject<bt2::ConstClockClass> expectedClockCls,
+        const bt2::OptionalBorrowedObject<bt2::ConstStreamClass> streamCls) noexcept :
+        std::runtime_error {"Clock classes are not correlatable"},
+        _mType {type}, _mExpectedUuid {expectedUuid}, _mActualClockCls {actualClockCls},
+        _mExpectedClockCls {expectedClockCls}, _mStreamCls {streamCls}
+
+    {
+    }
+
+    Type type() const noexcept
+    {
+        return _mType;
+    }
+
+    bt2s::optional<bt2c::UuidView> expectedUuid() const noexcept
+    {
+        return _mExpectedUuid;
+    }
+
+    bt2::OptionalBorrowedObject<bt2::ConstClockClass> actualClockCls() const noexcept
+    {
+        return _mActualClockCls;
+    }
+
+    bt2::OptionalBorrowedObject<bt2::ConstClockClass> expectedClockCls() const noexcept
+    {
+        return _mExpectedClockCls;
+    }
+
+    bt2::OptionalBorrowedObject<bt2::ConstStreamClass> streamCls() const noexcept
+    {
+        return _mStreamCls;
+    }
+
+private:
+    Type _mType;
+    bt2s::optional<bt2c::UuidView> _mExpectedUuid;
+    bt2::OptionalBorrowedObject<bt2::ConstClockClass> _mActualClockCls;
+    bt2::OptionalBorrowedObject<bt2::ConstClockClass> _mExpectedClockCls;
+    bt2::OptionalBorrowedObject<bt2::ConstStreamClass> _mStreamCls;
+};
+
+class ClockCorrelationValidator final
+{
+private:
+    enum class PropsExpectation
+    {
+        /* We haven't recorded clock properties yet. */
+        UNSET,
+
+        /* Expect to have no clock. */
+        NONE,
+
+        /* Expect a clock with a Unix epoch origin. */
+        ORIGIN_UNIX,
+
+        /* Expect a clock without a Unix epoch origin, but with a UUID. */
+        ORIGIN_OTHER_UUID,
+
+        /* Expect a clock without a Unix epoch origin and without a UUID. */
+        ORIGIN_OTHER_NO_UUID,
+    };
+
+public:
+    void validate(const bt2::ConstMessage msg)
+    {
+        if (!msg.isStreamBeginning() && !msg.isMessageIteratorInactivity()) {
+            return;
+        }
+
+        this->_validate(msg);
+    }
+
+private:
+    void _validate(const bt2::ConstMessage msg);
+
+    PropsExpectation _mExpectation = PropsExpectation::UNSET;
+
+    /*
+     * Expected UUID of the clock, if `_mExpectation` is
+     * `PropsExpectation::ORIGIN_OTHER_UUID`.
+     *
+     * If the origin of the clock is the Unix epoch, then the UUID is
+     * irrelevant because the clock will have a correlation with other
+     * clocks having the same origin.
+     */
+    bt2c::Uuid _mUuid;
+
+    /*
+     * Expected clock class, if `_mExpectation` is
+     * `ClockExpectation::ORIGIN_OTHER_NO_UUID`.
+     *
+     * If the first analyzed clock class has an unknown origin and no
+     * UUID, then all subsequent analyzed clock classes must be the same
+     * instance.
+     *
+     * To make sure that the clock class pointed to by this member
+     * doesn't get freed and another one reallocated at the same
+     * address, which could potentially bypass the clock expectation
+     * check, we keep a strong reference, ensuring that the clock class
+     * lives at least as long as the owner of this validator.
+     */
+    bt2::ConstClockClass::Shared _mClockClass;
+};
+
+} /* namespace bt2ccv */
+
+#endif /* CLOCK_CORRELATION_VALIDATOR_CLOCK_CORRELATION_VALIDATOR_HPP */
index 903219b8779d228aae93efae7a03fc56cba61129..13ba2d3628c1abf4b36239838be6e00146224da5 100644 (file)
@@ -29,6 +29,15 @@ extern "C" {
 #define BT_EXPORT __attribute__((visibility("default")))
 #endif
 
+/*
+ * BT_NOEXCEPT: defined to `noexcept` if compiling as C++, else empty.
+ */
+#if defined(__cplusplus)
+#define BT_NOEXCEPT noexcept
+#else
+#define BT_NOEXCEPT
+#endif
+
 /* Enable `txt` if developer mode is enabled. */
 #ifdef BT_DEV_MODE
 #define BT_IF_DEV_MODE(txt) txt
This page took 0.029199 seconds and 4 git commands to generate.