2 * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
4 * SPDX-License-Identifier: GPL-2.0-only
8 #include "clock-class.hpp"
9 #include "tsdl-trace-class-visitor.hpp"
11 #include <common/exception.hpp>
12 #include <common/format.hpp>
13 #include <common/make-unique.hpp>
14 #include <common/scope-exit.hpp>
15 #include <common/uuid.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
= { "stream_id",
51 "stream_instance_id" };
54 * A previous implementation always prepended '_' to the identifiers in order to
55 * side-step the problem of escaping TSDL keywords and ensuring identifiers
56 * started with an alphabetic character.
58 * Changing this behaviour to a smarter algorithm would break readers that have
59 * come to expect this initial underscore.
61 std::string
escape_tsdl_identifier(const std::string
& original_identifier
)
63 if (original_identifier
.size() == 0) {
64 LTTNG_THROW_ERROR("Invalid 0-length identifier used in trace description");
67 if (safe_tsdl_identifiers
.find(original_identifier
) != safe_tsdl_identifiers
.end()) {
68 return original_identifier
;
71 std::string new_identifier
;
72 /* Optimisticly assume most identifiers are valid and allocate the same length. */
73 new_identifier
.reserve(original_identifier
.size());
76 /* Replace illegal characters by '_'. */
77 std::locale c_locale
{ "C" };
78 for (const auto current_char
: original_identifier
) {
79 if (!std::isalnum(current_char
, c_locale
) && current_char
!= '_') {
80 new_identifier
+= '_';
82 new_identifier
+= current_char
;
86 return new_identifier
;
89 std::string
escape_tsdl_env_string_value(const std::string
& original_string
)
91 std::string escaped_string
;
93 escaped_string
.reserve(original_string
.size());
95 for (const auto c
: original_string
) {
98 escaped_string
+= "\\n";
101 escaped_string
+= "\\\\";
104 escaped_string
+= "\"";
112 return escaped_string
;
116 * Variants produced by LTTng-UST contain TSDL-unsafe names. A variant/selector
117 * sanitization pass is performed before serializing a trace class hierarchy to
120 * The variant_tsdl_keyword_sanitizer visitor is used to visit field before it
121 * is handed-over to the actual TSDL-producing visitor.
123 * As it visits fields, the variant_tsdl_keyword_sanitizer populates a
124 * "type_overrider" with TSDL-safe replacements for any variant or enumeration
125 * that uses TSDL-unsafe identifiers (reserved keywords).
127 * The type_overrider, in turn, is used by the rest of the TSDL serialization
128 * visitor (tsdl_field_visitor) to swap any TSDL-unsafe types with their
131 * The tsdl_field_visitor owns the type_overrider and only briefly shares it
132 * with the variant_tsdl_keyword_sanitizer which takes a reference to it.
134 class variant_tsdl_keyword_sanitizer
: public lttng::sessiond::trace::field_visitor
,
135 public lttng::sessiond::trace::type_visitor
{
137 using type_lookup_function
= std::function
<const lst::type
&(const lst::field_location
&)>;
139 variant_tsdl_keyword_sanitizer(tsdl::details::type_overrider
& type_overrides
,
140 type_lookup_function lookup_type
) :
141 _type_overrides
{ type_overrides
}, _lookup_type(lookup_type
)
146 class _c_string_comparator
{
148 int operator()(const char *lhs
, const char *rhs
) const
150 return std::strcmp(lhs
, rhs
) < 0;
153 using unsafe_names
= std::set
<const char *, _c_string_comparator
>;
155 virtual void visit(const lst::field
& field
) override final
157 _type_overrides
.type(field
.get_type()).accept(*this);
160 virtual void visit(const lst::integer_type
& type
__attribute__((unused
))) override final
164 virtual void visit(const lst::floating_point_type
& type
165 __attribute__((unused
))) override final
169 virtual void visit(const lst::signed_enumeration_type
& type
170 __attribute__((unused
))) override final
174 virtual void visit(const lst::unsigned_enumeration_type
& type
175 __attribute__((unused
))) override final
179 virtual void visit(const lst::static_length_array_type
& type
180 __attribute__((unused
))) override final
184 virtual void visit(const lst::dynamic_length_array_type
& type
185 __attribute__((unused
))) override final
189 virtual void visit(const lst::static_length_blob_type
& type
190 __attribute__((unused
))) override final
194 virtual void visit(const lst::dynamic_length_blob_type
& type
195 __attribute__((unused
))) override final
199 virtual void visit(const lst::null_terminated_string_type
& type
200 __attribute__((unused
))) override final
204 virtual void visit(const lst::structure_type
& type
) override final
206 /* Recurse into structure attributes. */
207 for (const auto& field
: type
.fields_
) {
208 field
->accept(*this);
213 * Create a new enumeration type replacing any mapping that match, by name, the elements in
214 * `unsafe_names_found` with a TSDL-safe version. Currently, unsafe identifiers are made
215 * safe by adding a leading underscore.
217 template <typename MappingIntegerType
>
218 lst::type::cuptr
_create_sanitized_selector(
219 const lst::typed_enumeration_type
<MappingIntegerType
>& original_selector
,
220 const unsafe_names
& unsafe_names_found
)
222 auto new_mappings
= std::make_shared
<
223 typename
lst::typed_enumeration_type
<MappingIntegerType
>::mappings
>();
225 for (const auto& mapping
: *original_selector
.mappings_
) {
226 if (unsafe_names_found
.find(mapping
.name
.c_str()) ==
227 unsafe_names_found
.end()) {
228 /* Mapping is safe, simply copy it. */
229 new_mappings
->emplace_back(mapping
);
231 /* Unsafe mapping, rename it and keep the rest of its attributes. */
232 new_mappings
->emplace_back(fmt::format("_{}", mapping
.name
),
237 return lttng::make_unique
<lst::typed_enumeration_type
<MappingIntegerType
>>(
238 original_selector
.alignment
,
239 original_selector
.byte_order
,
240 original_selector
.size
,
241 original_selector
.base_
,
245 template <typename MappingIntegerType
>
246 const typename
lst::typed_enumeration_type
<MappingIntegerType
>::mapping
&
247 _find_enumeration_mapping_by_range(
248 const typename
lst::typed_enumeration_type
<MappingIntegerType
>& enumeration_type
,
249 const typename
lst::typed_enumeration_type
<MappingIntegerType
>::mapping::range_t
&
250 target_mapping_range
)
252 for (const auto& mapping
: *enumeration_type
.mappings_
) {
253 if (mapping
.range
== target_mapping_range
) {
258 LTTNG_THROW_ERROR(fmt::format(
259 "Failed to find mapping by range in enumeration while sanitizing a variant: target_mapping_range={}",
260 target_mapping_range
));
264 * Copy `original_variant`, but use the mappings of a previously-published sanitized tag
265 * to produce a TSDL-safe version of the variant.
267 template <typename MappingIntegerType
>
269 _create_sanitized_variant(const lst::variant_type
<MappingIntegerType
>& original_variant
)
271 typename
lst::variant_type
<MappingIntegerType
>::choices new_choices
;
272 const auto& sanitized_selector
=
273 static_cast<const lst::typed_enumeration_type
<MappingIntegerType
>&>(
274 _type_overrides
.type(
275 _lookup_type(original_variant
.selector_field_location
)));
277 /* Visit variant choices to sanitize them as needed. */
278 for (const auto& choice
: original_variant
.choices_
) {
279 choice
.second
->accept(*this);
282 for (const auto& choice
: original_variant
.choices_
) {
283 const auto& sanitized_choice_type
= _type_overrides
.type(*choice
.second
);
285 new_choices
.emplace_back(_find_enumeration_mapping_by_range(
286 sanitized_selector
, choice
.first
.range
),
287 sanitized_choice_type
.copy());
290 return lttng::make_unique
<lst::variant_type
<MappingIntegerType
>>(
291 original_variant
.alignment
,
292 original_variant
.selector_field_location
,
293 std::move(new_choices
));
296 template <typename MappingIntegerType
>
297 void visit_variant(const lst::variant_type
<MappingIntegerType
>& type
)
299 unsafe_names unsafe_names_found
;
300 static const std::unordered_set
<std::string
> tsdl_protected_keywords
= {
301 "align", "callsite", "const", "char", "clock", "double",
302 "enum", "env", "event", "floating_point", "float", "integer",
303 "int", "long", "short", "signed", "stream", "string",
304 "struct", "trace", "typealias", "typedef", "unsigned", "variant",
305 "void", "_Bool", "_Complex", "_Imaginary",
308 for (const auto& choice
: type
.choices_
) {
309 if (tsdl_protected_keywords
.find(choice
.first
.name
) !=
310 tsdl_protected_keywords
.cend()) {
311 /* Choice name is illegal, we have to rename it and its matching
313 unsafe_names_found
.insert(choice
.first
.name
.c_str());
317 if (unsafe_names_found
.empty()) {
322 * Look-up selector field type.
324 * Since it may have been overriden previously, keep the original and overriden
325 * selector field types (which may be the same, if the original was not overriden).
327 * We work from the "overriden" selector field type to preserve any existing
328 * modifications. However, the original field type will be used to publish the new
329 * version of the type leaving only the most recent overriden type in the type
332 const auto& original_selector_type
= _lookup_type(type
.selector_field_location
);
333 const auto& overriden_selector_type
= _type_overrides
.type(original_selector_type
);
335 auto sanitized_selector_type
= _create_sanitized_selector(
336 static_cast<const lst::typed_enumeration_type
<MappingIntegerType
>&>(
337 overriden_selector_type
),
339 _type_overrides
.publish(original_selector_type
, std::move(sanitized_selector_type
));
341 auto sanitized_variant_type
= _create_sanitized_variant(
342 static_cast<const lst::variant_type
<MappingIntegerType
>&>(type
));
343 _type_overrides
.publish(type
, std::move(sanitized_variant_type
));
346 virtual void visit(const lst::variant_type
<
347 lst::signed_enumeration_type::mapping::range_t::range_integer_t
>& type
)
353 virtual void visit(const lst::variant_type
<
354 lst::unsigned_enumeration_type::mapping::range_t::range_integer_t
>& type
)
360 virtual void visit(const lst::static_length_string_type
& type
361 __attribute__((unused
))) override final
365 virtual void visit(const lst::dynamic_length_string_type
& type
366 __attribute__((unused
))) override final
370 tsdl::details::type_overrider
& _type_overrides
;
371 const type_lookup_function _lookup_type
;
374 class tsdl_field_visitor
: public lttng::sessiond::trace::field_visitor
,
375 public lttng::sessiond::trace::type_visitor
{
377 tsdl_field_visitor(const lst::abi
& abi
,
378 unsigned int indentation_level
,
379 const tsdl::details::type_overrider
& type_overrides
,
380 const nonstd::optional
<std::string
>& in_default_clock_class_name
=
382 _indentation_level
{ indentation_level
},
384 _bypass_identifier_escape
{ false },
385 _default_clock_class_name
{ in_default_clock_class_name
?
386 in_default_clock_class_name
->c_str() :
388 _type_overrides
{ type_overrides
}
392 /* Only call once. */
393 std::string
move_description()
395 return std::move(_description
);
399 virtual void visit(const lst::field
& field
) override final
402 * Hack: keep the name of the field being visited since
403 * the tracers can express sequences, variants, and arrays with an alignment
404 * constraint, which is not expressible in TSDL. To work around this limitation, an
405 * empty structure declaration is inserted when needed to express the aligment
406 * constraint. The name of this structure is generated using the field's name.
408 _current_field_name
.push(_bypass_identifier_escape
?
410 escape_tsdl_identifier(field
.name
));
411 _type_overrides
.type(field
.get_type()).accept(*this);
413 _description
+= _current_field_name
.top();
414 _current_field_name
.pop();
417 * Some types requires suffixes to be appended (e.g. the length of arrays
418 * and sequences, the mappings of enumerations).
420 while (!_type_suffixes
.empty()) {
421 _description
+= _type_suffixes
.front();
422 _type_suffixes
.pop();
428 virtual void visit(const lst::integer_type
& type
) override final
430 _description
+= "integer { ";
432 /* Mandatory properties (no defaults). */
433 _description
+= fmt::format("size = {size}; align = {alignment};",
434 fmt::arg("size", type
.size
),
435 fmt::arg("alignment", type
.alignment
));
437 /* Defaults to unsigned. */
438 if (type
.signedness_
== lst::integer_type::signedness::SIGNED
) {
439 _description
+= " signed = true;";
442 /* Defaults to 10. */
443 if (type
.base_
!= lst::integer_type::base::DECIMAL
) {
446 switch (type
.base_
) {
447 case lst::integer_type::base::BINARY
:
450 case lst::integer_type::base::OCTAL
:
453 case lst::integer_type::base::HEXADECIMAL
:
457 LTTNG_THROW_ERROR(fmt::format(
458 "Unexpected base encountered while serializing integer type to TSDL: base = {}",
462 _description
+= fmt::format(" base = {};", base
);
465 /* Defaults to the trace's native byte order. */
466 if (type
.byte_order
!= _trace_abi
.byte_order
) {
467 const auto byte_order_str
=
468 type
.byte_order
== lst::byte_order::BIG_ENDIAN_
? "be" : "le";
470 _description
+= fmt::format(" byte_order = {};", byte_order_str
);
473 if (_current_integer_encoding_override
) {
474 const char *encoding_str
;
476 switch (*_current_integer_encoding_override
) {
477 case lst::string_type::encoding::ASCII
:
478 encoding_str
= "ASCII";
480 case lst::string_type::encoding::UTF8
:
481 encoding_str
= "UTF8";
484 LTTNG_THROW_ERROR(fmt::format(
485 "Unexpected encoding encountered while serializing integer type to TSDL: encoding = {}",
486 (int) *_current_integer_encoding_override
));
489 _description
+= fmt::format(" encoding = {};", encoding_str
);
490 _current_integer_encoding_override
.reset();
493 if (std::find(type
.roles_
.begin(),
495 lst::integer_type::role::DEFAULT_CLOCK_TIMESTAMP
) !=
497 std::find(type
.roles_
.begin(),
499 lst::integer_type::role::PACKET_END_DEFAULT_CLOCK_TIMESTAMP
) !=
501 LTTNG_ASSERT(_default_clock_class_name
);
503 fmt::format(" map = clock.{}.value;", _default_clock_class_name
);
506 _description
+= " }";
509 virtual void visit(const lst::floating_point_type
& type
) override final
511 _description
+= fmt::format(
512 "floating_point {{ align = {alignment}; mant_dig = {mantissa_digits}; exp_dig = {exponent_digits};",
513 fmt::arg("alignment", type
.alignment
),
514 fmt::arg("mantissa_digits", type
.mantissa_digits
),
515 fmt::arg("exponent_digits", type
.exponent_digits
));
517 /* Defaults to the trace's native byte order. */
518 if (type
.byte_order
!= _trace_abi
.byte_order
) {
519 const auto byte_order_str
=
520 type
.byte_order
== lst::byte_order::BIG_ENDIAN_
? "be" : "le";
522 _description
+= fmt::format(" byte_order = {};", byte_order_str
);
525 _description
+= " }";
528 template <class EnumerationType
>
529 void visit_enumeration(const EnumerationType
& type
)
531 /* name follows, when applicable. */
532 _description
+= "enum : ";
534 visit(static_cast<const lst::integer_type
&>(type
));
535 _description
+= " {\n";
537 const auto mappings_indentation_level
= _indentation_level
+ 1;
539 bool first_mapping
= true;
540 for (const auto& mapping
: *type
.mappings_
) {
541 if (!first_mapping
) {
542 _description
+= ",\n";
545 _description
.resize(_description
.size() + mappings_indentation_level
, '\t');
546 if (mapping
.range
.begin
== mapping
.range
.end
) {
548 fmt::format("\"{mapping_name}\" = {mapping_value}",
549 fmt::arg("mapping_name", mapping
.name
),
550 fmt::arg("mapping_value", mapping
.range
.begin
));
552 _description
+= fmt::format(
553 "\"{mapping_name}\" = {mapping_range_begin} ... {mapping_range_end}",
554 fmt::arg("mapping_name", mapping
.name
),
555 fmt::arg("mapping_range_begin", mapping
.range
.begin
),
556 fmt::arg("mapping_range_end", mapping
.range
.end
));
559 first_mapping
= false;
562 _description
+= "\n";
563 _description
.resize(_description
.size() + _indentation_level
, '\t');
567 virtual void visit(const lst::signed_enumeration_type
& type
) override final
569 visit_enumeration(type
);
572 virtual void visit(const lst::unsigned_enumeration_type
& type
) override final
574 visit_enumeration(type
);
577 virtual void visit(const lst::static_length_array_type
& type
) override final
579 if (type
.alignment
!= 0) {
580 LTTNG_ASSERT(_current_field_name
.size() > 0);
581 _description
+= fmt::format(
582 "struct {{ }} align({alignment}) {field_name}_padding;\n",
583 fmt::arg("alignment", type
.alignment
),
584 fmt::arg("field_name", _current_field_name
.top()));
585 _description
.resize(_description
.size() + _indentation_level
, '\t');
588 type
.element_type
->accept(*this);
589 _type_suffixes
.emplace(fmt::format("[{}]", type
.length
));
592 virtual void visit(const lst::dynamic_length_array_type
& type
) override final
594 if (type
.alignment
!= 0) {
596 * Note that this doesn't support nested sequences. For
597 * the moment, tracers can't express those. However, we
598 * could wrap nested sequences in structures, which
599 * would allow us to express alignment constraints.
601 LTTNG_ASSERT(_current_field_name
.size() > 0);
602 _description
+= fmt::format(
603 "struct {{ }} align({alignment}) {field_name}_padding;\n",
604 fmt::arg("alignment", type
.alignment
),
605 fmt::arg("field_name", _current_field_name
.top()));
606 _description
.resize(_description
.size() + _indentation_level
, '\t');
609 type
.element_type
->accept(*this);
610 _type_suffixes
.emplace(fmt::format(
612 _bypass_identifier_escape
?
613 *(type
.length_field_location
.elements_
.end() - 1) :
614 escape_tsdl_identifier(
615 *(type
.length_field_location
.elements_
.end() - 1))));
618 virtual void visit(const lst::static_length_blob_type
& type
) override final
620 /* This type doesn't exist in CTF 1.x, express it as a static length array of
622 std::unique_ptr
<const lst::type
> uint8_element
=
623 lttng::make_unique
<lst::integer_type
>(
625 _trace_abi
.byte_order
,
627 lst::integer_type::signedness::UNSIGNED
,
628 lst::integer_type::base::HEXADECIMAL
);
629 const auto array
= lttng::make_unique
<lst::static_length_array_type
>(
630 type
.alignment
, std::move(uint8_element
), type
.length_bytes
);
635 virtual void visit(const lst::dynamic_length_blob_type
& type
) override final
637 /* This type doesn't exist in CTF 1.x, express it as a dynamic length array of
639 std::unique_ptr
<const lst::type
> uint8_element
=
640 lttng::make_unique
<lst::integer_type
>(
642 _trace_abi
.byte_order
,
644 lst::integer_type::signedness::UNSIGNED
,
645 lst::integer_type::base::HEXADECIMAL
);
646 const auto array
= lttng::make_unique
<lst::dynamic_length_array_type
>(
647 type
.alignment
, std::move(uint8_element
), type
.length_field_location
);
652 virtual void visit(const lst::null_terminated_string_type
& type
) override final
654 /* Defaults to UTF-8. */
655 if (type
.encoding_
== lst::null_terminated_string_type::encoding::ASCII
) {
656 _description
+= "string { encoding = ASCII }";
658 _description
+= "string";
662 virtual void visit(const lst::structure_type
& type
) override final
664 _indentation_level
++;
665 _description
+= "struct {";
667 const auto previous_bypass_identifier_escape
= _bypass_identifier_escape
;
668 _bypass_identifier_escape
= false;
669 for (const auto& field
: type
.fields_
) {
670 _description
+= "\n";
671 _description
.resize(_description
.size() + _indentation_level
, '\t');
672 field
->accept(*this);
675 _bypass_identifier_escape
= previous_bypass_identifier_escape
;
677 _indentation_level
--;
678 if (type
.fields_
.size() != 0) {
679 _description
+= "\n";
680 _description
.resize(_description
.size() + _indentation_level
, '\t');
686 template <class MappingIntegerType
>
687 void visit_variant(const lst::variant_type
<MappingIntegerType
>& type
)
689 if (type
.alignment
!= 0) {
690 LTTNG_ASSERT(_current_field_name
.size() > 0);
691 _description
+= fmt::format(
692 "struct {{ }} align({alignment}) {field_name}_padding;\n",
693 fmt::arg("alignment", type
.alignment
),
694 fmt::arg("field_name", _current_field_name
.top()));
695 _description
.resize(_description
.size() + _indentation_level
, '\t');
698 _indentation_level
++;
699 _description
+= fmt::format(
701 _bypass_identifier_escape
?
702 *(type
.selector_field_location
.elements_
.end() - 1) :
703 escape_tsdl_identifier(
704 *(type
.selector_field_location
.elements_
.end() - 1)));
707 * The CTF 1.8 specification only recommends that implementations ignore
708 * leading underscores in field names. Both babeltrace 1 and 2 expect the
709 * variant choice and enumeration mapping name to match perfectly. Given that we
710 * don't have access to the tag in this context, we have to assume they match.
712 const auto previous_bypass_identifier_escape
= _bypass_identifier_escape
;
713 _bypass_identifier_escape
= true;
714 for (const auto& field
: type
.choices_
) {
715 _description
.resize(_description
.size() + _indentation_level
, '\t');
716 field
.second
->accept(*this);
717 _description
+= fmt::format(" {};\n", field
.first
.name
);
720 _bypass_identifier_escape
= previous_bypass_identifier_escape
;
722 _indentation_level
--;
723 _description
.resize(_description
.size() + _indentation_level
, '\t');
727 virtual void visit(const lst::variant_type
<
728 lst::signed_enumeration_type::mapping::range_t::range_integer_t
>& type
)
734 virtual void visit(const lst::variant_type
<
735 lst::unsigned_enumeration_type::mapping::range_t::range_integer_t
>& type
)
741 lst::type::cuptr
create_character_type(enum lst::string_type::encoding encoding
)
743 _current_integer_encoding_override
= encoding
;
744 return lttng::make_unique
<lst::integer_type
>(
746 _trace_abi
.byte_order
,
748 lst::integer_type::signedness::UNSIGNED
,
749 lst::integer_type::base::DECIMAL
);
752 virtual void visit(const lst::static_length_string_type
& type
) override final
755 * TSDL expresses static-length strings as arrays of 8-bit integer with
756 * an encoding specified.
758 const auto char_array
= lttng::make_unique
<lst::static_length_array_type
>(
759 type
.alignment
, create_character_type(type
.encoding_
), type
.length
);
764 virtual void visit(const lst::dynamic_length_string_type
& type
) override final
767 * TSDL expresses dynamic-length strings as arrays of 8-bit integer with
768 * an encoding specified.
770 const auto char_sequence
= lttng::make_unique
<lst::dynamic_length_array_type
>(
772 create_character_type(type
.encoding_
),
773 type
.length_field_location
);
775 visit(*char_sequence
);
778 std::stack
<std::string
> _current_field_name
;
780 * Encoding to specify for the next serialized integer type.
781 * Since the integer_type does not allow an encoding to be specified (it is a TSDL-specific
782 * concept), this attribute is used when expressing static or dynamic length strings as
783 * arrays/sequences of bytes with an encoding.
785 nonstd::optional
<enum lst::string_type::encoding
> _current_integer_encoding_override
;
787 unsigned int _indentation_level
;
788 const lst::abi
& _trace_abi
;
790 std::queue
<std::string
> _type_suffixes
;
792 /* Description in TSDL format. */
793 std::string _description
;
795 bool _bypass_identifier_escape
;
796 const char *_default_clock_class_name
;
797 const tsdl::details::type_overrider
& _type_overrides
;
800 class tsdl_trace_environment_visitor
: public lst::trace_class_environment_visitor
{
802 tsdl_trace_environment_visitor() : _environment
{ "env {\n" }
806 virtual void visit(const lst::environment_field
<int64_t>& field
) override
808 _environment
+= fmt::format(" {} = {};\n", field
.name
, field
.value
);
811 virtual void visit(const lst::environment_field
<const char *>& field
) override
813 _environment
+= fmt::format(
814 " {} = \"{}\";\n", field
.name
, escape_tsdl_env_string_value(field
.value
));
817 /* Only call once. */
818 std::string
move_description()
820 _environment
+= "};\n\n";
821 return std::move(_environment
);
825 std::string _environment
;
829 tsdl::trace_class_visitor::trace_class_visitor(
830 const lst::abi
& trace_abi
,
831 tsdl::append_metadata_fragment_function append_metadata_fragment
) :
832 _trace_abi
{ trace_abi
}, _append_metadata_fragment(append_metadata_fragment
)
836 void tsdl::trace_class_visitor::append_metadata_fragment(const std::string
& fragment
) const
838 _append_metadata_fragment(fragment
);
841 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::trace_class
& trace_class
)
843 /* Ensure this instance is not used against multiple trace classes. */
844 LTTNG_ASSERT(!_current_trace_class
|| _current_trace_class
== &trace_class
);
845 _current_trace_class
= &trace_class
;
847 tsdl_field_visitor packet_header_visitor
{ trace_class
.abi
, 1, _sanitized_types_overrides
};
849 trace_class
.packet_header()->accept(packet_header_visitor
);
851 /* Declare type aliases, trace class, and packet header. */
852 auto trace_class_tsdl
= fmt::format(
853 "/* CTF {ctf_major}.{ctf_minor} */\n\n"
855 " major = {ctf_major};\n"
856 " minor = {ctf_minor};\n"
857 " uuid = \"{uuid}\";\n"
858 " byte_order = {byte_order};\n"
859 " packet.header := {packet_header_layout};\n"
861 fmt::arg("ctf_major", ctf_spec_major
),
862 fmt::arg("ctf_minor", ctf_spec_minor
),
863 fmt::arg("uint8_t_alignment", trace_class
.abi
.uint8_t_alignment
),
864 fmt::arg("uint16_t_alignment", trace_class
.abi
.uint16_t_alignment
),
865 fmt::arg("uint32_t_alignment", trace_class
.abi
.uint32_t_alignment
),
866 fmt::arg("uint64_t_alignment", trace_class
.abi
.uint64_t_alignment
),
867 fmt::arg("long_alignment", trace_class
.abi
.long_alignment
),
868 fmt::arg("long_size", trace_class
.abi
.long_alignment
),
869 fmt::arg("bits_per_long", trace_class
.abi
.bits_per_long
),
870 fmt::arg("uuid", lttng::utils::uuid_to_str(trace_class
.uuid
)),
871 fmt::arg("byte_order",
872 trace_class
.abi
.byte_order
== lst::byte_order::BIG_ENDIAN_
? "be" : "le"),
873 fmt::arg("packet_header_layout", packet_header_visitor
.move_description()));
875 /* Declare trace scope and type aliases. */
876 append_metadata_fragment(trace_class_tsdl
);
878 tsdl_trace_environment_visitor environment_visitor
;
879 trace_class
.accept(environment_visitor
);
880 append_metadata_fragment(environment_visitor
.move_description());
883 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::clock_class
& clock_class
)
885 auto uuid_str
= clock_class
.uuid
?
886 fmt::format(" uuid = \"{}\";\n", lttng::utils::uuid_to_str(*clock_class
.uuid
)) :
889 /* Assumes a single clock that maps to specific stream class fields/roles. */
890 auto clock_class_str
= fmt::format("clock {{\n"
891 " name = \"{name}\";\n"
894 " description = \"{description}\";\n"
895 " freq = {frequency};\n"
896 " offset = {offset};\n"
899 fmt::arg("name", clock_class
.name
),
900 fmt::arg("uuid", uuid_str
),
901 fmt::arg("description", clock_class
.description
),
902 fmt::arg("frequency", clock_class
.frequency
),
903 fmt::arg("offset", clock_class
.offset
));
905 append_metadata_fragment(clock_class_str
);
908 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::stream_class
& stream_class
)
910 _current_stream_class
= &stream_class
;
911 const auto clear_stream_class_on_exit
=
912 lttng::make_scope_exit([this]() noexcept
{ _current_stream_class
= nullptr; });
914 auto stream_class_str
= fmt::format("stream {{\n"
917 variant_tsdl_keyword_sanitizer
variant_sanitizer(
918 _sanitized_types_overrides
,
919 [this](const lttng::sessiond::trace::field_location
& location
) -> const lst::type
& {
920 return _lookup_field_type(location
);
923 const auto *event_header
= stream_class
.event_header();
925 tsdl_field_visitor event_header_visitor
{ _trace_abi
,
927 _sanitized_types_overrides
,
928 stream_class
.default_clock_class_name
};
930 event_header
->accept(variant_sanitizer
);
931 event_header
->accept(event_header_visitor
);
932 stream_class_str
+= fmt::format(" event.header := {};\n",
933 event_header_visitor
.move_description());
936 const auto *packet_context
= stream_class
.packet_context();
937 if (packet_context
) {
938 tsdl_field_visitor packet_context_visitor
{ _trace_abi
,
940 _sanitized_types_overrides
,
941 stream_class
.default_clock_class_name
};
943 packet_context
->accept(variant_sanitizer
);
944 packet_context
->accept(packet_context_visitor
);
945 stream_class_str
+= fmt::format(" packet.context := {};\n",
946 packet_context_visitor
.move_description());
949 const auto *event_context
= stream_class
.event_context();
951 tsdl_field_visitor event_context_visitor
{ _trace_abi
,
953 _sanitized_types_overrides
};
955 event_context
->accept(variant_sanitizer
);
956 event_context
->accept(event_context_visitor
);
957 stream_class_str
+= fmt::format(" event.context := {};\n",
958 event_context_visitor
.move_description());
961 stream_class_str
+= "};\n\n";
963 append_metadata_fragment(stream_class_str
);
966 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::event_class
& event_class
)
968 _current_event_class
= &event_class
;
969 const auto clear_event_class_on_exit
=
970 lttng::make_scope_exit([this]() noexcept
{ _current_event_class
= nullptr; });
972 auto event_class_str
= fmt::format("event {{\n"
973 " name = \"{name}\";\n"
975 " stream_id = {stream_class_id};\n"
976 " loglevel = {log_level};\n",
977 fmt::arg("name", event_class
.name
),
978 fmt::arg("id", event_class
.id
),
979 fmt::arg("stream_class_id", event_class
.stream_class_id
),
980 fmt::arg("log_level", event_class
.log_level
));
982 if (event_class
.model_emf_uri
) {
984 fmt::format(" model.emf.uri = \"{}\";\n", *event_class
.model_emf_uri
);
987 tsdl_field_visitor payload_visitor
{ _trace_abi
, 1, _sanitized_types_overrides
};
988 variant_tsdl_keyword_sanitizer
variant_sanitizer(
989 _sanitized_types_overrides
,
990 [this](const lttng::sessiond::trace::field_location
& location
) -> const lst::type
& {
991 return _lookup_field_type(location
);
994 event_class
.payload
->accept(variant_sanitizer
);
995 event_class
.payload
->accept(payload_visitor
);
998 fmt::format(" fields := {};\n}};\n\n", payload_visitor
.move_description());
1000 append_metadata_fragment(event_class_str
);
1003 void tsdl::details::type_overrider::publish(const lttng::sessiond::trace::type
& original_type
,
1004 lttng::sessiond::trace::type::cuptr new_type_override
)
1006 auto current_override
= _overriden_types
.find(&original_type
);
1008 if (current_override
!= _overriden_types
.end()) {
1009 current_override
->second
= std::move(new_type_override
);
1011 _overriden_types
.insert(
1012 std::make_pair(&original_type
, std::move(new_type_override
)));
1017 tsdl::details::type_overrider::type(const lttng::sessiond::trace::type
& original
) const noexcept
1019 const auto result
= _overriden_types
.find(&original
);
1021 if (result
!= _overriden_types
.end()) {
1022 /* Provide the overriden type. */
1023 return *result
->second
;
1026 /* Pass the original type through. */
1031 const lttng::sessiond::trace::type
&
1032 lookup_type_from_root_type(const lttng::sessiond::trace::type
& root_type
,
1033 const lttng::sessiond::trace::field_location
& field_location
)
1035 const auto *type
= &root_type
;
1037 for (const auto& location_element
: field_location
.elements_
) {
1038 /* Only structures can be traversed. */
1039 const auto *struct_type
= dynamic_cast<const lst::structure_type
*>(type
);
1042 * Traverse the type by following the field location path.
1044 * While field paths are assumed to have been validated before-hand,
1045 * a dynamic cast is performed here as an additional precaution
1046 * since none of this is performance-critical; it can be removed
1050 LTTNG_THROW_ERROR(fmt::format(
1051 "Encountered a type that is not a structure while traversing field location: field-location=`{}`",
1055 const auto field_found_it
=
1056 std::find_if(struct_type
->fields_
.cbegin(),
1057 struct_type
->fields_
.cend(),
1058 [&location_element
](const lst::field::cuptr
& struct_field
) {
1059 return struct_field
->name
== location_element
;
1062 if (field_found_it
== struct_type
->fields_
.cend()) {
1063 LTTNG_THROW_ERROR(fmt::format(
1064 "Failed to find field using field location: field-name:=`{field_name}`, field-location=`{field_location}`",
1065 fmt::arg("field_location", field_location
),
1066 fmt::arg("field_name", location_element
)));
1069 type
= &(*field_found_it
)->get_type();
1074 } /* anonymous namespace. */
1077 * The trace hierarchy is assumed to have been validated on creation.
1078 * This function can only fail due to a validation error, hence
1079 * why it throws on any unexpected/invalid field location.
1081 * Does not return an overriden field type; it returns the original field type
1082 * as found in the trace hierarchy.
1084 const lttng::sessiond::trace::type
& lttng::sessiond::tsdl::trace_class_visitor::_lookup_field_type(
1085 const lttng::sessiond::trace::field_location
& location
) const
1087 /* Validate the look-up is happening in a valid visit context. */
1088 switch (location
.root_
) {
1089 case lst::field_location::root::EVENT_RECORD_HEADER
:
1090 case lst::field_location::root::EVENT_RECORD_PAYLOAD
:
1091 if (!_current_event_class
) {
1093 "Field type look-up failure: no current event class in visitor's context");
1096 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT
:
1097 case lst::field_location::root::PACKET_CONTEXT
:
1098 if (!_current_stream_class
) {
1100 "Field type look-up failure: no current stream class in visitor's context");
1103 case lst::field_location::root::PACKET_HEADER
:
1104 if (!_current_trace_class
) {
1106 "Field type look-up failure: no current trace class in visitor's context");
1110 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT
:
1111 LTTNG_THROW_UNSUPPORTED_ERROR(
1112 "Field type look-up failure: event-record specific contexts are not supported");
1114 LTTNG_THROW_UNSUPPORTED_ERROR(
1115 "Field type look-up failure: unknown field location root");
1118 switch (location
.root_
) {
1119 case lst::field_location::root::PACKET_HEADER
:
1120 return lookup_type_from_root_type(*_current_trace_class
->packet_header(), location
);
1121 case lst::field_location::root::PACKET_CONTEXT
:
1122 return lookup_type_from_root_type(*_current_stream_class
->packet_context(),
1124 case lst::field_location::root::EVENT_RECORD_HEADER
:
1125 return lookup_type_from_root_type(*_current_stream_class
->event_header(), location
);
1126 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT
:
1127 return lookup_type_from_root_type(*_current_stream_class
->event_context(),
1129 case lst::field_location::root::EVENT_RECORD_PAYLOAD
:
1130 return lookup_type_from_root_type(*_current_event_class
->payload
, location
);
1131 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT
:
1133 /* Unreachable as it was checked before. */