clang-tidy: add Chrome-inspired checks
[lttng-tools.git] / src / bin / lttng-sessiond / tsdl-trace-class-visitor.cpp
1 /*
2 * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #include "clock-class.hpp"
9 #include "tsdl-trace-class-visitor.hpp"
10
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>
16
17 #include <vendor/optional.hpp>
18
19 #include <algorithm>
20 #include <array>
21 #include <locale>
22 #include <queue>
23 #include <set>
24 #include <stack>
25 #include <unordered_set>
26 #include <utility>
27
28 namespace lst = lttng::sessiond::trace;
29 namespace tsdl = lttng::sessiond::tsdl;
30
31 namespace {
32 const auto ctf_spec_major = 1;
33 const auto ctf_spec_minor = 8;
34
35 /*
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.
38 */
39 const std::unordered_set<std::string> safe_tsdl_identifiers = { "stream_id",
40 "packet_size",
41 "content_size",
42 "id",
43 "v",
44 "timestamp",
45 "events_discarded",
46 "packet_seq_num",
47 "timestamp_begin",
48 "timestamp_end",
49 "cpu_id",
50 "magic",
51 "uuid",
52 "stream_instance_id" };
53
54 /*
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.
58 *
59 * Changing this behaviour to a smarter algorithm would break readers that have
60 * come to expect this initial underscore.
61 */
62 std::string escape_tsdl_identifier(const std::string& original_identifier)
63 {
64 if (original_identifier.size() == 0) {
65 LTTNG_THROW_ERROR("Invalid 0-length identifier used in trace description");
66 }
67
68 if (safe_tsdl_identifiers.find(original_identifier) != safe_tsdl_identifiers.end()) {
69 return original_identifier;
70 }
71
72 std::string new_identifier;
73 /* Optimisticly assume most identifiers are valid and allocate the same length. */
74 new_identifier.reserve(original_identifier.size());
75 new_identifier = "_";
76
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 += '_';
82 } else {
83 new_identifier += current_char;
84 }
85 }
86
87 return new_identifier;
88 }
89
90 std::string escape_tsdl_env_string_value(const std::string& original_string)
91 {
92 std::string escaped_string;
93
94 escaped_string.reserve(original_string.size());
95
96 for (const auto c : original_string) {
97 switch (c) {
98 case '\n':
99 escaped_string += "\\n";
100 break;
101 case '\\':
102 escaped_string += "\\\\";
103 break;
104 case '"':
105 escaped_string += "\"";
106 break;
107 default:
108 escaped_string += c;
109 break;
110 }
111 }
112
113 return escaped_string;
114 }
115
116 /*
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
119 * TSDL.
120 *
121 * The variant_tsdl_keyword_sanitizer visitor is used to visit field before it
122 * is handed-over to the actual TSDL-producing visitor.
123 *
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).
127 *
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
130 * sanitized version.
131 *
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.
134 */
135 class variant_tsdl_keyword_sanitizer : public lttng::sessiond::trace::field_visitor,
136 public lttng::sessiond::trace::type_visitor {
137 public:
138 using type_lookup_function = std::function<const lst::type&(const lst::field_location&)>;
139
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))
143 {
144 }
145
146 private:
147 class _c_string_comparator {
148 public:
149 int operator()(const char *lhs, const char *rhs) const
150 {
151 return std::strcmp(lhs, rhs) < 0;
152 }
153 };
154 using unsafe_names = std::set<const char *, _c_string_comparator>;
155
156 void visit(const lst::field& field) final
157 {
158 _type_overrides.type(field.get_type()).accept(*this);
159 }
160
161 void visit(const lst::integer_type& type __attribute__((unused))) final
162 {
163 }
164
165 void visit(const lst::floating_point_type& type __attribute__((unused))) final
166 {
167 }
168
169 void visit(const lst::signed_enumeration_type& type __attribute__((unused))) final
170 {
171 }
172
173 void visit(const lst::unsigned_enumeration_type& type __attribute__((unused))) final
174 {
175 }
176
177 void visit(const lst::static_length_array_type& type __attribute__((unused))) final
178 {
179 }
180
181 void visit(const lst::dynamic_length_array_type& type __attribute__((unused))) final
182 {
183 }
184
185 void visit(const lst::static_length_blob_type& type __attribute__((unused))) final
186 {
187 }
188
189 void visit(const lst::dynamic_length_blob_type& type __attribute__((unused))) final
190 {
191 }
192
193 void visit(const lst::null_terminated_string_type& type __attribute__((unused))) final
194 {
195 }
196
197 void visit(const lst::structure_type& type) final
198 {
199 /* Recurse into structure attributes. */
200 for (const auto& field : type.fields_) {
201 field->accept(*this);
202 }
203 }
204
205 /*
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.
209 */
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)
214 {
215 auto new_mappings = std::make_shared<
216 typename lst::typed_enumeration_type<MappingIntegerType>::mappings>();
217
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);
223 } else {
224 /* Unsafe mapping, rename it and keep the rest of its attributes. */
225 new_mappings->emplace_back(fmt::format("_{}", mapping.name),
226 mapping.range);
227 }
228 }
229
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_,
235 new_mappings);
236 }
237
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)
244 {
245 for (const auto& mapping : *enumeration_type.mappings_) {
246 if (mapping.range == target_mapping_range) {
247 return mapping;
248 }
249 }
250
251 LTTNG_THROW_ERROR(fmt::format(
252 "Failed to find mapping by range in enumeration while sanitizing a variant: target_mapping_range={}",
253 target_mapping_range));
254 }
255
256 /*
257 * Copy `original_variant`, but use the mappings of a previously-published sanitized tag
258 * to produce a TSDL-safe version of the variant.
259 */
260 template <typename MappingIntegerType>
261 lst::type::cuptr
262 _create_sanitized_variant(const lst::variant_type<MappingIntegerType>& original_variant)
263 {
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)));
269
270 /* Visit variant choices to sanitize them as needed. */
271 for (const auto& choice : original_variant.choices_) {
272 choice.second->accept(*this);
273 }
274
275 for (const auto& choice : original_variant.choices_) {
276 const auto& sanitized_choice_type = _type_overrides.type(*choice.second);
277
278 new_choices.emplace_back(_find_enumeration_mapping_by_range(
279 sanitized_selector, choice.first.range),
280 sanitized_choice_type.copy());
281 }
282
283 return lttng::make_unique<lst::variant_type<MappingIntegerType>>(
284 original_variant.alignment,
285 original_variant.selector_field_location,
286 std::move(new_choices));
287 }
288
289 template <typename MappingIntegerType>
290 void visit_variant(const lst::variant_type<MappingIntegerType>& type)
291 {
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",
299 };
300
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
305 * mapping. */
306 unsafe_names_found.insert(choice.first.name.c_str());
307 }
308 }
309
310 if (unsafe_names_found.empty()) {
311 return;
312 }
313
314 /*
315 * Look-up selector field type.
316 *
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).
319 *
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
323 * overrides.
324 */
325 const auto& original_selector_type = _lookup_type(type.selector_field_location);
326 const auto& overriden_selector_type = _type_overrides.type(original_selector_type);
327
328 auto sanitized_selector_type = _create_sanitized_selector(
329 static_cast<const lst::typed_enumeration_type<MappingIntegerType>&>(
330 overriden_selector_type),
331 unsafe_names_found);
332 _type_overrides.publish(original_selector_type, std::move(sanitized_selector_type));
333
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));
337 }
338
339 void visit(const lst::variant_type<
340 lst::signed_enumeration_type::mapping::range_t::range_integer_t>& type) final
341 {
342 visit_variant(type);
343 }
344
345 void visit(const lst::variant_type<
346 lst::unsigned_enumeration_type::mapping::range_t::range_integer_t>& type) final
347 {
348 visit_variant(type);
349 }
350
351 void visit(const lst::static_length_string_type& type __attribute__((unused))) final
352 {
353 }
354
355 void visit(const lst::dynamic_length_string_type& type __attribute__((unused))) final
356 {
357 }
358
359 tsdl::details::type_overrider& _type_overrides;
360 const type_lookup_function _lookup_type;
361 };
362
363 class tsdl_field_visitor : public lttng::sessiond::trace::field_visitor,
364 public lttng::sessiond::trace::type_visitor {
365 public:
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 =
370 nonstd::nullopt) :
371 _indentation_level{ indentation_level },
372 _trace_abi{ abi },
373
374 _default_clock_class_name{ in_default_clock_class_name ?
375 in_default_clock_class_name->c_str() :
376 nullptr },
377 _type_overrides{ type_overrides }
378 {
379 }
380
381 /* Only call once. */
382 std::string move_description()
383 {
384 return std::move(_description);
385 }
386
387 private:
388 void visit(const lst::field& field) final
389 {
390 /*
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.
396 */
397 _current_field_name.push(_bypass_identifier_escape ?
398 field.name :
399 escape_tsdl_identifier(field.name));
400 _type_overrides.type(field.get_type()).accept(*this);
401 _description += " ";
402 _description += _current_field_name.top();
403 _current_field_name.pop();
404
405 /*
406 * Some types requires suffixes to be appended (e.g. the length of arrays
407 * and sequences, the mappings of enumerations).
408 */
409 while (!_type_suffixes.empty()) {
410 _description += _type_suffixes.front();
411 _type_suffixes.pop();
412 }
413
414 _description += ";";
415 }
416
417 void visit(const lst::integer_type& type) final
418 {
419 _description += "integer { ";
420
421 /* Mandatory properties (no defaults). */
422 _description += fmt::format("size = {size}; align = {alignment};",
423 fmt::arg("size", type.size),
424 fmt::arg("alignment", type.alignment));
425
426 /* Defaults to unsigned. */
427 if (type.signedness_ == lst::integer_type::signedness::SIGNED) {
428 _description += " signed = true;";
429 }
430
431 /* Defaults to 10. */
432 if (type.base_ != lst::integer_type::base::DECIMAL) {
433 unsigned int base;
434
435 switch (type.base_) {
436 case lst::integer_type::base::BINARY:
437 base = 2;
438 break;
439 case lst::integer_type::base::OCTAL:
440 base = 8;
441 break;
442 case lst::integer_type::base::HEXADECIMAL:
443 base = 16;
444 break;
445 default:
446 LTTNG_THROW_ERROR(fmt::format(
447 "Unexpected base encountered while serializing integer type to TSDL: base = {}",
448 (int) type.base_));
449 }
450
451 _description += fmt::format(" base = {};", base);
452 }
453
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";
458
459 _description += fmt::format(" byte_order = {};", byte_order_str);
460 }
461
462 if (_current_integer_encoding_override) {
463 const char *encoding_str;
464
465 switch (*_current_integer_encoding_override) {
466 case lst::string_type::encoding::ASCII:
467 encoding_str = "ASCII";
468 break;
469 case lst::string_type::encoding::UTF8:
470 encoding_str = "UTF8";
471 break;
472 default:
473 LTTNG_THROW_ERROR(fmt::format(
474 "Unexpected encoding encountered while serializing integer type to TSDL: encoding = {}",
475 (int) *_current_integer_encoding_override));
476 }
477
478 _description += fmt::format(" encoding = {};", encoding_str);
479 _current_integer_encoding_override.reset();
480 }
481
482 if (std::find(type.roles_.begin(),
483 type.roles_.end(),
484 lst::integer_type::role::DEFAULT_CLOCK_TIMESTAMP) !=
485 type.roles_.end() ||
486 std::find(type.roles_.begin(),
487 type.roles_.end(),
488 lst::integer_type::role::PACKET_END_DEFAULT_CLOCK_TIMESTAMP) !=
489 type.roles_.end()) {
490 LTTNG_ASSERT(_default_clock_class_name);
491 _description +=
492 fmt::format(" map = clock.{}.value;", _default_clock_class_name);
493 }
494
495 _description += " }";
496 }
497
498 void visit(const lst::floating_point_type& type) final
499 {
500 _description += fmt::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));
505
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";
510
511 _description += fmt::format(" byte_order = {};", byte_order_str);
512 }
513
514 _description += " }";
515 }
516
517 template <class EnumerationType>
518 void visit_enumeration(const EnumerationType& type)
519 {
520 /* name follows, when applicable. */
521 _description += "enum : ";
522
523 visit(static_cast<const lst::integer_type&>(type));
524 _description += " {\n";
525
526 const auto mappings_indentation_level = _indentation_level + 1;
527
528 bool first_mapping = true;
529 for (const auto& mapping : *type.mappings_) {
530 if (!first_mapping) {
531 _description += ",\n";
532 }
533
534 _description.resize(_description.size() + mappings_indentation_level, '\t');
535 if (mapping.range.begin == mapping.range.end) {
536 _description +=
537 fmt::format("\"{mapping_name}\" = {mapping_value}",
538 fmt::arg("mapping_name", mapping.name),
539 fmt::arg("mapping_value", mapping.range.begin));
540 } else {
541 _description += fmt::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));
546 }
547
548 first_mapping = false;
549 }
550
551 _description += "\n";
552 _description.resize(_description.size() + _indentation_level, '\t');
553 _description += "}";
554 }
555
556 void visit(const lst::signed_enumeration_type& type) final
557 {
558 visit_enumeration(type);
559 }
560
561 void visit(const lst::unsigned_enumeration_type& type) final
562 {
563 visit_enumeration(type);
564 }
565
566 void visit(const lst::static_length_array_type& type) final
567 {
568 if (type.alignment != 0) {
569 LTTNG_ASSERT(_current_field_name.size() > 0);
570 _description += fmt::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');
575 }
576
577 type.element_type->accept(*this);
578 _type_suffixes.emplace(fmt::format("[{}]", type.length));
579 }
580
581 void visit(const lst::dynamic_length_array_type& type) final
582 {
583 if (type.alignment != 0) {
584 /*
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.
589 */
590 LTTNG_ASSERT(_current_field_name.size() > 0);
591 _description += fmt::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');
596 }
597
598 type.element_type->accept(*this);
599 _type_suffixes.emplace(fmt::format(
600 "[{}]",
601 _bypass_identifier_escape ?
602 *(type.length_field_location.elements_.end() - 1) :
603 escape_tsdl_identifier(
604 *(type.length_field_location.elements_.end() - 1))));
605 }
606
607 void visit(const lst::static_length_blob_type& type) final
608 {
609 /* This type doesn't exist in CTF 1.x, express it as a static length array of
610 * uint8_t. */
611 std::unique_ptr<const lst::type> uint8_element =
612 lttng::make_unique<lst::integer_type>(
613 8,
614 _trace_abi.byte_order,
615 8,
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);
620
621 visit(*array);
622 }
623
624 void visit(const lst::dynamic_length_blob_type& type) final
625 {
626 /* This type doesn't exist in CTF 1.x, express it as a dynamic length array of
627 * uint8_t. */
628 std::unique_ptr<const lst::type> uint8_element =
629 lttng::make_unique<lst::integer_type>(
630 0,
631 _trace_abi.byte_order,
632 8,
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);
637
638 visit(*array);
639 }
640
641 void visit(const lst::null_terminated_string_type& type) final
642 {
643 /* Defaults to UTF-8. */
644 if (type.encoding_ == lst::null_terminated_string_type::encoding::ASCII) {
645 _description += "string { encoding = ASCII }";
646 } else {
647 _description += "string";
648 }
649 }
650
651 void visit(const lst::structure_type& type) final
652 {
653 _indentation_level++;
654 _description += "struct {";
655
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);
662 }
663
664 _bypass_identifier_escape = previous_bypass_identifier_escape;
665
666 _indentation_level--;
667 if (type.fields_.size() != 0) {
668 _description += "\n";
669 _description.resize(_description.size() + _indentation_level, '\t');
670 }
671
672 _description += "}";
673 }
674
675 template <class MappingIntegerType>
676 void visit_variant(const lst::variant_type<MappingIntegerType>& type)
677 {
678 if (type.alignment != 0) {
679 LTTNG_ASSERT(_current_field_name.size() > 0);
680 _description += fmt::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');
685 }
686
687 _indentation_level++;
688 _description += fmt::format(
689 "variant <{}> {{\n",
690 _bypass_identifier_escape ?
691 *(type.selector_field_location.elements_.end() - 1) :
692 escape_tsdl_identifier(
693 *(type.selector_field_location.elements_.end() - 1)));
694
695 /*
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.
700 */
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 += fmt::format(" {};\n", field.first.name);
707 }
708
709 _bypass_identifier_escape = previous_bypass_identifier_escape;
710
711 _indentation_level--;
712 _description.resize(_description.size() + _indentation_level, '\t');
713 _description += "}";
714 }
715
716 void visit(const lst::variant_type<
717 lst::signed_enumeration_type::mapping::range_t::range_integer_t>& type) final
718 {
719 visit_variant(type);
720 }
721
722 void visit(const lst::variant_type<
723 lst::unsigned_enumeration_type::mapping::range_t::range_integer_t>& type) final
724 {
725 visit_variant(type);
726 }
727
728 lst::type::cuptr create_character_type(enum lst::string_type::encoding encoding)
729 {
730 _current_integer_encoding_override = encoding;
731 return lttng::make_unique<lst::integer_type>(
732 8,
733 _trace_abi.byte_order,
734 8,
735 lst::integer_type::signedness::UNSIGNED,
736 lst::integer_type::base::DECIMAL);
737 }
738
739 void visit(const lst::static_length_string_type& type) final
740 {
741 /*
742 * TSDL expresses static-length strings as arrays of 8-bit integer with
743 * an encoding specified.
744 */
745 const auto char_array = lttng::make_unique<lst::static_length_array_type>(
746 type.alignment, create_character_type(type.encoding_), type.length);
747
748 visit(*char_array);
749 }
750
751 void visit(const lst::dynamic_length_string_type& type) final
752 {
753 /*
754 * TSDL expresses dynamic-length strings as arrays of 8-bit integer with
755 * an encoding specified.
756 */
757 const auto char_sequence = lttng::make_unique<lst::dynamic_length_array_type>(
758 type.alignment,
759 create_character_type(type.encoding_),
760 type.length_field_location);
761
762 visit(*char_sequence);
763 }
764
765 std::stack<std::string> _current_field_name;
766 /*
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.
771 */
772 nonstd::optional<enum lst::string_type::encoding> _current_integer_encoding_override;
773
774 unsigned int _indentation_level;
775 const lst::abi& _trace_abi;
776
777 std::queue<std::string> _type_suffixes;
778
779 /* Description in TSDL format. */
780 std::string _description;
781
782 bool _bypass_identifier_escape{ false };
783 const char *_default_clock_class_name;
784 const tsdl::details::type_overrider& _type_overrides;
785 };
786
787 class tsdl_trace_environment_visitor : public lst::trace_class_environment_visitor {
788 public:
789 tsdl_trace_environment_visitor() : _environment{ "env {\n" }
790 {
791 }
792
793 void visit(const lst::environment_field<int64_t>& field) override
794 {
795 _environment += fmt::format(" {} = {};\n", field.name, field.value);
796 }
797
798 void visit(const lst::environment_field<const char *>& field) override
799 {
800 _environment += fmt::format(" {} = \"{}\";\n",
801 field.name,
802 escape_tsdl_env_string_value(field.value));
803 }
804
805 /* Only call once. */
806 std::string move_description()
807 {
808 _environment += "};\n\n";
809 return std::move(_environment);
810 }
811
812 private:
813 std::string _environment;
814 };
815 } /* namespace */
816
817 tsdl::trace_class_visitor::trace_class_visitor(
818 const lst::abi& trace_abi,
819 tsdl::append_metadata_fragment_function append_metadata_fragment) :
820 _trace_abi{ trace_abi }, _append_metadata_fragment(std::move(append_metadata_fragment))
821 {
822 }
823
824 void tsdl::trace_class_visitor::append_metadata_fragment(const std::string& fragment) const
825 {
826 _append_metadata_fragment(fragment);
827 }
828
829 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::trace_class& trace_class)
830 {
831 /* Ensure this instance is not used against multiple trace classes. */
832 LTTNG_ASSERT(!_current_trace_class || _current_trace_class == &trace_class);
833 _current_trace_class = &trace_class;
834
835 tsdl_field_visitor packet_header_visitor{ trace_class.abi, 1, _sanitized_types_overrides };
836
837 trace_class.packet_header()->accept(packet_header_visitor);
838
839 /* Declare type aliases, trace class, and packet header. */
840 auto trace_class_tsdl = fmt::format(
841 "/* CTF {ctf_major}.{ctf_minor} */\n\n"
842 "trace {{\n"
843 " major = {ctf_major};\n"
844 " minor = {ctf_minor};\n"
845 " uuid = \"{uuid}\";\n"
846 " byte_order = {byte_order};\n"
847 " packet.header := {packet_header_layout};\n"
848 "}};\n\n",
849 fmt::arg("ctf_major", ctf_spec_major),
850 fmt::arg("ctf_minor", ctf_spec_minor),
851 fmt::arg("uint8_t_alignment", trace_class.abi.uint8_t_alignment),
852 fmt::arg("uint16_t_alignment", trace_class.abi.uint16_t_alignment),
853 fmt::arg("uint32_t_alignment", trace_class.abi.uint32_t_alignment),
854 fmt::arg("uint64_t_alignment", trace_class.abi.uint64_t_alignment),
855 fmt::arg("long_alignment", trace_class.abi.long_alignment),
856 fmt::arg("long_size", trace_class.abi.long_alignment),
857 fmt::arg("bits_per_long", trace_class.abi.bits_per_long),
858 fmt::arg("uuid", lttng::utils::uuid_to_str(trace_class.uuid)),
859 fmt::arg("byte_order",
860 trace_class.abi.byte_order == lst::byte_order::BIG_ENDIAN_ ? "be" : "le"),
861 fmt::arg("packet_header_layout", packet_header_visitor.move_description()));
862
863 /* Declare trace scope and type aliases. */
864 append_metadata_fragment(trace_class_tsdl);
865
866 tsdl_trace_environment_visitor environment_visitor;
867 trace_class.accept(environment_visitor);
868 append_metadata_fragment(environment_visitor.move_description());
869 }
870
871 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::clock_class& clock_class)
872 {
873 auto uuid_str = clock_class.uuid ?
874 fmt::format(" uuid = \"{}\";\n", lttng::utils::uuid_to_str(*clock_class.uuid)) :
875 "";
876
877 /* Assumes a single clock that maps to specific stream class fields/roles. */
878 auto clock_class_str = fmt::format("clock {{\n"
879 " name = \"{name}\";\n"
880 /* Optional uuid. */
881 "{uuid}"
882 " description = \"{description}\";\n"
883 " freq = {frequency};\n"
884 " offset = {offset};\n"
885 "}};\n"
886 "\n",
887 fmt::arg("name", clock_class.name),
888 fmt::arg("uuid", uuid_str),
889 fmt::arg("description", clock_class.description),
890 fmt::arg("frequency", clock_class.frequency),
891 fmt::arg("offset", clock_class.offset));
892
893 append_metadata_fragment(clock_class_str);
894 }
895
896 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::stream_class& stream_class)
897 {
898 _current_stream_class = &stream_class;
899 const auto clear_stream_class_on_exit =
900 lttng::make_scope_exit([this]() noexcept { _current_stream_class = nullptr; });
901
902 auto stream_class_str = fmt::format("stream {{\n"
903 " id = {};\n",
904 stream_class.id);
905 variant_tsdl_keyword_sanitizer variant_sanitizer(
906 _sanitized_types_overrides,
907 [this](const lttng::sessiond::trace::field_location& location) -> const lst::type& {
908 return _lookup_field_type(location);
909 });
910
911 const auto *event_header = stream_class.event_header();
912 if (event_header) {
913 tsdl_field_visitor event_header_visitor{ _trace_abi,
914 1,
915 _sanitized_types_overrides,
916 stream_class.default_clock_class_name };
917
918 event_header->accept(variant_sanitizer);
919 event_header->accept(event_header_visitor);
920 stream_class_str += fmt::format(" event.header := {};\n",
921 event_header_visitor.move_description());
922 }
923
924 const auto *packet_context = stream_class.packet_context();
925 if (packet_context) {
926 tsdl_field_visitor packet_context_visitor{ _trace_abi,
927 1,
928 _sanitized_types_overrides,
929 stream_class.default_clock_class_name };
930
931 packet_context->accept(variant_sanitizer);
932 packet_context->accept(packet_context_visitor);
933 stream_class_str += fmt::format(" packet.context := {};\n",
934 packet_context_visitor.move_description());
935 }
936
937 const auto *event_context = stream_class.event_context();
938 if (event_context) {
939 tsdl_field_visitor event_context_visitor{ _trace_abi,
940 1,
941 _sanitized_types_overrides };
942
943 event_context->accept(variant_sanitizer);
944 event_context->accept(event_context_visitor);
945 stream_class_str += fmt::format(" event.context := {};\n",
946 event_context_visitor.move_description());
947 }
948
949 stream_class_str += "};\n\n";
950
951 append_metadata_fragment(stream_class_str);
952 }
953
954 void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::event_class& event_class)
955 {
956 _current_event_class = &event_class;
957 const auto clear_event_class_on_exit =
958 lttng::make_scope_exit([this]() noexcept { _current_event_class = nullptr; });
959
960 auto event_class_str = fmt::format("event {{\n"
961 " name = \"{name}\";\n"
962 " id = {id};\n"
963 " stream_id = {stream_class_id};\n"
964 " loglevel = {log_level};\n",
965 fmt::arg("name", event_class.name),
966 fmt::arg("id", event_class.id),
967 fmt::arg("stream_class_id", event_class.stream_class_id),
968 fmt::arg("log_level", event_class.log_level));
969
970 if (event_class.model_emf_uri) {
971 event_class_str +=
972 fmt::format(" model.emf.uri = \"{}\";\n", *event_class.model_emf_uri);
973 }
974
975 tsdl_field_visitor payload_visitor{ _trace_abi, 1, _sanitized_types_overrides };
976 variant_tsdl_keyword_sanitizer variant_sanitizer(
977 _sanitized_types_overrides,
978 [this](const lttng::sessiond::trace::field_location& location) -> const lst::type& {
979 return _lookup_field_type(location);
980 });
981
982 event_class.payload->accept(variant_sanitizer);
983 event_class.payload->accept(payload_visitor);
984
985 event_class_str +=
986 fmt::format(" fields := {};\n}};\n\n", payload_visitor.move_description());
987
988 append_metadata_fragment(event_class_str);
989 }
990
991 void tsdl::details::type_overrider::publish(const lttng::sessiond::trace::type& original_type,
992 lttng::sessiond::trace::type::cuptr new_type_override)
993 {
994 auto current_override = _overriden_types.find(&original_type);
995
996 if (current_override != _overriden_types.end()) {
997 current_override->second = std::move(new_type_override);
998 } else {
999 _overriden_types.insert(
1000 std::make_pair(&original_type, std::move(new_type_override)));
1001 }
1002 }
1003
1004 const lst::type&
1005 tsdl::details::type_overrider::type(const lttng::sessiond::trace::type& original) const noexcept
1006 {
1007 const auto result = _overriden_types.find(&original);
1008
1009 if (result != _overriden_types.end()) {
1010 /* Provide the overriden type. */
1011 return *result->second;
1012 }
1013
1014 /* Pass the original type through. */
1015 return original;
1016 }
1017
1018 namespace {
1019 const lttng::sessiond::trace::type&
1020 lookup_type_from_root_type(const lttng::sessiond::trace::type& root_type,
1021 const lttng::sessiond::trace::field_location& field_location)
1022 {
1023 const auto *type = &root_type;
1024
1025 for (const auto& location_element : field_location.elements_) {
1026 /* Only structures can be traversed. */
1027 const auto *struct_type = dynamic_cast<const lst::structure_type *>(type);
1028
1029 /*
1030 * Traverse the type by following the field location path.
1031 *
1032 * While field paths are assumed to have been validated before-hand,
1033 * a dynamic cast is performed here as an additional precaution
1034 * since none of this is performance-critical; it can be removed
1035 * safely.
1036 */
1037 if (!struct_type) {
1038 LTTNG_THROW_ERROR(fmt::format(
1039 "Encountered a type that is not a structure while traversing field location: field-location=`{}`",
1040 field_location));
1041 }
1042
1043 const auto field_found_it =
1044 std::find_if(struct_type->fields_.cbegin(),
1045 struct_type->fields_.cend(),
1046 [&location_element](const lst::field::cuptr& struct_field) {
1047 return struct_field->name == location_element;
1048 });
1049
1050 if (field_found_it == struct_type->fields_.cend()) {
1051 LTTNG_THROW_ERROR(fmt::format(
1052 "Failed to find field using field location: field-name:=`{field_name}`, field-location=`{field_location}`",
1053 fmt::arg("field_location", field_location),
1054 fmt::arg("field_name", location_element)));
1055 }
1056
1057 type = &(*field_found_it)->get_type();
1058 }
1059
1060 return *type;
1061 }
1062 } /* anonymous namespace. */
1063
1064 /*
1065 * The trace hierarchy is assumed to have been validated on creation.
1066 * This function can only fail due to a validation error, hence
1067 * why it throws on any unexpected/invalid field location.
1068 *
1069 * Does not return an overriden field type; it returns the original field type
1070 * as found in the trace hierarchy.
1071 */
1072 const lttng::sessiond::trace::type& lttng::sessiond::tsdl::trace_class_visitor::_lookup_field_type(
1073 const lttng::sessiond::trace::field_location& location) const
1074 {
1075 /* Validate the look-up is happening in a valid visit context. */
1076 switch (location.root_) {
1077 case lst::field_location::root::EVENT_RECORD_HEADER:
1078 case lst::field_location::root::EVENT_RECORD_PAYLOAD:
1079 if (!_current_event_class) {
1080 LTTNG_THROW_ERROR(
1081 "Field type look-up failure: no current event class in visitor's context");
1082 }
1083 /* fall through. */
1084 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT:
1085 case lst::field_location::root::PACKET_CONTEXT:
1086 if (!_current_stream_class) {
1087 LTTNG_THROW_ERROR(
1088 "Field type look-up failure: no current stream class in visitor's context");
1089 }
1090 /* fall through. */
1091 case lst::field_location::root::PACKET_HEADER:
1092 if (!_current_trace_class) {
1093 LTTNG_THROW_ERROR(
1094 "Field type look-up failure: no current trace class in visitor's context");
1095 }
1096
1097 break;
1098 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT:
1099 LTTNG_THROW_UNSUPPORTED_ERROR(
1100 "Field type look-up failure: event-record specific contexts are not supported");
1101 default:
1102 LTTNG_THROW_UNSUPPORTED_ERROR(
1103 "Field type look-up failure: unknown field location root");
1104 }
1105
1106 switch (location.root_) {
1107 case lst::field_location::root::PACKET_HEADER:
1108 return lookup_type_from_root_type(*_current_trace_class->packet_header(), location);
1109 case lst::field_location::root::PACKET_CONTEXT:
1110 return lookup_type_from_root_type(*_current_stream_class->packet_context(),
1111 location);
1112 case lst::field_location::root::EVENT_RECORD_HEADER:
1113 return lookup_type_from_root_type(*_current_stream_class->event_header(), location);
1114 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT:
1115 return lookup_type_from_root_type(*_current_stream_class->event_context(),
1116 location);
1117 case lst::field_location::root::EVENT_RECORD_PAYLOAD:
1118 return lookup_type_from_root_type(*_current_event_class->payload, location);
1119 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT:
1120 default:
1121 /* Unreachable as it was checked before. */
1122 abort();
1123 }
1124 }
This page took 0.103554 seconds and 5 git commands to generate.