From: Simon Marchi Date: Fri, 1 Mar 2024 15:59:51 +0000 (-0500) Subject: clock-correlation-validator: add clock correlation validation util X-Git-Url: https://git.efficios.com/?p=babeltrace.git;a=commitdiff_plain;h=e0f8968a1182ea437ee4db0abc4043ba1dc286a2 clock-correlation-validator: add clock correlation validation util `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 Reviewed-on: https://review.lttng.org/c/babeltrace/+/11753 Reviewed-by: Philippe Proulx Tested-by: jenkins --- diff --git a/src/Makefile.am b/src/Makefile.am index 7df63580..d42b5e15 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 index 00000000..a116c364 --- /dev/null +++ b/src/clock-correlation-validator/clock-correlation-validator.cpp @@ -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 clockCls; + bt2::OptionalBorrowedObject 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( + 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(validator)->validate(bt2::wrap(msg)); + return true; + } catch (const bt2ccv::ClockCorrelationError& error) { + *type = static_cast(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(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 index 00000000..4ccb7598 --- /dev/null +++ b/src/clock-correlation-validator/clock-correlation-validator.h @@ -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 +#include +#include + +#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 index 00000000..773c97cb --- /dev/null +++ b/src/clock-correlation-validator/clock-correlation-validator.hpp @@ -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 expectedUuid, + const bt2::OptionalBorrowedObject actualClockCls, + const bt2::OptionalBorrowedObject expectedClockCls, + const bt2::OptionalBorrowedObject 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 expectedUuid() const noexcept + { + return _mExpectedUuid; + } + + bt2::OptionalBorrowedObject actualClockCls() const noexcept + { + return _mActualClockCls; + } + + bt2::OptionalBorrowedObject expectedClockCls() const noexcept + { + return _mExpectedClockCls; + } + + bt2::OptionalBorrowedObject streamCls() const noexcept + { + return _mStreamCls; + } + +private: + Type _mType; + bt2s::optional _mExpectedUuid; + bt2::OptionalBorrowedObject _mActualClockCls; + bt2::OptionalBorrowedObject _mExpectedClockCls; + bt2::OptionalBorrowedObject _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 */ diff --git a/src/common/macros.h b/src/common/macros.h index 903219b8..13ba2d36 100644 --- a/src/common/macros.h +++ b/src/common/macros.h @@ -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