2 * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: GPL-2.0-only
8 #include "tsdl-trace-class-visitor.hpp"
9 #include "clock-class.hpp"
11 #include <common/exception.hpp>
12 #include <common/format.hpp>
13 #include <common/make-unique.hpp>
14 #include <common/uuid.hpp>
15 #include <common/scope-exit.hpp>
17 #include <vendor/optional.hpp>
25 #include <unordered_set>
27 namespace lst
= lttng::sessiond::trace
;
28 namespace tsdl
= lttng::sessiond::tsdl
;
31 const auto ctf_spec_major
= 1;
32 const auto ctf_spec_minor
= 8;
35 * Although the CTF v1.8 specification recommends ignoring any leading underscore, Some readers,
36 * such as Babeltrace 1.x, expect special identifiers without a prepended underscore.
38 const std::unordered_set
<std::string
> safe_tsdl_identifiers
= {
56 * A previous implementation always prepended '_' to the identifiers in order to
57 * side-step the problem of escaping TSDL keywords and ensuring identifiers
58 * started with an alphabetic character.
60 * Changing this behaviour to a smarter algorithm would break readers that have
61 * come to expect this initial underscore.
63 std::string
escape_tsdl_identifier(const std::string
& original_identifier
)
65 if (original_identifier
.size() == 0) {
66 LTTNG_THROW_ERROR("Invalid 0-length identifier used in trace description");
69 if (safe_tsdl_identifiers
.find(original_identifier
) != safe_tsdl_identifiers
.end()) {
70 return original_identifier
;
73 std::string new_identifier
;
74 /* Optimisticly assume most identifiers are valid and allocate the same length. */
75 new_identifier
.reserve(original_identifier
.size());
78 /* Replace illegal characters by '_'. */
79 std::locale c_locale
{"C"};
80 for (const auto current_char
: original_identifier
) {
81 if (!std::isalnum(current_char
, c_locale
) && current_char
!= '_') {
82 new_identifier
+= '_';
84 new_identifier
+= current_char
;
88 return new_identifier
;
91 std::string
escape_tsdl_env_string_value(const std::string
& original_string
)
93 std::string escaped_string
;
95 escaped_string
.reserve(original_string
.size());
97 for (const auto c
: original_string
) {
100 escaped_string
+= "\\n";
103 escaped_string
+= "\\\\";
106 escaped_string
+= "\"";
114 return escaped_string
;
118 * Variants produced by LTTng-UST contain TSDL-unsafe names. A variant/selector
119 * sanitization pass is performed before serializing a trace class hierarchy to
122 * The variant_tsdl_keyword_sanitizer visitor is used to visit field before it
123 * is handed-over to the actual TSDL-producing visitor.
125 * As it visits fields, the variant_tsdl_keyword_sanitizer populates a
126 * "type_overrider" with TSDL-safe replacements for any variant or enumeration
127 * that uses TSDL-unsafe identifiers (reserved keywords).
129 * The type_overrider, in turn, is used by the rest of the TSDL serialization
130 * visitor (tsdl_field_visitor) to swap any TSDL-unsafe types with their
133 * The tsdl_field_visitor owns the type_overrider and only briefly shares it
134 * with the variant_tsdl_keyword_sanitizer which takes a reference to it.
136 class variant_tsdl_keyword_sanitizer
: public lttng::sessiond::trace::field_visitor
,
137 public lttng::sessiond::trace::type_visitor
{
139 using type_lookup_function
= std::function
<const lst::type
&(const lst::field_location
&)>;
141 variant_tsdl_keyword_sanitizer(tsdl::details::type_overrider
& type_overrides
,
142 type_lookup_function lookup_type
) :
143 _type_overrides
{type_overrides
}, _lookup_type(lookup_type
)
148 class _c_string_comparator
{
150 int operator()(const char *lhs
, const char *rhs
) const
152 return std::strcmp(lhs
, rhs
) < 0;
155 using unsafe_names
= std::set
<const char *, _c_string_comparator
>;
157 virtual void visit(const lst::field
& field
) override final
159 _type_overrides
.type(field
.get_type()).accept(*this);
162 virtual void visit(const lst::integer_type
& type
__attribute__((unused
))) override final
166 virtual void visit(const lst::floating_point_type
& type
__attribute__((unused
))) override final
170 virtual void visit(const lst::signed_enumeration_type
& type
__attribute__((unused
))) override final
174 virtual void visit(const lst::unsigned_enumeration_type
& type
__attribute__((unused
))) override final
178 virtual void visit(const lst::static_length_array_type
& type
__attribute__((unused
))) override final
182 virtual void visit(const lst::dynamic_length_array_type
& type
__attribute__((unused
))) override final
186 virtual void visit(const lst::static_length_blob_type
& type
__attribute__((unused
))) override final
190 virtual void visit(const lst::dynamic_length_blob_type
& type
__attribute__((unused
))) override final
194 virtual void visit(const lst::null_terminated_string_type
& type
__attribute__((unused
))) override final
198 virtual void visit(const lst::structure_type
& type
) override final
200 /* Recurse into structure attributes. */
201 for (const auto& field
: type
.fields_
) {
202 field
->accept(*this);
207 * Create a new enumeration type replacing any mapping that match, by name, the elements in `unsafe_names_found`
208 * with a TSDL-safe version. Currently, unsafe identifiers are made safe by adding
209 * a leading underscore.
211 template <typename MappingIntegerType
>
212 lst::type::cuptr
_create_sanitized_selector(
213 const lst::typed_enumeration_type
<MappingIntegerType
>& original_selector
,
214 const unsafe_names
& unsafe_names_found
)
216 auto new_mappings
= std::make_shared
<typename
lst::typed_enumeration_type
<
217 MappingIntegerType
>::mappings
>();
219 for (const auto& mapping
: *original_selector
.mappings_
) {
220 if (unsafe_names_found
.find(mapping
.name
.c_str()) ==
221 unsafe_names_found
.end()) {
222 /* Mapping is safe, simply copy it. */
223 new_mappings
->emplace_back(mapping
);
225 /* Unsafe mapping, rename it and keep the rest of its attributes. */
226 new_mappings
->emplace_back(
227 fmt::format("_{}", mapping
.name
), mapping
.range
);
231 return lttng::make_unique
<lst::typed_enumeration_type
<MappingIntegerType
>>(
232 original_selector
.alignment
, original_selector
.byte_order
,
233 original_selector
.size
, original_selector
.base_
, new_mappings
);
236 template <typename MappingIntegerType
>
237 const typename
lst::typed_enumeration_type
<MappingIntegerType
>::mapping
&
238 _find_enumeration_mapping_by_range(
239 const typename
lst::typed_enumeration_type
<MappingIntegerType
>&
241 const typename
lst::typed_enumeration_type
<
242 MappingIntegerType
>::mapping::range_t
& target_mapping_range
)
244 for (const auto& mapping
: *enumeration_type
.mappings_
) {
245 if (mapping
.range
== target_mapping_range
) {
250 LTTNG_THROW_ERROR(fmt::format(
251 "Failed to find mapping by range in enumeration while sanitizing a variant: target_mapping_range={}",
252 target_mapping_range
));
256 * Copy `original_variant`, but use the mappings of a previously-published sanitized tag
257 * to produce a TSDL-safe version of the variant.
259 template <typename MappingIntegerType
>
260 lst::type::cuptr
_create_sanitized_variant(
261 const lst::variant_type
<MappingIntegerType
>& original_variant
)
263 typename
lst::variant_type
<MappingIntegerType
>::choices new_choices
;
264 const auto& sanitized_selector
= static_cast<
265 const lst::typed_enumeration_type
<MappingIntegerType
>&>(
266 _type_overrides
.type(_lookup_type(
267 original_variant
.selector_field_location
)));
269 /* Visit variant choices to sanitize them as needed. */
270 for (const auto& choice
: original_variant
.choices_
) {
271 choice
.second
->accept(*this);
274 for (const auto& choice
: original_variant
.choices_
) {
275 const auto& sanitized_choice_type
= _type_overrides
.type(*choice
.second
);
277 new_choices
.emplace_back(
278 _find_enumeration_mapping_by_range(
279 sanitized_selector
, choice
.first
.range
),
280 sanitized_choice_type
.copy());
283 return lttng::make_unique
<lst::variant_type
<MappingIntegerType
>>(
284 original_variant
.alignment
,
285 original_variant
.selector_field_location
,
286 std::move(new_choices
));
289 template <typename MappingIntegerType
>
290 void visit_variant(const lst::variant_type
<MappingIntegerType
>& type
)
292 unsafe_names unsafe_names_found
;
293 static const std::unordered_set
<std::string
> tsdl_protected_keywords
= {
324 for (const auto& choice
: type
.choices_
) {
325 if (tsdl_protected_keywords
.find(choice
.first
.name
) != tsdl_protected_keywords
.cend()) {
326 /* Choice name is illegal, we have to rename it and its matching mapping. */
327 unsafe_names_found
.insert(choice
.first
.name
.c_str());
331 if (unsafe_names_found
.empty()) {
336 * Look-up selector field type.
338 * Since it may have been overriden previously, keep the original and overriden
339 * selector field types (which may be the same, if the original was not overriden).
341 * We work from the "overriden" selector field type to preserve any existing
342 * modifications. However, the original field type will be used to publish the new
343 * version of the type leaving only the most recent overriden type in the type
346 const auto& original_selector_type
= _lookup_type(type
.selector_field_location
);
347 const auto& overriden_selector_type
= _type_overrides
.type(original_selector_type
);
349 auto sanitized_selector_type
= _create_sanitized_selector(
350 static_cast<const lst::typed_enumeration_type
<MappingIntegerType
>&>(
351 overriden_selector_type
), unsafe_names_found
);
352 _type_overrides
.publish(original_selector_type
, std::move(sanitized_selector_type
));
354 auto sanitized_variant_type
= _create_sanitized_variant(
355 static_cast<const lst::variant_type
<MappingIntegerType
>&>(type
));
356 _type_overrides
.publish(type
, std::move(sanitized_variant_type
));
359 virtual void visit(const lst::variant_type
<lst::signed_enumeration_type::mapping::range_t::range_integer_t
>& type
) override final
364 virtual void visit(const lst::variant_type
<lst::unsigned_enumeration_type::mapping::range_t::range_integer_t
>& type
) override final
369 virtual void visit(const lst::static_length_string_type
& type
__attribute__((unused
))) override final
373 virtual void visit(const lst::dynamic_length_string_type
& type
__attribute__((unused
))) override final
377 tsdl::details::type_overrider
& _type_overrides
;
378 const type_lookup_function _lookup_type
;
381 class tsdl_field_visitor
: public lttng::sessiond::trace::field_visitor
,
382 public lttng::sessiond::trace::type_visitor
{
384 tsdl_field_visitor(const lst::abi
& abi
,
385 unsigned int indentation_level
,
386 const tsdl::details::type_overrider
& type_overrides
,
387 const nonstd::optional
<std::string
>& in_default_clock_class_name
=
389 _indentation_level
{indentation_level
},
391 _bypass_identifier_escape
{false},
392 _default_clock_class_name
{in_default_clock_class_name
?
393 in_default_clock_class_name
->c_str() :
395 _type_overrides
{type_overrides
}
399 /* Only call once. */
400 std::string
move_description()
402 return std::move(_description
);
406 virtual void visit(const lst::field
& field
) override final
409 * Hack: keep the name of the field being visited since
410 * the tracers can express sequences, variants, and arrays with an alignment
411 * constraint, which is not expressible in TSDL. To work around this limitation, an
412 * empty structure declaration is inserted when needed to express the aligment
413 * constraint. The name of this structure is generated using the field's name.
415 _current_field_name
.push(_bypass_identifier_escape
?
416 field
.name
: escape_tsdl_identifier(field
.name
));
417 _type_overrides
.type(field
.get_type()).accept(*this);
419 _description
+= _current_field_name
.top();
420 _current_field_name
.pop();
423 * Some types requires suffixes to be appended (e.g. the length of arrays
424 * and sequences, the mappings of enumerations).
426 while (!_type_suffixes
.empty()) {
427 _description
+= _type_suffixes
.front();
428 _type_suffixes
.pop();
434 virtual void visit(const lst::integer_type
& type
) override final
436 _description
+= "integer { ";
438 /* Mandatory properties (no defaults). */
439 _description
+= fmt::format("size = {size}; align = {alignment};",
440 fmt::arg("size", type
.size
),
441 fmt::arg("alignment", type
.alignment
));
443 /* Defaults to unsigned. */
444 if (type
.signedness_
== lst::integer_type::signedness::SIGNED
) {
445 _description
+= " signed = true;";
448 /* Defaults to 10. */
449 if (type
.base_
!= lst::integer_type::base::DECIMAL
) {
452 switch (type
.base_
) {
453 case lst::integer_type::base::BINARY
:
456 case lst::integer_type::base::OCTAL
:
459 case lst::integer_type::base::HEXADECIMAL
:
463 LTTNG_THROW_ERROR(fmt::format(
464 "Unexpected base encountered while serializing integer type to TSDL: base = {}",
468 _description
+= fmt::format(" base = {};", base
);
471 /* Defaults to the trace's native byte order. */
472 if (type
.byte_order
!= _trace_abi
.byte_order
) {
473 const auto byte_order_str
= type
.byte_order
== lst::byte_order::BIG_ENDIAN_
? "be" : "le";
475 _description
+= fmt::format(" byte_order = {};", byte_order_str
);
478 if (_current_integer_encoding_override
) {
479 const char *encoding_str
;
481 switch (*_current_integer_encoding_override
) {
482 case lst::string_type::encoding::ASCII
:
483 encoding_str
= "ASCII";
485 case lst::string_type::encoding::UTF8
:
486 encoding_str
= "UTF8";
489 LTTNG_THROW_ERROR(fmt::format(
490 "Unexpected encoding encountered while serializing integer type to TSDL: encoding = {}",
491 (int) *_current_integer_encoding_override
));
494 _description
+= fmt::format(" encoding = {};", encoding_str
);
495 _current_integer_encoding_override
.reset();
498 if (std::find(type
.roles_
.begin(), type
.roles_
.end(),
499 lst::integer_type::role::DEFAULT_CLOCK_TIMESTAMP
) !=
501 std::find(type
.roles_
.begin(), type
.roles_
.end(),
502 lst::integer_type::role::
503 PACKET_END_DEFAULT_CLOCK_TIMESTAMP
) !=
505 LTTNG_ASSERT(_default_clock_class_name
);
506 _description
+= fmt::format(
507 " map = clock.{}.value;", _default_clock_class_name
);
510 _description
+= " }";
513 virtual void visit(const lst::floating_point_type
& type
) override final
515 _description
+= fmt::format(
516 "floating_point {{ align = {alignment}; mant_dig = {mantissa_digits}; exp_dig = {exponent_digits};",
517 fmt::arg("alignment", type
.alignment
),
518 fmt::arg("mantissa_digits", type
.mantissa_digits
),
519 fmt::arg("exponent_digits", type
.exponent_digits
));
521 /* Defaults to the trace's native byte order. */
522 if (type
.byte_order
!= _trace_abi
.byte_order
) {
523 const auto byte_order_str
= type
.byte_order
== lst::byte_order::BIG_ENDIAN_
? "be" : "le";
525 _description
+= fmt::format(" byte_order = {};", byte_order_str
);
528 _description
+= " }";
531 template <class EnumerationType
>
532 void visit_enumeration(const EnumerationType
& type
)
534 /* name follows, when applicable. */
535 _description
+= "enum : ";
537 visit(static_cast<const lst::integer_type
&>(type
));
538 _description
+= " {\n";
540 const auto mappings_indentation_level
= _indentation_level
+ 1;
542 bool first_mapping
= true;
543 for (const auto& mapping
: *type
.mappings_
) {
544 if (!first_mapping
) {
545 _description
+= ",\n";
548 _description
.resize(_description
.size() + mappings_indentation_level
, '\t');
549 if (mapping
.range
.begin
== mapping
.range
.end
) {
550 _description
+= fmt::format(
551 "\"{mapping_name}\" = {mapping_value}",
552 fmt::arg("mapping_name", mapping
.name
),
553 fmt::arg("mapping_value", mapping
.range
.begin
));
555 _description
+= fmt::format(
556 "\"{mapping_name}\" = {mapping_range_begin} ... {mapping_range_end}",
557 fmt::arg("mapping_name", mapping
.name
),
558 fmt::arg("mapping_range_begin",
559 mapping
.range
.begin
),
560 fmt::arg("mapping_range_end", mapping
.range
.end
));
563 first_mapping
= false;
566 _description
+= "\n";
567 _description
.resize(_description
.size() + _indentation_level
, '\t');
571 virtual void visit(const lst::signed_enumeration_type
& type
) override final
573 visit_enumeration(type
);
576 virtual void visit(const lst::unsigned_enumeration_type
& type
) override final
578 visit_enumeration(type
);
581 virtual void visit(const lst::static_length_array_type
& type
) override final
583 if (type
.alignment
!= 0) {
584 LTTNG_ASSERT(_current_field_name
.size() > 0);
585 _description
+= fmt::format(
586 "struct {{ }} align({alignment}) {field_name}_padding;\n",
587 fmt::arg("alignment", type
.alignment
),
588 fmt::arg("field_name", _current_field_name
.top()));
589 _description
.resize(_description
.size() + _indentation_level
, '\t');
592 type
.element_type
->accept(*this);
593 _type_suffixes
.emplace(fmt::format("[{}]", type
.length
));
596 virtual void visit(const lst::dynamic_length_array_type
& type
) override final
598 if (type
.alignment
!= 0) {
600 * Note that this doesn't support nested sequences. For
601 * the moment, tracers can't express those. However, we
602 * could wrap nested sequences in structures, which
603 * would allow us to express alignment constraints.
605 LTTNG_ASSERT(_current_field_name
.size() > 0);
606 _description
+= fmt::format(
607 "struct {{ }} align({alignment}) {field_name}_padding;\n",
608 fmt::arg("alignment", type
.alignment
),
609 fmt::arg("field_name", _current_field_name
.top()));
610 _description
.resize(_description
.size() + _indentation_level
, '\t');
613 type
.element_type
->accept(*this);
614 _type_suffixes
.emplace(fmt::format("[{}]",
615 _bypass_identifier_escape
?
616 *(type
.length_field_location
.elements_
.end() - 1) :
617 escape_tsdl_identifier(*(type
.length_field_location
.elements_
.end() - 1))));
620 virtual void visit(const lst::static_length_blob_type
& type
) override final
622 /* This type doesn't exist in CTF 1.x, express it as a static length array of uint8_t. */
623 std::unique_ptr
<const lst::type
> uint8_element
= lttng::make_unique
<lst::integer_type
>(8,
624 _trace_abi
.byte_order
, 8, lst::integer_type::signedness::UNSIGNED
,
625 lst::integer_type::base::HEXADECIMAL
);
626 const auto array
= lttng::make_unique
<lst::static_length_array_type
>(
627 type
.alignment
, std::move(uint8_element
), type
.length_bytes
);
632 virtual void visit(const lst::dynamic_length_blob_type
& type
) override final
634 /* This type doesn't exist in CTF 1.x, express it as a dynamic length array of uint8_t. */
635 std::unique_ptr
<const lst::type
> uint8_element
= lttng::make_unique
<lst::integer_type
>(0,
636 _trace_abi
.byte_order
, 8, lst::integer_type::signedness::UNSIGNED
,
637 lst::integer_type::base::HEXADECIMAL
);
638 const auto array
= lttng::make_unique
<lst::dynamic_length_array_type
>(
639 type
.alignment
, std::move(uint8_element
), type
.length_field_location
);
644 virtual void visit(const lst::null_terminated_string_type
& type
) override final
646 /* Defaults to UTF-8. */
647 if (type
.encoding_
== lst::null_terminated_string_type::encoding::ASCII
) {
648 _description
+= "string { encoding = ASCII }";
650 _description
+= "string";
654 virtual void visit(const lst::structure_type
& type
) override final
656 _indentation_level
++;
657 _description
+= "struct {";
659 const auto previous_bypass_identifier_escape
= _bypass_identifier_escape
;
660 _bypass_identifier_escape
= false;
661 for (const auto& field
: type
.fields_
) {
662 _description
+= "\n";
663 _description
.resize(_description
.size() + _indentation_level
, '\t');
664 field
->accept(*this);
667 _bypass_identifier_escape
= previous_bypass_identifier_escape
;
669 _indentation_level
--;
670 if (type
.fields_
.size() != 0) {
671 _description
+= "\n";
672 _description
.resize(_description
.size() + _indentation_level
, '\t');
678 template <class MappingIntegerType
>
679 void visit_variant(const lst::variant_type
<MappingIntegerType
>& type
)
681 if (type
.alignment
!= 0) {
682 LTTNG_ASSERT(_current_field_name
.size() > 0);
683 _description
+= fmt::format(
684 "struct {{ }} align({alignment}) {field_name}_padding;\n",
685 fmt::arg("alignment", type
.alignment
),
686 fmt::arg("field_name", _current_field_name
.top()));
687 _description
.resize(_description
.size() + _indentation_level
, '\t');
690 _indentation_level
++;
691 _description
+= fmt::format("variant <{}> {{\n",
692 _bypass_identifier_escape
?
693 *(type
.selector_field_location
.elements_
.end() - 1) :
694 escape_tsdl_identifier(*(type
.selector_field_location
.elements_
.end() - 1)));
697 * The CTF 1.8 specification only recommends that implementations ignore
698 * leading underscores in field names. Both babeltrace 1 and 2 expect the
699 * variant choice and enumeration mapping name to match perfectly. Given that we
700 * don't have access to the tag in this context, we have to assume they match.
702 const auto previous_bypass_identifier_escape
= _bypass_identifier_escape
;
703 _bypass_identifier_escape
= true;
704 for (const auto& field
: type
.choices_
) {
705 _description
.resize(_description
.size() + _indentation_level
, '\t');
706 field
.second
->accept(*this);
707 _description
+= fmt::format(" {};\n", field
.first
.name
);
710 _bypass_identifier_escape
= previous_bypass_identifier_escape
;
712 _indentation_level
--;
713 _description
.resize(_description
.size() + _indentation_level
, '\t');
717 virtual void visit(const lst::variant_type
<lst::signed_enumeration_type::mapping::range_t::range_integer_t
>& type
) override final
722 virtual void visit(const lst::variant_type
<lst::unsigned_enumeration_type::mapping::range_t::range_integer_t
>& type
) override final
727 lst::type::cuptr
create_character_type(enum lst::string_type::encoding encoding
)
729 _current_integer_encoding_override
= encoding
;
730 return lttng::make_unique
<lst::integer_type
>(8, _trace_abi
.byte_order
, 8,
731 lst::integer_type::signedness::UNSIGNED
,
732 lst::integer_type::base::DECIMAL
);
735 virtual void visit(const lst::static_length_string_type
& type
) override final
738 * TSDL expresses static-length strings as arrays of 8-bit integer with
739 * an encoding specified.
741 const auto char_array
= lttng::make_unique
<lst::static_length_array_type
>(
742 type
.alignment
, create_character_type(type
.encoding_
), type
.length
);
747 virtual void visit(const lst::dynamic_length_string_type
& type
) override final
750 * TSDL expresses dynamic-length strings as arrays of 8-bit integer with
751 * an encoding specified.
753 const auto char_sequence
= lttng::make_unique
<lst::dynamic_length_array_type
>(
754 type
.alignment
, create_character_type(type
.encoding_
),
755 type
.length_field_location
);
757 visit(*char_sequence
);
760 std::stack
<std::string
> _current_field_name
;
762 * Encoding to specify for the next serialized integer type.
763 * Since the integer_type does not allow an encoding to be specified (it is a TSDL-specific
764 * concept), this attribute is used when expressing static or dynamic length strings as
765 * arrays/sequences of bytes with an encoding.
767 nonstd::optional
<enum lst::string_type::encoding
> _current_integer_encoding_override
;
769 unsigned int _indentation_level
;
770 const lst::abi
& _trace_abi
;
772 std::queue
<std::string
> _type_suffixes
;
774 /* Description in TSDL format. */
775 std::string _description
;
777 bool _bypass_identifier_escape
;
778 const char *_default_clock_class_name
;
779 const tsdl::details::type_overrider
& _type_overrides
;
782 class tsdl_trace_environment_visitor
: public lst::trace_class_environment_visitor
{
784 tsdl_trace_environment_visitor() : _environment
{"env {\n"}
788 virtual void visit(const lst::environment_field
<int64_t>& field
) override
790 _environment
+= fmt::format(" {} = {};\n", field
.name
, field
.value
);
793 virtual void visit(const lst::environment_field
<const char *>& field
) override
795 _environment
+= fmt::format(" {} = \"{}\";\n", field
.name
,
796 escape_tsdl_env_string_value(field
.value
));
799 /* Only call once. */
800 std::string
move_description()
802 _environment
+= "};\n\n";
803 return std::move(_environment
);
807 std::string _environment
;
811 tsdl::trace_class_visitor::trace_class_visitor(const lst::abi
& trace_abi
,
812 tsdl::append_metadata_fragment_function append_metadata_fragment
) :
813 _trace_abi
{trace_abi
},
814 _append_metadata_fragment(append_metadata_fragment
)
818 void tsdl::trace_class_visitor::append_metadata_fragment(const std::string
& fragment
) const
820 _append_metadata_fragment(fragment
);
823 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::trace_class
& trace_class
)
825 /* Ensure this instance is not used against multiple trace classes. */
826 LTTNG_ASSERT(!_current_trace_class
|| _current_trace_class
== &trace_class
);
827 _current_trace_class
= &trace_class
;
829 tsdl_field_visitor packet_header_visitor
{trace_class
.abi
, 1, _sanitized_types_overrides
};
831 trace_class
.packet_header()->accept(packet_header_visitor
);
833 /* Declare type aliases, trace class, and packet header. */
834 auto trace_class_tsdl
= fmt::format(
835 "/* CTF {ctf_major}.{ctf_minor} */\n\n"
837 " major = {ctf_major};\n"
838 " minor = {ctf_minor};\n"
839 " uuid = \"{uuid}\";\n"
840 " byte_order = {byte_order};\n"
841 " packet.header := {packet_header_layout};\n"
843 fmt::arg("ctf_major", ctf_spec_major
),
844 fmt::arg("ctf_minor", ctf_spec_minor
),
845 fmt::arg("uint8_t_alignment", trace_class
.abi
.uint8_t_alignment
),
846 fmt::arg("uint16_t_alignment", trace_class
.abi
.uint16_t_alignment
),
847 fmt::arg("uint32_t_alignment", trace_class
.abi
.uint32_t_alignment
),
848 fmt::arg("uint64_t_alignment", trace_class
.abi
.uint64_t_alignment
),
849 fmt::arg("long_alignment", trace_class
.abi
.long_alignment
),
850 fmt::arg("long_size", trace_class
.abi
.long_alignment
),
851 fmt::arg("bits_per_long", trace_class
.abi
.bits_per_long
),
852 fmt::arg("uuid", lttng::utils::uuid_to_str(trace_class
.uuid
)),
853 fmt::arg("byte_order",
854 trace_class
.abi
.byte_order
== lst::byte_order::BIG_ENDIAN_
? "be" : "le"),
855 fmt::arg("packet_header_layout", packet_header_visitor
.move_description()));
857 /* Declare trace scope and type aliases. */
858 append_metadata_fragment(trace_class_tsdl
);
860 tsdl_trace_environment_visitor environment_visitor
;
861 trace_class
.accept(environment_visitor
);
862 append_metadata_fragment(environment_visitor
.move_description());
865 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::clock_class
& clock_class
)
867 auto uuid_str
= clock_class
.uuid
?
868 fmt::format(" uuid = \"{}\";\n",
869 lttng::utils::uuid_to_str(*clock_class
.uuid
)) :
872 /* Assumes a single clock that maps to specific stream class fields/roles. */
873 auto clock_class_str
= fmt::format(
875 " name = \"{name}\";\n"
878 " description = \"{description}\";\n"
879 " freq = {frequency};\n"
880 " offset = {offset};\n"
883 fmt::arg("name", clock_class
.name
),
884 fmt::arg("uuid", uuid_str
),
885 fmt::arg("description", clock_class
.description
),
886 fmt::arg("frequency", clock_class
.frequency
),
887 fmt::arg("offset", clock_class
.offset
));
889 append_metadata_fragment(clock_class_str
);
892 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::stream_class
& stream_class
)
894 _current_stream_class
= &stream_class
;
895 const auto clear_stream_class_on_exit
= lttng::make_scope_exit(
896 [this]() noexcept
{ _current_stream_class
= nullptr; });
898 auto stream_class_str
= fmt::format("stream {{\n"
899 " id = {};\n", stream_class
.id
);
900 variant_tsdl_keyword_sanitizer
variant_sanitizer(_sanitized_types_overrides
,
901 [this](const lttng::sessiond::trace::field_location
& location
)
902 -> const lst::type
& {
903 return _lookup_field_type(location
);
906 const auto *event_header
= stream_class
.event_header();
908 tsdl_field_visitor event_header_visitor
{_trace_abi
, 1, _sanitized_types_overrides
,
909 stream_class
.default_clock_class_name
};
911 event_header
->accept(variant_sanitizer
);
912 event_header
->accept(event_header_visitor
);
913 stream_class_str
+= fmt::format(" event.header := {};\n",
914 event_header_visitor
.move_description());
917 const auto *packet_context
= stream_class
.packet_context();
918 if (packet_context
) {
919 tsdl_field_visitor packet_context_visitor
{_trace_abi
, 1, _sanitized_types_overrides
,
920 stream_class
.default_clock_class_name
};
922 packet_context
->accept(variant_sanitizer
);
923 packet_context
->accept(packet_context_visitor
);
924 stream_class_str
+= fmt::format(" packet.context := {};\n",
925 packet_context_visitor
.move_description());
928 const auto *event_context
= stream_class
.event_context();
930 tsdl_field_visitor event_context_visitor
{_trace_abi
, 1, _sanitized_types_overrides
};
932 event_context
->accept(variant_sanitizer
);
933 event_context
->accept(event_context_visitor
);
934 stream_class_str
+= fmt::format(" event.context := {};\n",
935 event_context_visitor
.move_description());
938 stream_class_str
+= "};\n\n";
940 append_metadata_fragment(stream_class_str
);
943 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::event_class
& event_class
)
945 _current_event_class
= &event_class
;
946 const auto clear_event_class_on_exit
= lttng::make_scope_exit(
947 [this]() noexcept
{ _current_event_class
= nullptr; });
949 auto event_class_str
= fmt::format("event {{\n"
950 " name = \"{name}\";\n"
952 " stream_id = {stream_class_id};\n"
953 " loglevel = {log_level};\n",
954 fmt::arg("name", event_class
.name
),
955 fmt::arg("id", event_class
.id
),
956 fmt::arg("stream_class_id", event_class
.stream_class_id
),
957 fmt::arg("log_level", event_class
.log_level
));
959 if (event_class
.model_emf_uri
) {
960 event_class_str
+= fmt::format(
961 " model.emf.uri = \"{}\";\n", *event_class
.model_emf_uri
);
964 tsdl_field_visitor payload_visitor
{_trace_abi
, 1, _sanitized_types_overrides
};
965 variant_tsdl_keyword_sanitizer
variant_sanitizer(_sanitized_types_overrides
,
966 [this](const lttng::sessiond::trace::field_location
& location
)
967 -> const lst::type
& {
968 return _lookup_field_type(location
);
971 event_class
.payload
->accept(variant_sanitizer
);
972 event_class
.payload
->accept(payload_visitor
);
974 event_class_str
+= fmt::format(
975 " fields := {};\n}};\n\n", payload_visitor
.move_description());
977 append_metadata_fragment(event_class_str
);
980 void tsdl::details::type_overrider::publish(
981 const lttng::sessiond::trace::type
& original_type
,
982 lttng::sessiond::trace::type::cuptr new_type_override
)
984 auto current_override
= _overriden_types
.find(&original_type
);
986 if (current_override
!= _overriden_types
.end()) {
987 current_override
->second
= std::move(new_type_override
);
989 _overriden_types
.insert(std::make_pair(&original_type
, std::move(new_type_override
)));
993 const lst::type
& tsdl::details::type_overrider::type(
994 const lttng::sessiond::trace::type
& original
) const noexcept
996 const auto result
= _overriden_types
.find(&original
);
998 if (result
!= _overriden_types
.end()) {
999 /* Provide the overriden type. */
1000 return *result
->second
;
1003 /* Pass the original type through. */
1008 const lttng::sessiond::trace::type
& lookup_type_from_root_type(
1009 const lttng::sessiond::trace::type
& root_type
,
1010 const lttng::sessiond::trace::field_location
& field_location
)
1012 const auto *type
= &root_type
;
1014 for (const auto& location_element
: field_location
.elements_
) {
1015 /* Only structures can be traversed. */
1016 const auto *struct_type
= dynamic_cast<const lst::structure_type
*>(type
);
1019 * Traverse the type by following the field location path.
1021 * While field paths are assumed to have been validated before-hand,
1022 * a dynamic cast is performed here as an additional precaution
1023 * since none of this is performance-critical; it can be removed
1027 LTTNG_THROW_ERROR(fmt::format(
1028 "Encountered a type that is not a structure while traversing field location: field-location=`{}`",
1032 const auto field_found_it
= std::find_if(struct_type
->fields_
.cbegin(),
1033 struct_type
->fields_
.cend(),
1034 [&location_element
](const lst::field::cuptr
& struct_field
) {
1035 return struct_field
->name
== location_element
;
1038 if (field_found_it
== struct_type
->fields_
.cend()) {
1039 LTTNG_THROW_ERROR(fmt::format(
1040 "Failed to find field using field location: field-name:=`{field_name}`, field-location=`{field_location}`",
1041 fmt::arg("field_location", field_location
),
1042 fmt::arg("field_name", location_element
)));
1045 type
= &(*field_found_it
)->get_type();
1050 } /* anonymous namespace. */
1053 * The trace hierarchy is assumed to have been validated on creation.
1054 * This function can only fail due to a validation error, hence
1055 * why it throws on any unexpected/invalid field location.
1057 * Does not return an overriden field type; it returns the original field type
1058 * as found in the trace hierarchy.
1060 const lttng::sessiond::trace::type
& lttng::sessiond::tsdl::trace_class_visitor::_lookup_field_type(
1061 const lttng::sessiond::trace::field_location
& location
) const
1063 /* Validate the look-up is happening in a valid visit context. */
1064 switch (location
.root_
) {
1065 case lst::field_location::root::EVENT_RECORD_HEADER
:
1066 case lst::field_location::root::EVENT_RECORD_PAYLOAD
:
1067 if (!_current_event_class
) {
1069 "Field type look-up failure: no current event class in visitor's context");
1072 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT
:
1073 case lst::field_location::root::PACKET_CONTEXT
:
1074 if (!_current_stream_class
) {
1076 "Field type look-up failure: no current stream class in visitor's context");
1079 case lst::field_location::root::PACKET_HEADER
:
1080 if (!_current_trace_class
) {
1082 "Field type look-up failure: no current trace class in visitor's context");
1086 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT
:
1087 LTTNG_THROW_UNSUPPORTED_ERROR(
1088 "Field type look-up failure: event-record specific contexts are not supported");
1090 LTTNG_THROW_UNSUPPORTED_ERROR(
1091 "Field type look-up failure: unknown field location root");
1094 switch (location
.root_
) {
1095 case lst::field_location::root::PACKET_HEADER
:
1096 return lookup_type_from_root_type(
1097 *_current_trace_class
->packet_header(), location
);
1098 case lst::field_location::root::PACKET_CONTEXT
:
1099 return lookup_type_from_root_type(
1100 *_current_stream_class
->packet_context(), location
);
1101 case lst::field_location::root::EVENT_RECORD_HEADER
:
1102 return lookup_type_from_root_type(
1103 *_current_stream_class
->event_header(), location
);
1104 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT
:
1105 return lookup_type_from_root_type(
1106 *_current_stream_class
->event_context(), location
);
1107 case lst::field_location::root::EVENT_RECORD_PAYLOAD
:
1108 return lookup_type_from_root_type(
1109 *_current_event_class
->payload
, location
);
1110 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT
:
1112 /* Unreachable as it was checked before. */