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>
28 namespace lst
= lttng::sessiond::trace
;
29 namespace tsdl
= lttng::sessiond::tsdl
;
32 const auto ctf_spec_major
= 1;
33 const auto ctf_spec_minor
= 8;
36 * Although the CTF v1.8 specification recommends ignoring any leading underscore, Some readers,
37 * such as Babeltrace 1.x, expect special identifiers without a prepended underscore.
39 const std::unordered_set
<std::string
> safe_tsdl_identifiers
= { "stream_id",
52 "stream_instance_id" };
55 * A previous implementation always prepended '_' to the identifiers in order to
56 * side-step the problem of escaping TSDL keywords and ensuring identifiers
57 * started with an alphabetic character.
59 * Changing this behaviour to a smarter algorithm would break readers that have
60 * come to expect this initial underscore.
62 std::string
escape_tsdl_identifier(const std::string
& original_identifier
)
64 if (original_identifier
.size() == 0) {
65 LTTNG_THROW_ERROR("Invalid 0-length identifier used in trace description");
68 if (safe_tsdl_identifiers
.find(original_identifier
) != safe_tsdl_identifiers
.end()) {
69 return original_identifier
;
72 std::string new_identifier
;
73 /* Optimisticly assume most identifiers are valid and allocate the same length. */
74 new_identifier
.reserve(original_identifier
.size());
77 /* Replace illegal characters by '_'. */
78 std::locale c_locale
{ "C" };
79 for (const auto current_char
: original_identifier
) {
80 if (!std::isalnum(current_char
, c_locale
) && current_char
!= '_') {
81 new_identifier
+= '_';
83 new_identifier
+= current_char
;
87 return new_identifier
;
90 std::string
escape_tsdl_env_string_value(const std::string
& original_string
)
92 std::string escaped_string
;
94 escaped_string
.reserve(original_string
.size());
96 for (const auto c
: original_string
) {
99 escaped_string
+= "\\n";
102 escaped_string
+= "\\\\";
105 escaped_string
+= "\"";
113 return escaped_string
;
117 * Variants produced by LTTng-UST contain TSDL-unsafe names. A variant/selector
118 * sanitization pass is performed before serializing a trace class hierarchy to
121 * The variant_tsdl_keyword_sanitizer visitor is used to visit field before it
122 * is handed-over to the actual TSDL-producing visitor.
124 * As it visits fields, the variant_tsdl_keyword_sanitizer populates a
125 * "type_overrider" with TSDL-safe replacements for any variant or enumeration
126 * that uses TSDL-unsafe identifiers (reserved keywords).
128 * The type_overrider, in turn, is used by the rest of the TSDL serialization
129 * visitor (tsdl_field_visitor) to swap any TSDL-unsafe types with their
132 * The tsdl_field_visitor owns the type_overrider and only briefly shares it
133 * with the variant_tsdl_keyword_sanitizer which takes a reference to it.
135 class variant_tsdl_keyword_sanitizer
: public lttng::sessiond::trace::field_visitor
,
136 public lttng::sessiond::trace::type_visitor
{
138 using type_lookup_function
= std::function
<const lst::type
&(const lst::field_location
&)>;
140 variant_tsdl_keyword_sanitizer(tsdl::details::type_overrider
& type_overrides
,
141 type_lookup_function lookup_type
) :
142 _type_overrides(type_overrides
), _lookup_type(std::move(lookup_type
))
147 class _c_string_comparator
{
149 int operator()(const char *lhs
, const char *rhs
) const
151 return std::strcmp(lhs
, rhs
) < 0;
154 using unsafe_names
= std::set
<const char *, _c_string_comparator
>;
156 void visit(const lst::field
& field
) final
158 _type_overrides
.type(field
.get_type()).accept(*this);
161 void visit(const lst::integer_type
& type
__attribute__((unused
))) final
165 void visit(const lst::floating_point_type
& type
__attribute__((unused
))) final
169 void visit(const lst::signed_enumeration_type
& type
__attribute__((unused
))) final
173 void visit(const lst::unsigned_enumeration_type
& type
__attribute__((unused
))) final
177 void visit(const lst::static_length_array_type
& type
__attribute__((unused
))) final
181 void visit(const lst::dynamic_length_array_type
& type
__attribute__((unused
))) final
185 void visit(const lst::static_length_blob_type
& type
__attribute__((unused
))) final
189 void visit(const lst::dynamic_length_blob_type
& type
__attribute__((unused
))) final
193 void visit(const lst::null_terminated_string_type
& type
__attribute__((unused
))) final
197 void visit(const lst::structure_type
& type
) final
199 /* Recurse into structure attributes. */
200 for (const auto& field
: type
.fields_
) {
201 field
->accept(*this);
206 * Create a new enumeration type replacing any mapping that match, by name, the elements in
207 * `unsafe_names_found` with a TSDL-safe version. Currently, unsafe identifiers are made
208 * safe by adding a leading underscore.
210 template <typename MappingIntegerType
>
211 lst::type::cuptr
_create_sanitized_selector(
212 const lst::typed_enumeration_type
<MappingIntegerType
>& original_selector
,
213 const unsafe_names
& unsafe_names_found
)
215 auto new_mappings
= std::make_shared
<
216 typename
lst::typed_enumeration_type
<MappingIntegerType
>::mappings
>();
218 for (const auto& mapping
: *original_selector
.mappings_
) {
219 if (unsafe_names_found
.find(mapping
.name
.c_str()) ==
220 unsafe_names_found
.end()) {
221 /* Mapping is safe, simply copy it. */
222 new_mappings
->emplace_back(mapping
);
224 /* Unsafe mapping, rename it and keep the rest of its attributes. */
225 new_mappings
->emplace_back(lttng::format("_{}", mapping
.name
),
230 return lttng::make_unique
<lst::typed_enumeration_type
<MappingIntegerType
>>(
231 original_selector
.alignment
,
232 original_selector
.byte_order
,
233 original_selector
.size
,
234 original_selector
.base_
,
238 template <typename MappingIntegerType
>
239 const typename
lst::typed_enumeration_type
<MappingIntegerType
>::mapping
&
240 _find_enumeration_mapping_by_range(
241 const typename
lst::typed_enumeration_type
<MappingIntegerType
>& enumeration_type
,
242 const typename
lst::typed_enumeration_type
<MappingIntegerType
>::mapping::range_t
&
243 target_mapping_range
)
245 for (const auto& mapping
: *enumeration_type
.mappings_
) {
246 if (mapping
.range
== target_mapping_range
) {
251 LTTNG_THROW_ERROR(lttng::format(
252 "Failed to find mapping by range in enumeration while sanitizing a variant: target_mapping_range={}",
253 target_mapping_range
));
257 * Copy `original_variant`, but use the mappings of a previously-published sanitized tag
258 * to produce a TSDL-safe version of the variant.
260 template <typename MappingIntegerType
>
262 _create_sanitized_variant(const lst::variant_type
<MappingIntegerType
>& original_variant
)
264 typename
lst::variant_type
<MappingIntegerType
>::choices new_choices
;
265 const auto& sanitized_selector
=
266 static_cast<const lst::typed_enumeration_type
<MappingIntegerType
>&>(
267 _type_overrides
.type(
268 _lookup_type(original_variant
.selector_field_location
)));
270 /* Visit variant choices to sanitize them as needed. */
271 for (const auto& choice
: original_variant
.choices_
) {
272 choice
.second
->accept(*this);
275 for (const auto& choice
: original_variant
.choices_
) {
276 const auto& sanitized_choice_type
= _type_overrides
.type(*choice
.second
);
278 new_choices
.emplace_back(_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
= {
294 "align", "callsite", "const", "char", "clock", "double",
295 "enum", "env", "event", "floating_point", "float", "integer",
296 "int", "long", "short", "signed", "stream", "string",
297 "struct", "trace", "typealias", "typedef", "unsigned", "variant",
298 "void", "_Bool", "_Complex", "_Imaginary",
301 for (const auto& choice
: type
.choices_
) {
302 if (tsdl_protected_keywords
.find(choice
.first
.name
) !=
303 tsdl_protected_keywords
.cend()) {
304 /* Choice name is illegal, we have to rename it and its matching
306 unsafe_names_found
.insert(choice
.first
.name
.c_str());
310 if (unsafe_names_found
.empty()) {
315 * Look-up selector field type.
317 * Since it may have been overriden previously, keep the original and overriden
318 * selector field types (which may be the same, if the original was not overriden).
320 * We work from the "overriden" selector field type to preserve any existing
321 * modifications. However, the original field type will be used to publish the new
322 * version of the type leaving only the most recent overriden type in the type
325 const auto& original_selector_type
= _lookup_type(type
.selector_field_location
);
326 const auto& overriden_selector_type
= _type_overrides
.type(original_selector_type
);
328 auto sanitized_selector_type
= _create_sanitized_selector(
329 static_cast<const lst::typed_enumeration_type
<MappingIntegerType
>&>(
330 overriden_selector_type
),
332 _type_overrides
.publish(original_selector_type
, std::move(sanitized_selector_type
));
334 auto sanitized_variant_type
= _create_sanitized_variant(
335 static_cast<const lst::variant_type
<MappingIntegerType
>&>(type
));
336 _type_overrides
.publish(type
, std::move(sanitized_variant_type
));
339 void visit(const lst::variant_type
<
340 lst::signed_enumeration_type::mapping::range_t::range_integer_t
>& type
) final
345 void visit(const lst::variant_type
<
346 lst::unsigned_enumeration_type::mapping::range_t::range_integer_t
>& type
) final
351 void visit(const lst::static_length_string_type
& type
__attribute__((unused
))) final
355 void visit(const lst::dynamic_length_string_type
& type
__attribute__((unused
))) final
359 tsdl::details::type_overrider
& _type_overrides
;
360 const type_lookup_function _lookup_type
;
363 class tsdl_field_visitor
: public lttng::sessiond::trace::field_visitor
,
364 public lttng::sessiond::trace::type_visitor
{
366 tsdl_field_visitor(const lst::abi
& abi
,
367 unsigned int indentation_level
,
368 const tsdl::details::type_overrider
& type_overrides
,
369 const nonstd::optional
<std::string
>& in_default_clock_class_name
=
371 _indentation_level(indentation_level
),
374 _default_clock_class_name(in_default_clock_class_name
?
375 in_default_clock_class_name
->c_str() :
377 _type_overrides(type_overrides
)
381 /* Only call once. */
382 std::string
move_description()
384 return std::move(_description
);
388 void visit(const lst::field
& field
) final
391 * Hack: keep the name of the field being visited since
392 * the tracers can express sequences, variants, and arrays with an alignment
393 * constraint, which is not expressible in TSDL. To work around this limitation, an
394 * empty structure declaration is inserted when needed to express the aligment
395 * constraint. The name of this structure is generated using the field's name.
397 _current_field_name
.push(_bypass_identifier_escape
?
399 escape_tsdl_identifier(field
.name
));
400 _type_overrides
.type(field
.get_type()).accept(*this);
402 _description
+= _current_field_name
.top();
403 _current_field_name
.pop();
406 * Some types requires suffixes to be appended (e.g. the length of arrays
407 * and sequences, the mappings of enumerations).
409 while (!_type_suffixes
.empty()) {
410 _description
+= _type_suffixes
.front();
411 _type_suffixes
.pop();
417 void visit(const lst::integer_type
& type
) final
419 _description
+= "integer { ";
421 /* Mandatory properties (no defaults). */
422 _description
+= lttng::format("size = {size}; align = {alignment};",
423 fmt::arg("size", type
.size
),
424 fmt::arg("alignment", type
.alignment
));
426 /* Defaults to unsigned. */
427 if (type
.signedness_
== lst::integer_type::signedness::SIGNED
) {
428 _description
+= " signed = true;";
431 /* Defaults to 10. */
432 if (type
.base_
!= lst::integer_type::base::DECIMAL
) {
435 switch (type
.base_
) {
436 case lst::integer_type::base::BINARY
:
439 case lst::integer_type::base::OCTAL
:
442 case lst::integer_type::base::HEXADECIMAL
:
446 LTTNG_THROW_ERROR(lttng::format(
447 "Unexpected base encountered while serializing integer type to TSDL: base = {}",
451 _description
+= lttng::format(" base = {};", base
);
454 /* Defaults to the trace's native byte order. */
455 if (type
.byte_order
!= _trace_abi
.byte_order
) {
456 const auto byte_order_str
=
457 type
.byte_order
== lst::byte_order::BIG_ENDIAN_
? "be" : "le";
459 _description
+= lttng::format(" byte_order = {};", byte_order_str
);
462 if (_current_integer_encoding_override
) {
463 const char *encoding_str
;
465 switch (*_current_integer_encoding_override
) {
466 case lst::string_type::encoding::ASCII
:
467 encoding_str
= "ASCII";
469 case lst::string_type::encoding::UTF8
:
470 encoding_str
= "UTF8";
473 LTTNG_THROW_ERROR(lttng::format(
474 "Unexpected encoding encountered while serializing integer type to TSDL: encoding = {}",
475 (int) *_current_integer_encoding_override
));
478 _description
+= lttng::format(" encoding = {};", encoding_str
);
479 _current_integer_encoding_override
.reset();
482 if (std::find(type
.roles_
.begin(),
484 lst::integer_type::role::DEFAULT_CLOCK_TIMESTAMP
) !=
486 std::find(type
.roles_
.begin(),
488 lst::integer_type::role::PACKET_END_DEFAULT_CLOCK_TIMESTAMP
) !=
490 LTTNG_ASSERT(_default_clock_class_name
);
492 lttng::format(" map = clock.{}.value;", _default_clock_class_name
);
495 _description
+= " }";
498 void visit(const lst::floating_point_type
& type
) final
500 _description
+= lttng::format(
501 "floating_point {{ align = {alignment}; mant_dig = {mantissa_digits}; exp_dig = {exponent_digits};",
502 fmt::arg("alignment", type
.alignment
),
503 fmt::arg("mantissa_digits", type
.mantissa_digits
),
504 fmt::arg("exponent_digits", type
.exponent_digits
));
506 /* Defaults to the trace's native byte order. */
507 if (type
.byte_order
!= _trace_abi
.byte_order
) {
508 const auto byte_order_str
=
509 type
.byte_order
== lst::byte_order::BIG_ENDIAN_
? "be" : "le";
511 _description
+= lttng::format(" byte_order = {};", byte_order_str
);
514 _description
+= " }";
517 template <class EnumerationType
>
518 void visit_enumeration(const EnumerationType
& type
)
520 /* name follows, when applicable. */
521 _description
+= "enum : ";
523 visit(static_cast<const lst::integer_type
&>(type
));
524 _description
+= " {\n";
526 const auto mappings_indentation_level
= _indentation_level
+ 1;
528 bool first_mapping
= true;
529 for (const auto& mapping
: *type
.mappings_
) {
530 if (!first_mapping
) {
531 _description
+= ",\n";
534 _description
.resize(_description
.size() + mappings_indentation_level
, '\t');
535 if (mapping
.range
.begin
== mapping
.range
.end
) {
536 _description
+= lttng::format(
537 "\"{mapping_name}\" = {mapping_value}",
538 fmt::arg("mapping_name", mapping
.name
),
539 fmt::arg("mapping_value", mapping
.range
.begin
));
541 _description
+= lttng::format(
542 "\"{mapping_name}\" = {mapping_range_begin} ... {mapping_range_end}",
543 fmt::arg("mapping_name", mapping
.name
),
544 fmt::arg("mapping_range_begin", mapping
.range
.begin
),
545 fmt::arg("mapping_range_end", mapping
.range
.end
));
548 first_mapping
= false;
551 _description
+= "\n";
552 _description
.resize(_description
.size() + _indentation_level
, '\t');
556 void visit(const lst::signed_enumeration_type
& type
) final
558 visit_enumeration(type
);
561 void visit(const lst::unsigned_enumeration_type
& type
) final
563 visit_enumeration(type
);
566 void visit(const lst::static_length_array_type
& type
) final
568 if (type
.alignment
!= 0) {
569 LTTNG_ASSERT(_current_field_name
.size() > 0);
570 _description
+= lttng::format(
571 "struct {{ }} align({alignment}) {field_name}_padding;\n",
572 fmt::arg("alignment", type
.alignment
),
573 fmt::arg("field_name", _current_field_name
.top()));
574 _description
.resize(_description
.size() + _indentation_level
, '\t');
577 type
.element_type
->accept(*this);
578 _type_suffixes
.emplace(lttng::format("[{}]", type
.length
));
581 void visit(const lst::dynamic_length_array_type
& type
) final
583 if (type
.alignment
!= 0) {
585 * Note that this doesn't support nested sequences. For
586 * the moment, tracers can't express those. However, we
587 * could wrap nested sequences in structures, which
588 * would allow us to express alignment constraints.
590 LTTNG_ASSERT(_current_field_name
.size() > 0);
591 _description
+= lttng::format(
592 "struct {{ }} align({alignment}) {field_name}_padding;\n",
593 fmt::arg("alignment", type
.alignment
),
594 fmt::arg("field_name", _current_field_name
.top()));
595 _description
.resize(_description
.size() + _indentation_level
, '\t');
598 type
.element_type
->accept(*this);
599 _type_suffixes
.emplace(lttng::format(
601 _bypass_identifier_escape
?
602 *(type
.length_field_location
.elements_
.end() - 1) :
603 escape_tsdl_identifier(
604 *(type
.length_field_location
.elements_
.end() - 1))));
607 void visit(const lst::static_length_blob_type
& type
) final
609 /* This type doesn't exist in CTF 1.x, express it as a static length array of
611 std::unique_ptr
<const lst::type
> uint8_element
=
612 lttng::make_unique
<lst::integer_type
>(
614 _trace_abi
.byte_order
,
616 lst::integer_type::signedness::UNSIGNED
,
617 lst::integer_type::base::HEXADECIMAL
);
618 const auto array
= lttng::make_unique
<lst::static_length_array_type
>(
619 type
.alignment
, std::move(uint8_element
), type
.length_bytes
);
624 void visit(const lst::dynamic_length_blob_type
& type
) final
626 /* This type doesn't exist in CTF 1.x, express it as a dynamic length array of
628 std::unique_ptr
<const lst::type
> uint8_element
=
629 lttng::make_unique
<lst::integer_type
>(
631 _trace_abi
.byte_order
,
633 lst::integer_type::signedness::UNSIGNED
,
634 lst::integer_type::base::HEXADECIMAL
);
635 const auto array
= lttng::make_unique
<lst::dynamic_length_array_type
>(
636 type
.alignment
, std::move(uint8_element
), type
.length_field_location
);
641 void visit(const lst::null_terminated_string_type
& type
) final
643 /* Defaults to UTF-8. */
644 if (type
.encoding_
== lst::null_terminated_string_type::encoding::ASCII
) {
645 _description
+= "string { encoding = ASCII }";
647 _description
+= "string";
651 void visit(const lst::structure_type
& type
) final
653 _indentation_level
++;
654 _description
+= "struct {";
656 const auto previous_bypass_identifier_escape
= _bypass_identifier_escape
;
657 _bypass_identifier_escape
= false;
658 for (const auto& field
: type
.fields_
) {
659 _description
+= "\n";
660 _description
.resize(_description
.size() + _indentation_level
, '\t');
661 field
->accept(*this);
664 _bypass_identifier_escape
= previous_bypass_identifier_escape
;
666 _indentation_level
--;
667 if (type
.fields_
.size() != 0) {
668 _description
+= "\n";
669 _description
.resize(_description
.size() + _indentation_level
, '\t');
675 template <class MappingIntegerType
>
676 void visit_variant(const lst::variant_type
<MappingIntegerType
>& type
)
678 if (type
.alignment
!= 0) {
679 LTTNG_ASSERT(_current_field_name
.size() > 0);
680 _description
+= lttng::format(
681 "struct {{ }} align({alignment}) {field_name}_padding;\n",
682 fmt::arg("alignment", type
.alignment
),
683 fmt::arg("field_name", _current_field_name
.top()));
684 _description
.resize(_description
.size() + _indentation_level
, '\t');
687 _indentation_level
++;
688 _description
+= lttng::format(
690 _bypass_identifier_escape
?
691 *(type
.selector_field_location
.elements_
.end() - 1) :
692 escape_tsdl_identifier(
693 *(type
.selector_field_location
.elements_
.end() - 1)));
696 * The CTF 1.8 specification only recommends that implementations ignore
697 * leading underscores in field names. Both babeltrace 1 and 2 expect the
698 * variant choice and enumeration mapping name to match perfectly. Given that we
699 * don't have access to the tag in this context, we have to assume they match.
701 const auto previous_bypass_identifier_escape
= _bypass_identifier_escape
;
702 _bypass_identifier_escape
= true;
703 for (const auto& field
: type
.choices_
) {
704 _description
.resize(_description
.size() + _indentation_level
, '\t');
705 field
.second
->accept(*this);
706 _description
+= lttng::format(" {};\n", field
.first
.name
);
709 _bypass_identifier_escape
= previous_bypass_identifier_escape
;
711 _indentation_level
--;
712 _description
.resize(_description
.size() + _indentation_level
, '\t');
716 void visit(const lst::variant_type
<
717 lst::signed_enumeration_type::mapping::range_t::range_integer_t
>& type
) final
722 void visit(const lst::variant_type
<
723 lst::unsigned_enumeration_type::mapping::range_t::range_integer_t
>& type
) final
728 lst::type::cuptr
create_character_type(enum lst::string_type::encoding encoding
)
730 _current_integer_encoding_override
= encoding
;
731 return lttng::make_unique
<lst::integer_type
>(
733 _trace_abi
.byte_order
,
735 lst::integer_type::signedness::UNSIGNED
,
736 lst::integer_type::base::DECIMAL
);
739 void visit(const lst::static_length_string_type
& type
) final
742 * TSDL expresses static-length strings as arrays of 8-bit integer with
743 * an encoding specified.
745 const auto char_array
= lttng::make_unique
<lst::static_length_array_type
>(
746 type
.alignment
, create_character_type(type
.encoding_
), type
.length
);
751 void visit(const lst::dynamic_length_string_type
& type
) final
754 * TSDL expresses dynamic-length strings as arrays of 8-bit integer with
755 * an encoding specified.
757 const auto char_sequence
= lttng::make_unique
<lst::dynamic_length_array_type
>(
759 create_character_type(type
.encoding_
),
760 type
.length_field_location
);
762 visit(*char_sequence
);
765 std::stack
<std::string
> _current_field_name
;
767 * Encoding to specify for the next serialized integer type.
768 * Since the integer_type does not allow an encoding to be specified (it is a TSDL-specific
769 * concept), this attribute is used when expressing static or dynamic length strings as
770 * arrays/sequences of bytes with an encoding.
772 nonstd::optional
<enum lst::string_type::encoding
> _current_integer_encoding_override
;
774 unsigned int _indentation_level
;
775 const lst::abi
& _trace_abi
;
777 std::queue
<std::string
> _type_suffixes
;
779 /* Description in TSDL format. */
780 std::string _description
;
782 bool _bypass_identifier_escape
{ false };
783 const char *_default_clock_class_name
;
784 const tsdl::details::type_overrider
& _type_overrides
;
787 class tsdl_trace_environment_visitor
: public lst::trace_class_environment_visitor
{
789 tsdl_trace_environment_visitor() = default;
791 void visit(const lst::environment_field
<int64_t>& field
) override
793 _environment
+= lttng::format(" {} = {};\n", field
.name
, field
.value
);
796 void visit(const lst::environment_field
<const char *>& field
) override
798 _environment
+= lttng::format(
799 " {} = \"{}\";\n", field
.name
, escape_tsdl_env_string_value(field
.value
));
802 /* Only call once. */
803 std::string
move_description()
805 _environment
+= "};\n\n";
806 return std::move(_environment
);
810 std::string _environment
{ "env {\n" };
814 tsdl::trace_class_visitor::trace_class_visitor(
815 const lst::abi
& trace_abi
,
816 tsdl::append_metadata_fragment_function append_metadata_fragment
) :
817 _trace_abi(trace_abi
), _append_metadata_fragment(std::move(append_metadata_fragment
))
821 void tsdl::trace_class_visitor::append_metadata_fragment(const std::string
& fragment
) const
823 _append_metadata_fragment(fragment
);
826 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::trace_class
& trace_class
)
828 /* Ensure this instance is not used against multiple trace classes. */
829 LTTNG_ASSERT(!_current_trace_class
|| _current_trace_class
== &trace_class
);
830 _current_trace_class
= &trace_class
;
832 tsdl_field_visitor packet_header_visitor
{ trace_class
.abi
, 1, _sanitized_types_overrides
};
834 trace_class
.packet_header()->accept(packet_header_visitor
);
836 /* Declare type aliases, trace class, and packet header. */
837 auto trace_class_tsdl
= lttng::format(
838 "/* CTF {ctf_major}.{ctf_minor} */\n\n"
840 " major = {ctf_major};\n"
841 " minor = {ctf_minor};\n"
842 " uuid = \"{uuid}\";\n"
843 " byte_order = {byte_order};\n"
844 " packet.header := {packet_header_layout};\n"
846 fmt::arg("ctf_major", ctf_spec_major
),
847 fmt::arg("ctf_minor", ctf_spec_minor
),
848 fmt::arg("uint8_t_alignment", trace_class
.abi
.uint8_t_alignment
),
849 fmt::arg("uint16_t_alignment", trace_class
.abi
.uint16_t_alignment
),
850 fmt::arg("uint32_t_alignment", trace_class
.abi
.uint32_t_alignment
),
851 fmt::arg("uint64_t_alignment", trace_class
.abi
.uint64_t_alignment
),
852 fmt::arg("long_alignment", trace_class
.abi
.long_alignment
),
853 fmt::arg("long_size", trace_class
.abi
.long_alignment
),
854 fmt::arg("bits_per_long", trace_class
.abi
.bits_per_long
),
855 fmt::arg("uuid", lttng::utils::uuid_to_str(trace_class
.uuid
)),
856 fmt::arg("byte_order",
857 trace_class
.abi
.byte_order
== lst::byte_order::BIG_ENDIAN_
? "be" : "le"),
858 fmt::arg("packet_header_layout", packet_header_visitor
.move_description()));
860 /* Declare trace scope and type aliases. */
861 append_metadata_fragment(trace_class_tsdl
);
863 tsdl_trace_environment_visitor environment_visitor
;
864 trace_class
.accept(environment_visitor
);
865 append_metadata_fragment(environment_visitor
.move_description());
868 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::clock_class
& clock_class
)
870 auto uuid_str
= clock_class
.uuid
?
871 lttng::format(" uuid = \"{}\";\n", lttng::utils::uuid_to_str(*clock_class
.uuid
)) :
874 /* Assumes a single clock that maps to specific stream class fields/roles. */
875 auto clock_class_str
= lttng::format("clock {{\n"
876 " name = \"{name}\";\n"
879 " description = \"{description}\";\n"
880 " freq = {frequency};\n"
881 " offset = {offset};\n"
884 fmt::arg("name", clock_class
.name
),
885 fmt::arg("uuid", uuid_str
),
886 fmt::arg("description", clock_class
.description
),
887 fmt::arg("frequency", clock_class
.frequency
),
888 fmt::arg("offset", clock_class
.offset
));
890 append_metadata_fragment(clock_class_str
);
893 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::stream_class
& stream_class
)
895 _current_stream_class
= &stream_class
;
896 const auto clear_stream_class_on_exit
=
897 lttng::make_scope_exit([this]() noexcept
{ _current_stream_class
= nullptr; });
899 auto stream_class_str
= lttng::format("stream {{\n"
902 variant_tsdl_keyword_sanitizer
variant_sanitizer(
903 _sanitized_types_overrides
,
904 [this](const lttng::sessiond::trace::field_location
& location
) -> const lst::type
& {
905 return _lookup_field_type(location
);
908 const auto *event_header
= stream_class
.event_header();
910 tsdl_field_visitor event_header_visitor
{ _trace_abi
,
912 _sanitized_types_overrides
,
913 stream_class
.default_clock_class_name
};
915 event_header
->accept(variant_sanitizer
);
916 event_header
->accept(event_header_visitor
);
917 stream_class_str
+= lttng::format(" event.header := {};\n",
918 event_header_visitor
.move_description());
921 const auto *packet_context
= stream_class
.packet_context();
922 if (packet_context
) {
923 tsdl_field_visitor packet_context_visitor
{ _trace_abi
,
925 _sanitized_types_overrides
,
926 stream_class
.default_clock_class_name
};
928 packet_context
->accept(variant_sanitizer
);
929 packet_context
->accept(packet_context_visitor
);
930 stream_class_str
+= lttng::format(" packet.context := {};\n",
931 packet_context_visitor
.move_description());
934 const auto *event_context
= stream_class
.event_context();
936 tsdl_field_visitor event_context_visitor
{ _trace_abi
,
938 _sanitized_types_overrides
};
940 event_context
->accept(variant_sanitizer
);
941 event_context
->accept(event_context_visitor
);
942 stream_class_str
+= lttng::format(" event.context := {};\n",
943 event_context_visitor
.move_description());
946 stream_class_str
+= "};\n\n";
948 append_metadata_fragment(stream_class_str
);
951 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::event_class
& event_class
)
953 _current_event_class
= &event_class
;
954 const auto clear_event_class_on_exit
=
955 lttng::make_scope_exit([this]() noexcept
{ _current_event_class
= nullptr; });
957 auto event_class_str
=
958 lttng::format("event {{\n"
959 " name = \"{name}\";\n"
961 " stream_id = {stream_class_id};\n"
962 " loglevel = {log_level};\n",
963 fmt::arg("name", event_class
.name
),
964 fmt::arg("id", event_class
.id
),
965 fmt::arg("stream_class_id", event_class
.stream_class_id
),
966 fmt::arg("log_level", event_class
.log_level
));
968 if (event_class
.model_emf_uri
) {
970 lttng::format(" model.emf.uri = \"{}\";\n", *event_class
.model_emf_uri
);
973 tsdl_field_visitor payload_visitor
{ _trace_abi
, 1, _sanitized_types_overrides
};
974 variant_tsdl_keyword_sanitizer
variant_sanitizer(
975 _sanitized_types_overrides
,
976 [this](const lttng::sessiond::trace::field_location
& location
) -> const lst::type
& {
977 return _lookup_field_type(location
);
980 event_class
.payload
->accept(variant_sanitizer
);
981 event_class
.payload
->accept(payload_visitor
);
984 lttng::format(" fields := {};\n}};\n\n", payload_visitor
.move_description());
986 append_metadata_fragment(event_class_str
);
989 void tsdl::details::type_overrider::publish(const lttng::sessiond::trace::type
& original_type
,
990 lttng::sessiond::trace::type::cuptr new_type_override
)
992 auto current_override
= _overriden_types
.find(&original_type
);
994 if (current_override
!= _overriden_types
.end()) {
995 current_override
->second
= std::move(new_type_override
);
997 _overriden_types
.insert(
998 std::make_pair(&original_type
, std::move(new_type_override
)));
1003 tsdl::details::type_overrider::type(const lttng::sessiond::trace::type
& original
) const noexcept
1005 const auto result
= _overriden_types
.find(&original
);
1007 if (result
!= _overriden_types
.end()) {
1008 /* Provide the overriden type. */
1009 return *result
->second
;
1012 /* Pass the original type through. */
1017 const lttng::sessiond::trace::type
&
1018 lookup_type_from_root_type(const lttng::sessiond::trace::type
& root_type
,
1019 const lttng::sessiond::trace::field_location
& field_location
)
1021 const auto *type
= &root_type
;
1023 for (const auto& location_element
: field_location
.elements_
) {
1024 /* Only structures can be traversed. */
1025 const auto *struct_type
= dynamic_cast<const lst::structure_type
*>(type
);
1028 * Traverse the type by following the field location path.
1030 * While field paths are assumed to have been validated before-hand,
1031 * a dynamic cast is performed here as an additional precaution
1032 * since none of this is performance-critical; it can be removed
1036 LTTNG_THROW_ERROR(lttng::format(
1037 "Encountered a type that is not a structure while traversing field location: field-location=`{}`",
1041 const auto field_found_it
=
1042 std::find_if(struct_type
->fields_
.cbegin(),
1043 struct_type
->fields_
.cend(),
1044 [&location_element
](const lst::field::cuptr
& struct_field
) {
1045 return struct_field
->name
== location_element
;
1048 if (field_found_it
== struct_type
->fields_
.cend()) {
1049 LTTNG_THROW_ERROR(lttng::format(
1050 "Failed to find field using field location: field-name:=`{field_name}`, field-location=`{field_location}`",
1051 fmt::arg("field_location", field_location
),
1052 fmt::arg("field_name", location_element
)));
1055 type
= &(*field_found_it
)->get_type();
1060 } /* anonymous namespace. */
1063 * The trace hierarchy is assumed to have been validated on creation.
1064 * This function can only fail due to a validation error, hence
1065 * why it throws on any unexpected/invalid field location.
1067 * Does not return an overriden field type; it returns the original field type
1068 * as found in the trace hierarchy.
1070 const lttng::sessiond::trace::type
& lttng::sessiond::tsdl::trace_class_visitor::_lookup_field_type(
1071 const lttng::sessiond::trace::field_location
& location
) const
1073 /* Validate the look-up is happening in a valid visit context. */
1074 switch (location
.root_
) {
1075 case lst::field_location::root::EVENT_RECORD_HEADER
:
1076 case lst::field_location::root::EVENT_RECORD_PAYLOAD
:
1077 if (!_current_event_class
) {
1079 "Field type look-up failure: no current event class in visitor's context");
1082 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT
:
1083 case lst::field_location::root::PACKET_CONTEXT
:
1084 if (!_current_stream_class
) {
1086 "Field type look-up failure: no current stream class in visitor's context");
1089 case lst::field_location::root::PACKET_HEADER
:
1090 if (!_current_trace_class
) {
1092 "Field type look-up failure: no current trace class in visitor's context");
1096 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT
:
1097 LTTNG_THROW_UNSUPPORTED_ERROR(
1098 "Field type look-up failure: event-record specific contexts are not supported");
1100 LTTNG_THROW_UNSUPPORTED_ERROR(
1101 "Field type look-up failure: unknown field location root");
1104 switch (location
.root_
) {
1105 case lst::field_location::root::PACKET_HEADER
:
1106 return lookup_type_from_root_type(*_current_trace_class
->packet_header(), location
);
1107 case lst::field_location::root::PACKET_CONTEXT
:
1108 return lookup_type_from_root_type(*_current_stream_class
->packet_context(),
1110 case lst::field_location::root::EVENT_RECORD_HEADER
:
1111 return lookup_type_from_root_type(*_current_stream_class
->event_header(), location
);
1112 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT
:
1113 return lookup_type_from_root_type(*_current_stream_class
->event_context(),
1115 case lst::field_location::root::EVENT_RECORD_PAYLOAD
:
1116 return lookup_type_from_root_type(*_current_event_class
->payload
, location
);
1117 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT
:
1119 /* Unreachable as it was checked before. */