2 * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: GPL-2.0-only
11 #include <common/format.hpp>
12 #include <common/make-unique.hpp>
14 #include <vendor/optional.hpp>
19 #include <type_traits>
29 enum class byte_order {
34 class field_location {
40 EVENT_RECORD_COMMON_CONTEXT,
41 EVENT_RECORD_SPECIFIC_CONTEXT,
45 using elements = std::vector<std::string>;
47 field_location(root lookup_root, elements elements);
48 bool operator==(const field_location& other) const noexcept;
51 const elements elements_;
55 * Field, and the various field types, represents fields as exposed by the
56 * LTTng tracers. These classes do not attempt to describe the complete spectrum of the CTF
62 using cuptr = std::unique_ptr<const type>;
64 static byte_order reverse_byte_order(byte_order byte_order) noexcept;
66 bool operator==(const type& other) const noexcept;
67 bool operator!=(const type& other) const noexcept;
70 /* Obtain an independent copy of `type`. */
71 virtual type::cuptr copy() const = 0;
73 virtual void accept(type_visitor& visitor) const = 0;
75 const unsigned int alignment;
78 explicit type(unsigned int alignment);
81 virtual bool _is_equal(const type& rhs) const noexcept = 0;
86 using uptr = std::unique_ptr<field>;
87 using cuptr = std::unique_ptr<const field>;
89 field(std::string name, type::cuptr type);
90 void accept(field_visitor& visitor) const;
91 bool operator==(const field& other) const noexcept;
93 const type& get_type() const;
94 type::cuptr move_type() noexcept;
96 const std::string name;
102 class integer_type : public type {
104 enum class signedness {
117 DEFAULT_CLOCK_TIMESTAMP,
118 /* Packet header field class specific roles. */
119 DATA_STREAM_CLASS_ID,
122 /* Packet context field class specific roles. */
123 DISCARDED_EVENT_RECORD_COUNTER_SNAPSHOT,
124 PACKET_CONTENT_LENGTH,
125 PACKET_END_DEFAULT_CLOCK_TIMESTAMP,
126 PACKET_SEQUENCE_NUMBER,
128 /* Event record field class roles. */
129 EVENT_RECORD_CLASS_ID,
132 using roles = std::vector<role>;
134 integer_type(unsigned int alignment,
135 byte_order byte_order,
137 signedness signedness,
141 type::cuptr copy() const override;
143 void accept(type_visitor& visitor) const override;
145 const enum byte_order byte_order;
146 const unsigned int size;
148 * signedness and base are suffixed with '_' to work-around a bug in older
149 * GCCs (before 6) that do not recognize hidden/shadowed enumeration as valid
150 * nested-name-specifiers.
152 const signedness signedness_;
157 bool _is_equal(const type& other) const noexcept override;
160 class floating_point_type : public type {
162 floating_point_type(unsigned int alignment,
163 byte_order byte_order,
164 unsigned int exponent_digits,
165 unsigned int mantissa_digits);
167 type::cuptr copy() const final;
169 void accept(type_visitor& visitor) const final;
171 const enum byte_order byte_order;
172 const unsigned int exponent_digits;
173 const unsigned int mantissa_digits;
176 bool _is_equal(const type& other) const noexcept final;
179 class enumeration_type : public integer_type {
181 enumeration_type(unsigned int alignment,
182 enum byte_order byte_order,
184 enum signedness signedness,
186 integer_type::roles roles = {});
188 void accept(type_visitor& visitor) const override = 0;
192 template <class MappingIntegerType>
193 class enumeration_mapping_range {
195 using range_integer_t = MappingIntegerType;
197 enumeration_mapping_range(MappingIntegerType in_begin, MappingIntegerType in_end) :
198 begin{in_begin}, end{in_end}
202 const range_integer_t begin, end;
205 template <class MappingIntegerType>
206 bool operator==(const enumeration_mapping_range<MappingIntegerType>& lhs,
207 const enumeration_mapping_range<MappingIntegerType>& rhs) noexcept
209 return lhs.begin == rhs.begin && lhs.end == rhs.end;
212 template <class MappingIntegerType>
213 class enumeration_mapping {
215 using range_t = enumeration_mapping_range<MappingIntegerType>;
217 enumeration_mapping(const enumeration_mapping<MappingIntegerType>& other) = default;
218 enumeration_mapping(const enumeration_mapping<MappingIntegerType>&& other) noexcept :
219 name{ std::move(other.name) }, range{ other.range }
223 enumeration_mapping(std::string in_name, MappingIntegerType value) : name{std::move(in_name)}, range{value, value}
227 enumeration_mapping(std::string in_name, range_t in_range) : name{std::move(in_name)}, range{in_range}
231 const std::string name;
233 * Only one range per mapping is supported for the moment as
234 * the tracers (and CTF 1.8) can't express multiple ranges per
235 * mapping, which is allowed by CTF 2.
240 template <class MappingIntegerType>
241 bool operator==(const enumeration_mapping<MappingIntegerType>& lhs,
242 const enumeration_mapping<MappingIntegerType>& rhs) noexcept
244 return lhs.name == rhs.name && lhs.range == rhs.range;
246 } /* namespace details */
248 template <typename MappingIntegerType>
249 class typed_enumeration_type : public enumeration_type {
251 using mapping = details::enumeration_mapping<MappingIntegerType>;
252 using mappings = std::vector<mapping>;
254 static_assert(std::is_integral<MappingIntegerType>::value &&
255 sizeof(MappingIntegerType) == 8,
256 "MappingIntegerType must be either int64_t or uint64_t");
258 typed_enumeration_type(unsigned int in_alignment,
259 enum byte_order in_byte_order,
260 unsigned int in_size,
262 const std::shared_ptr<const mappings>& in_mappings,
263 integer_type::roles in_roles = {}) :
264 enumeration_type(in_alignment,
267 std::is_signed<MappingIntegerType>::value ?
268 integer_type::signedness::SIGNED :
269 integer_type::signedness::UNSIGNED,
271 std::move(in_roles)),
272 mappings_{std::move(in_mappings)}
276 type::cuptr copy() const override
278 return lttng::make_unique<typed_enumeration_type<MappingIntegerType>>(
279 alignment, byte_order, size, base_, mappings_, roles_);
282 void accept(type_visitor& visitor) const final;
284 const std::shared_ptr<const mappings> mappings_;
287 bool _is_equal(const type& base_other) const noexcept final
289 const auto& other = static_cast<const typed_enumeration_type<MappingIntegerType>&>(
292 return integer_type::_is_equal(base_other) && *this->mappings_ == *other.mappings_;
296 /* Aliases for all allowed enumeration mapping types. */
297 using signed_enumeration_type = typed_enumeration_type<int64_t>;
298 using unsigned_enumeration_type = typed_enumeration_type<uint64_t>;
300 class array_type : public type {
302 array_type(unsigned int alignment, type::cuptr element_type);
304 const type::cuptr element_type;
307 bool _is_equal(const type& base_other) const noexcept override;
310 class static_length_array_type : public array_type {
312 static_length_array_type(unsigned int alignment,
313 type::cuptr element_type,
316 type::cuptr copy() const final;
318 void accept(type_visitor& visitor) const final;
320 const uint64_t length;
323 bool _is_equal(const type& base_other) const noexcept final;
326 class dynamic_length_array_type : public array_type {
328 dynamic_length_array_type(unsigned int alignment,
329 type::cuptr element_type,
330 field_location length_field_location);
332 type::cuptr copy() const final;
334 void accept(type_visitor& visitor) const final;
336 const field_location length_field_location;
339 bool _is_equal(const type& base_other) const noexcept final;
342 class static_length_blob_type : public type {
345 /* Packet header field class specific role. */
346 METADATA_STREAM_UUID,
349 using roles = std::vector<role>;
351 static_length_blob_type(unsigned int alignment, uint64_t in_length_bytes, roles roles = {});
353 type::cuptr copy() const final;
355 void accept(type_visitor& visitor) const final;
357 const uint64_t length_bytes;
361 bool _is_equal(const type& base_other) const noexcept final;
364 class dynamic_length_blob_type : public type {
366 dynamic_length_blob_type(unsigned int alignment, field_location length_field_location);
368 type::cuptr copy() const final;
370 void accept(type_visitor& visitor) const final;
372 const field_location length_field_location;
375 bool _is_equal(const type& base_other) const noexcept final;
378 class string_type : public type {
380 enum class encoding {
385 string_type(unsigned int alignment, enum encoding encoding);
388 * encoding is suffixed with '_' to work-around a bug in older
389 * GCCs (before 6) that do not recognize hidden/shadowed enumeration as valid
390 * nested-name-specifiers.
392 const encoding encoding_;
395 bool _is_equal(const type& base_other) const noexcept override;
398 class static_length_string_type : public string_type {
400 static_length_string_type(
401 unsigned int alignment, enum encoding in_encoding, uint64_t length);
403 type::cuptr copy() const final;
405 void accept(type_visitor& visitor) const final;
407 const uint64_t length;
410 bool _is_equal(const type& base_other) const noexcept final;
413 class dynamic_length_string_type : public string_type {
415 dynamic_length_string_type(unsigned int alignment,
416 enum encoding in_encoding,
417 field_location length_field_location);
419 type::cuptr copy() const final;
421 void accept(type_visitor& visitor) const final;
423 const field_location length_field_location;
426 bool _is_equal(const type& base_other) const noexcept final;
429 class null_terminated_string_type : public string_type {
431 null_terminated_string_type(unsigned int alignment, enum encoding in_encoding);
433 type::cuptr copy() const final;
435 void accept(type_visitor& visitor) const final;
438 class structure_type : public type {
440 using fields = std::vector<field::cuptr>;
442 structure_type(unsigned int alignment, fields in_fields);
444 type::cuptr copy() const final;
446 void accept(type_visitor& visitor) const final;
448 const fields fields_;
451 bool _is_equal(const type& base_other) const noexcept final;
454 template <typename MappingIntegerType>
455 class variant_type : public type {
456 static_assert(std::is_same<MappingIntegerType,
457 unsigned_enumeration_type::mapping::range_t::
458 range_integer_t>::value ||
459 std::is_same<MappingIntegerType,
460 signed_enumeration_type::mapping::range_t::
461 range_integer_t>::value,
462 "Variant mapping integer type must be one of those allowed by typed_enumeration_type");
465 using choice = std::pair<const details::enumeration_mapping<MappingIntegerType>, type::cuptr>;
466 using choices = std::vector<choice>;
468 variant_type(unsigned int in_alignment,
469 field_location in_selector_field_location,
470 choices in_choices) :
472 selector_field_location{std::move(in_selector_field_location)},
473 choices_{std::move(in_choices)}
477 type::cuptr copy() const final
479 choices copy_of_choices;
481 copy_of_choices.reserve(choices_.size());
482 for (const auto& current_choice : choices_) {
483 copy_of_choices.emplace_back(
484 current_choice.first, current_choice.second->copy());
487 return lttng::make_unique<variant_type<MappingIntegerType>>(
488 alignment, selector_field_location, std::move(copy_of_choices));
491 void accept(type_visitor& visitor) const final;
493 const field_location selector_field_location;
494 const choices choices_;
497 static bool _choices_are_equal(const choices& a, const choices& b)
499 if (a.size() != b.size()) {
503 return std::equal(a.cbegin(), a.cend(), b.cbegin(),
504 [](const choice& choice_a, const choice& choice_b) {
505 return choice_a.first == choice_b.first &&
506 *choice_a.second == *choice_b.second;
510 bool _is_equal(const type& base_other) const noexcept final
512 const auto& other = static_cast<decltype(*this)&>(base_other);
514 return selector_field_location == other.selector_field_location &&
515 _choices_are_equal(choices_, other.choices_);
519 class field_visitor {
521 virtual ~field_visitor() = default;
522 virtual void visit(const field& field) = 0;
525 field_visitor() = default;
530 virtual ~type_visitor() = default;
531 virtual void visit(const integer_type& type) = 0;
532 virtual void visit(const floating_point_type& type) = 0;
533 virtual void visit(const signed_enumeration_type& type) = 0;
534 virtual void visit(const unsigned_enumeration_type& type) = 0;
535 virtual void visit(const static_length_array_type& type) = 0;
536 virtual void visit(const dynamic_length_array_type& type) = 0;
537 virtual void visit(const static_length_blob_type& type) = 0;
538 virtual void visit(const dynamic_length_blob_type& type) = 0;
539 virtual void visit(const null_terminated_string_type& type) = 0;
540 virtual void visit(const static_length_string_type& type) = 0;
541 virtual void visit(const dynamic_length_string_type& type) = 0;
542 virtual void visit(const structure_type& type) = 0;
543 virtual void visit(const variant_type<signed_enumeration_type::mapping::range_t::range_integer_t>& type) = 0;
544 virtual void visit(const variant_type<unsigned_enumeration_type::mapping::range_t::range_integer_t>& type) = 0;
547 type_visitor() = default;
550 } /* namespace trace */
551 } /* namespace sessiond */
552 } /* namespace lttng */
555 * Field formatters for libfmt.
557 * Due to a bug in g++ < 7.1, this specialization must be enclosed in the fmt namespace,
558 * see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480.
562 struct formatter<lttng::sessiond::trace::field_location> : formatter<std::string> {
563 template <typename FormatContextType>
564 typename FormatContextType::iterator format(
565 const lttng::sessiond::trace::field_location& location, FormatContextType& ctx)
567 std::string location_str{"["};
569 switch (location.root_) {
570 case lttng::sessiond::trace::field_location::root::PACKET_HEADER:
571 location_str += "\"packet-header\"";
573 case lttng::sessiond::trace::field_location::root::PACKET_CONTEXT:
574 location_str += "\"packet-context\"";
576 case lttng::sessiond::trace::field_location::root::EVENT_RECORD_HEADER:
577 location_str += "\"event-record-header\"";
579 case lttng::sessiond::trace::field_location::root::EVENT_RECORD_COMMON_CONTEXT:
580 location_str += "\"event-record-common-context\"";
582 case lttng::sessiond::trace::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT:
583 location_str += "\"event-record-specific-context\"";
585 case lttng::sessiond::trace::field_location::root::EVENT_RECORD_PAYLOAD:
586 location_str += "\"event-record-payload\"";
590 for (const auto &name : location.elements_) {
591 location_str += ", \"" + name + "\"";
595 return format_to(ctx.out(), location_str);
600 template <typename MappingIntegerType>
601 ::std::string format_mapping_range(typename lttng::sessiond::trace::typed_enumeration_type<
602 MappingIntegerType>::mapping::range_t range)
604 if (range.begin == range.end) {
605 return ::fmt::format("[{}]", range.begin);
607 return ::fmt::format("[{}, {}]", range.begin, range.end);
610 } /* namespace details */
613 struct formatter<typename lttng::sessiond::trace::signed_enumeration_type::mapping::range_t>
614 : formatter<std::string> {
615 template <typename FormatContextType>
616 typename FormatContextType::iterator
617 format(typename lttng::sessiond::trace::signed_enumeration_type::mapping::range_t range,
618 FormatContextType& ctx)
620 return format_to(ctx.out(),
621 details::format_mapping_range<
622 lttng::sessiond::trace::signed_enumeration_type::
623 mapping::range_t::range_integer_t>(
629 struct formatter<typename lttng::sessiond::trace::unsigned_enumeration_type::mapping::range_t>
630 : formatter<std::string> {
631 template <typename FormatContextType>
632 typename FormatContextType::iterator
633 format(typename lttng::sessiond::trace::unsigned_enumeration_type::mapping::range_t range,
634 FormatContextType& ctx)
636 return format_to(ctx.out(),
637 details::format_mapping_range<
638 lttng::sessiond::trace::unsigned_enumeration_type::
639 mapping::range_t::range_integer_t>(
644 } /* namespace fmt */
646 #endif /* LTTNG_FIELD_H */