Clean-up: sessiond: rename public accessors
[lttng-tools.git] / src / bin / lttng-sessiond / tsdl-trace-class-visitor.cpp
CommitLineData
d7bfb9b0
JG
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
d7bfb9b0 8#include "tsdl-trace-class-visitor.hpp"
24ed18f2 9#include "clock-class.hpp"
d7bfb9b0
JG
10
11#include <common/exception.hpp>
12#include <common/format.hpp>
13#include <common/make-unique.hpp>
14#include <common/uuid.hpp>
b6bbb1d6 15#include <common/scope-exit.hpp>
d7bfb9b0 16
24ed18f2
JG
17#include <vendor/optional.hpp>
18
19#include <algorithm>
d7bfb9b0 20#include <array>
d7bfb9b0 21#include <locale>
2f35b2f5
JG
22#include <queue>
23#include <set>
24ed18f2 24#include <stack>
b6bbb1d6 25#include <unordered_set>
d7bfb9b0
JG
26
27namespace lst = lttng::sessiond::trace;
28namespace tsdl = lttng::sessiond::tsdl;
29
30namespace {
31const auto ctf_spec_major = 1;
32const auto ctf_spec_minor = 8;
33
2f35b2f5
JG
34/*
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.
37 */
b6bbb1d6 38const std::unordered_set<std::string> safe_tsdl_identifiers = {
24ed18f2
JG
39 "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};
2f35b2f5 54
d7bfb9b0
JG
55/*
56 * A previous implementation always prepended '_' to the identifiers in order to
57 * side-step the problem of escaping TSDL keywords and ensuring identifiers
58 * started with an alphabetic character.
59 *
60 * Changing this behaviour to a smarter algorithm would break readers that have
61 * come to expect this initial underscore.
62 */
63std::string escape_tsdl_identifier(const std::string& original_identifier)
64{
65 if (original_identifier.size() == 0) {
66 LTTNG_THROW_ERROR("Invalid 0-length identifier used in trace description");
67 }
68
2f35b2f5
JG
69 if (safe_tsdl_identifiers.find(original_identifier) != safe_tsdl_identifiers.end()) {
70 return original_identifier;
71 }
72
d7bfb9b0
JG
73 std::string new_identifier;
74 /* Optimisticly assume most identifiers are valid and allocate the same length. */
75 new_identifier.reserve(original_identifier.size());
76 new_identifier = "_";
77
78 /* Replace illegal characters by '_'. */
79 std::locale c_locale{"C"};
80 for (const auto current_char : original_identifier) {
81 if (!std::isalnum(current_char, c_locale) && current_char != '_') {
82 new_identifier += '_';
83 } else {
84 new_identifier += current_char;
85 }
86 }
87
88 return new_identifier;
89}
90
91std::string escape_tsdl_env_string_value(const std::string& original_string)
92{
93 std::string escaped_string;
94
95 escaped_string.reserve(original_string.size());
96
97 for (const auto c : original_string) {
98 switch (c) {
99 case '\n':
100 escaped_string += "\\n";
101 break;
102 case '\\':
103 escaped_string += "\\\\";
104 break;
105 case '"':
106 escaped_string += "\"";
107 break;
108 default:
109 escaped_string += c;
110 break;
111 }
112 }
113
114 return escaped_string;
115}
116
b6bbb1d6
JG
117/*
118 * Variants produced by LTTng-UST contain TSDL-unsafe names. A variant/selector
119 * sanitization pass is performed before serializing a trace class hierarchy to
120 * TSDL.
121 *
122 * The variant_tsdl_keyword_sanitizer visitor is used to visit field before it
123 * is handed-over to the actual TSDL-producing visitor.
124 *
125 * As it visits fields, the variant_tsdl_keyword_sanitizer populates a
126 * "type_overrider" with TSDL-safe replacements for any variant or enumeration
127 * that uses TSDL-unsafe identifiers (reserved keywords).
128 *
129 * The type_overrider, in turn, is used by the rest of the TSDL serialization
130 * visitor (tsdl_field_visitor) to swap any TSDL-unsafe types with their
131 * sanitized version.
132 *
133 * The tsdl_field_visitor owns the type_overrider and only briefly shares it
134 * with the variant_tsdl_keyword_sanitizer which takes a reference to it.
135 */
136class variant_tsdl_keyword_sanitizer : public lttng::sessiond::trace::field_visitor,
137 public lttng::sessiond::trace::type_visitor {
138public:
139 using type_lookup_function = std::function<const lst::type&(const lst::field_location&)>;
140
141 variant_tsdl_keyword_sanitizer(tsdl::details::type_overrider& type_overrides,
142 type_lookup_function lookup_type) :
143 _type_overrides{type_overrides}, _lookup_type(lookup_type)
144 {
145 }
146
147private:
148 class _c_string_comparator {
149 public:
150 int operator()(const char *lhs, const char *rhs) const
151 {
152 return std::strcmp(lhs, rhs) < 0;
153 }
154 };
155 using unsafe_names = std::set<const char *, _c_string_comparator>;
156
157 virtual void visit(const lst::field& field) override final
158 {
159 _type_overrides.type(field.get_type()).accept(*this);
160 }
161
162 virtual void visit(const lst::integer_type& type __attribute__((unused))) override final
163 {
164 }
165
166 virtual void visit(const lst::floating_point_type& type __attribute__((unused))) override final
167 {
168 }
169
170 virtual void visit(const lst::signed_enumeration_type& type __attribute__((unused))) override final
171 {
172 }
173
174 virtual void visit(const lst::unsigned_enumeration_type& type __attribute__((unused))) override final
175 {
176 }
177
178 virtual void visit(const lst::static_length_array_type& type __attribute__((unused))) override final
179 {
180 }
181
182 virtual void visit(const lst::dynamic_length_array_type& type __attribute__((unused))) override final
183 {
184 }
185
186 virtual void visit(const lst::static_length_blob_type& type __attribute__((unused))) override final
187 {
188 }
189
190 virtual void visit(const lst::dynamic_length_blob_type& type __attribute__((unused))) override final
191 {
192 }
193
194 virtual void visit(const lst::null_terminated_string_type& type __attribute__((unused))) override final
195 {
196 }
197
198 virtual void visit(const lst::structure_type& type) override final
199 {
200 /* Recurse into structure attributes. */
201 for (const auto& field : type.fields_) {
202 field->accept(*this);
203 }
204 }
205
206 /*
207 * Create a new enumeration type replacing any mapping that match, by name, the elements in `unsafe_names_found`
208 * with a TSDL-safe version. Currently, unsafe identifiers are made safe by adding
209 * a leading underscore.
210 */
211 template <typename MappingIntegerType>
212 lst::type::cuptr _create_sanitized_selector(
213 const lst::typed_enumeration_type<MappingIntegerType>& original_selector,
214 const unsafe_names& unsafe_names_found)
215 {
216 auto new_mappings = std::make_shared<typename lst::typed_enumeration_type<
217 MappingIntegerType>::mappings>();
218
219 for (const auto& mapping : *original_selector.mappings_) {
220 if (unsafe_names_found.find(mapping.name.c_str()) ==
221 unsafe_names_found.end()) {
222 /* Mapping is safe, simply copy it. */
223 new_mappings->emplace_back(mapping);
224 } else {
225 /* Unsafe mapping, rename it and keep the rest of its attributes. */
226 new_mappings->emplace_back(
227 fmt::format("_{}", mapping.name), mapping.range);
228 }
229 }
230
231 return lttng::make_unique<lst::typed_enumeration_type<MappingIntegerType>>(
232 original_selector.alignment, original_selector.byte_order,
233 original_selector.size, original_selector.base_, new_mappings);
234 }
235
236 template <typename MappingIntegerType>
237 const typename lst::typed_enumeration_type<MappingIntegerType>::mapping&
238 _find_enumeration_mapping_by_range(
239 const typename lst::typed_enumeration_type<MappingIntegerType>&
240 enumeration_type,
241 const typename lst::typed_enumeration_type<
242 MappingIntegerType>::mapping::range_t& target_mapping_range)
243 {
244 for (const auto& mapping : *enumeration_type.mappings_) {
245 if (mapping.range == target_mapping_range) {
246 return mapping;
247 }
248 }
249
250 LTTNG_THROW_ERROR(fmt::format(
251 "Failed to find mapping by range in enumeration while sanitizing a variant: target_mapping_range={}",
252 target_mapping_range));
253 }
254
255 /*
256 * Copy `original_variant`, but use the mappings of a previously-published sanitized tag
257 * to produce a TSDL-safe version of the variant.
258 */
259 template <typename MappingIntegerType>
260 lst::type::cuptr _create_sanitized_variant(
261 const lst::variant_type<MappingIntegerType>& original_variant)
262 {
263 typename lst::variant_type<MappingIntegerType>::choices new_choices;
264 const auto& sanitized_selector = static_cast<
265 const lst::typed_enumeration_type<MappingIntegerType>&>(
266 _type_overrides.type(_lookup_type(
267 original_variant.selector_field_location)));
268
269 /* Visit variant choices to sanitize them as needed. */
270 for (const auto& choice : original_variant.choices_) {
271 choice.second->accept(*this);
272 }
273
274 for (const auto& choice : original_variant.choices_) {
275 const auto& sanitized_choice_type = _type_overrides.type(*choice.second);
276
277 new_choices.emplace_back(
278 _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",
295 "callsite",
296 "const",
297 "char",
298 "clock",
299 "double",
300 "enum",
301 "env",
302 "event",
303 "floating_point",
304 "float",
305 "integer",
306 "int",
307 "long",
308 "short",
309 "signed",
310 "stream",
311 "string",
312 "struct",
313 "trace",
314 "typealias",
315 "typedef",
316 "unsigned",
317 "variant",
318 "void",
319 "_Bool",
320 "_Complex",
321 "_Imaginary",
322 };
323
324 for (const auto& choice : type.choices_) {
325 if (tsdl_protected_keywords.find(choice.first.name) != tsdl_protected_keywords.cend()) {
326 /* Choice name is illegal, we have to rename it and its matching mapping. */
327 unsafe_names_found.insert(choice.first.name.c_str());
328 }
329 }
330
331 if (unsafe_names_found.empty()) {
332 return;
333 }
334
335 /*
336 * Look-up selector field type.
337 *
338 * Since it may have been overriden previously, keep the original and overriden
339 * selector field types (which may be the same, if the original was not overriden).
340 *
341 * We work from the "overriden" selector field type to preserve any existing
342 * modifications. However, the original field type will be used to publish the new
343 * version of the type leaving only the most recent overriden type in the type
344 * overrides.
345 */
346 const auto& original_selector_type = _lookup_type(type.selector_field_location);
347 const auto& overriden_selector_type = _type_overrides.type(original_selector_type);
348
349 auto sanitized_selector_type = _create_sanitized_selector(
350 static_cast<const lst::typed_enumeration_type<MappingIntegerType>&>(
351 overriden_selector_type), unsafe_names_found);
352 _type_overrides.publish(original_selector_type, std::move(sanitized_selector_type));
353
354 auto sanitized_variant_type = _create_sanitized_variant(
355 static_cast<const lst::variant_type<MappingIntegerType>&>(type));
356 _type_overrides.publish(type, std::move(sanitized_variant_type));
357 }
358
359 virtual void visit(const lst::variant_type<lst::signed_enumeration_type::mapping::range_t::range_integer_t>& type) override final
360 {
361 visit_variant(type);
362 }
363
364 virtual void visit(const lst::variant_type<lst::unsigned_enumeration_type::mapping::range_t::range_integer_t>& type) override final
365 {
366 visit_variant(type);
367 }
368
369 virtual void visit(const lst::static_length_string_type& type __attribute__((unused))) override final
370 {
371 }
372
373 virtual void visit(const lst::dynamic_length_string_type& type __attribute__((unused))) override final
374 {
375 }
376
377 tsdl::details::type_overrider& _type_overrides;
378 const type_lookup_function _lookup_type;
379};
380
d7bfb9b0
JG
381class tsdl_field_visitor : public lttng::sessiond::trace::field_visitor,
382 public lttng::sessiond::trace::type_visitor {
383public:
24ed18f2
JG
384 tsdl_field_visitor(const lst::abi& abi,
385 unsigned int indentation_level,
b6bbb1d6 386 const tsdl::details::type_overrider& type_overrides,
24ed18f2
JG
387 const nonstd::optional<std::string>& in_default_clock_class_name =
388 nonstd::nullopt) :
389 _indentation_level{indentation_level},
390 _trace_abi{abi},
391 _bypass_identifier_escape{false},
392 _default_clock_class_name{in_default_clock_class_name ?
393 in_default_clock_class_name->c_str() :
b6bbb1d6
JG
394 nullptr},
395 _type_overrides{type_overrides}
d7bfb9b0
JG
396 {
397 }
398
da9dd521 399 /* Only call once. */
c22ded12 400 std::string move_description()
d7bfb9b0 401 {
da9dd521 402 return std::move(_description);
d7bfb9b0
JG
403 }
404
405private:
406 virtual void visit(const lst::field& field) override final
407 {
408 /*
409 * Hack: keep the name of the field being visited since
410 * the tracers can express sequences, variants, and arrays with an alignment
411 * constraint, which is not expressible in TSDL. To work around this limitation, an
412 * empty structure declaration is inserted when needed to express the aligment
413 * constraint. The name of this structure is generated using the field's name.
414 */
24ed18f2
JG
415 _current_field_name.push(_bypass_identifier_escape ?
416 field.name : escape_tsdl_identifier(field.name));
b6bbb1d6 417 _type_overrides.type(field.get_type()).accept(*this);
d7bfb9b0 418 _description += " ";
24ed18f2
JG
419 _description += _current_field_name.top();
420 _current_field_name.pop();
d7bfb9b0
JG
421
422 /*
423 * Some types requires suffixes to be appended (e.g. the length of arrays
424 * and sequences, the mappings of enumerations).
425 */
426 while (!_type_suffixes.empty()) {
427 _description += _type_suffixes.front();
428 _type_suffixes.pop();
429 }
430
431 _description += ";";
d7bfb9b0
JG
432 }
433
434 virtual void visit(const lst::integer_type& type) override final
435 {
436 _description += "integer { ";
437
438 /* Mandatory properties (no defaults). */
439 _description += fmt::format("size = {size}; align = {alignment};",
440 fmt::arg("size", type.size),
441 fmt::arg("alignment", type.alignment));
442
443 /* Defaults to unsigned. */
65cd3c0c 444 if (type.signedness_ == lst::integer_type::signedness::SIGNED) {
d7bfb9b0
JG
445 _description += " signed = true;";
446 }
447
448 /* Defaults to 10. */
65cd3c0c 449 if (type.base_ != lst::integer_type::base::DECIMAL) {
d7bfb9b0
JG
450 unsigned int base;
451
65cd3c0c 452 switch (type.base_) {
d7bfb9b0
JG
453 case lst::integer_type::base::BINARY:
454 base = 2;
455 break;
456 case lst::integer_type::base::OCTAL:
457 base = 8;
458 break;
459 case lst::integer_type::base::HEXADECIMAL:
460 base = 16;
461 break;
462 default:
463 LTTNG_THROW_ERROR(fmt::format(
464 "Unexpected base encountered while serializing integer type to TSDL: base = {}",
65cd3c0c 465 (int) type.base_));
d7bfb9b0
JG
466 }
467
468 _description += fmt::format(" base = {};", base);
469 }
470
471 /* Defaults to the trace's native byte order. */
472 if (type.byte_order != _trace_abi.byte_order) {
473 const auto byte_order_str = type.byte_order == lst::byte_order::BIG_ENDIAN_ ? "be" : "le";
474
475 _description += fmt::format(" byte_order = {};", byte_order_str);
476 }
477
478 if (_current_integer_encoding_override) {
479 const char *encoding_str;
480
481 switch (*_current_integer_encoding_override) {
482 case lst::string_type::encoding::ASCII:
483 encoding_str = "ASCII";
484 break;
485 case lst::string_type::encoding::UTF8:
486 encoding_str = "UTF8";
487 break;
488 default:
489 LTTNG_THROW_ERROR(fmt::format(
490 "Unexpected encoding encountered while serializing integer type to TSDL: encoding = {}",
491 (int) *_current_integer_encoding_override));
492 }
493
494 _description += fmt::format(" encoding = {};", encoding_str);
495 _current_integer_encoding_override.reset();
496 }
497
24ed18f2
JG
498 if (std::find(type.roles_.begin(), type.roles_.end(),
499 lst::integer_type::role::DEFAULT_CLOCK_TIMESTAMP) !=
500 type.roles_.end() ||
501 std::find(type.roles_.begin(), type.roles_.end(),
502 lst::integer_type::role::
503 PACKET_END_DEFAULT_CLOCK_TIMESTAMP) !=
504 type.roles_.end()) {
505 LTTNG_ASSERT(_default_clock_class_name);
506 _description += fmt::format(
507 " map = clock.{}.value;", _default_clock_class_name);
508 }
509
d7bfb9b0
JG
510 _description += " }";
511 }
512
513 virtual void visit(const lst::floating_point_type& type) override final
514 {
515 _description += fmt::format(
516 "floating_point {{ align = {alignment}; mant_dig = {mantissa_digits}; exp_dig = {exponent_digits};",
517 fmt::arg("alignment", type.alignment),
518 fmt::arg("mantissa_digits", type.mantissa_digits),
519 fmt::arg("exponent_digits", type.exponent_digits));
520
521 /* Defaults to the trace's native byte order. */
522 if (type.byte_order != _trace_abi.byte_order) {
523 const auto byte_order_str = type.byte_order == lst::byte_order::BIG_ENDIAN_ ? "be" : "le";
524
525 _description += fmt::format(" byte_order = {};", byte_order_str);
526 }
527
528 _description += " }";
529 }
530
531 template <class EnumerationType>
532 void visit_enumeration(const EnumerationType& type)
533 {
534 /* name follows, when applicable. */
535 _description += "enum : ";
536
b6bbb1d6
JG
537 visit(static_cast<const lst::integer_type&>(type));
538 _description += " {\n";
d7bfb9b0
JG
539
540 const auto mappings_indentation_level = _indentation_level + 1;
541
542 bool first_mapping = true;
da9dd521 543 for (const auto& mapping : *type.mappings_) {
d7bfb9b0
JG
544 if (!first_mapping) {
545 _description += ",\n";
546 }
547
548 _description.resize(_description.size() + mappings_indentation_level, '\t');
da9dd521 549 if (mapping.range.begin == mapping.range.end) {
d7bfb9b0
JG
550 _description += fmt::format(
551 "\"{mapping_name}\" = {mapping_value}",
552 fmt::arg("mapping_name", mapping.name),
da9dd521 553 fmt::arg("mapping_value", mapping.range.begin));
d7bfb9b0
JG
554 } else {
555 _description += fmt::format(
556 "\"{mapping_name}\" = {mapping_range_begin} ... {mapping_range_end}",
557 fmt::arg("mapping_name", mapping.name),
558 fmt::arg("mapping_range_begin",
da9dd521
JG
559 mapping.range.begin),
560 fmt::arg("mapping_range_end", mapping.range.end));
d7bfb9b0
JG
561 }
562
563 first_mapping = false;
564 }
565
566 _description += "\n";
567 _description.resize(_description.size() + _indentation_level, '\t');
568 _description += "}";
569 }
570
571 virtual void visit(const lst::signed_enumeration_type& type) override final
572 {
573 visit_enumeration(type);
574 }
575
576 virtual void visit(const lst::unsigned_enumeration_type& type) override final
577 {
578 visit_enumeration(type);
579 }
580
581 virtual void visit(const lst::static_length_array_type& type) override final
582 {
583 if (type.alignment != 0) {
24ed18f2 584 LTTNG_ASSERT(_current_field_name.size() > 0);
d7bfb9b0
JG
585 _description += fmt::format(
586 "struct {{ }} align({alignment}) {field_name}_padding;\n",
587 fmt::arg("alignment", type.alignment),
24ed18f2 588 fmt::arg("field_name", _current_field_name.top()));
d7bfb9b0
JG
589 _description.resize(_description.size() + _indentation_level, '\t');
590 }
591
592 type.element_type->accept(*this);
593 _type_suffixes.emplace(fmt::format("[{}]", type.length));
594 }
595
596 virtual void visit(const lst::dynamic_length_array_type& type) override final
597 {
598 if (type.alignment != 0) {
599 /*
600 * Note that this doesn't support nested sequences. For
601 * the moment, tracers can't express those. However, we
602 * could wrap nested sequences in structures, which
603 * would allow us to express alignment constraints.
604 */
24ed18f2 605 LTTNG_ASSERT(_current_field_name.size() > 0);
d7bfb9b0
JG
606 _description += fmt::format(
607 "struct {{ }} align({alignment}) {field_name}_padding;\n",
608 fmt::arg("alignment", type.alignment),
24ed18f2 609 fmt::arg("field_name", _current_field_name.top()));
d7bfb9b0
JG
610 _description.resize(_description.size() + _indentation_level, '\t');
611 }
612
613 type.element_type->accept(*this);
24ed18f2
JG
614 _type_suffixes.emplace(fmt::format("[{}]",
615 _bypass_identifier_escape ?
eda1aa02
JG
616 *(type.length_field_location.elements_.end() - 1) :
617 escape_tsdl_identifier(*(type.length_field_location.elements_.end() - 1))));
d7bfb9b0
JG
618 }
619
e7360180
JG
620 virtual void visit(const lst::static_length_blob_type& type) override final
621 {
622 /* This type doesn't exist in CTF 1.x, express it as a static length array of uint8_t. */
623 std::unique_ptr<const lst::type> uint8_element = lttng::make_unique<lst::integer_type>(8,
624 _trace_abi.byte_order, 8, lst::integer_type::signedness::UNSIGNED,
625 lst::integer_type::base::HEXADECIMAL);
626 const auto array = lttng::make_unique<lst::static_length_array_type>(
627 type.alignment, std::move(uint8_element), type.length_bytes);
628
629 visit(*array);
630 }
631
632 virtual void visit(const lst::dynamic_length_blob_type& type) override final
633 {
634 /* This type doesn't exist in CTF 1.x, express it as a dynamic length array of uint8_t. */
635 std::unique_ptr<const lst::type> uint8_element = lttng::make_unique<lst::integer_type>(0,
636 _trace_abi.byte_order, 8, lst::integer_type::signedness::UNSIGNED,
637 lst::integer_type::base::HEXADECIMAL);
638 const auto array = lttng::make_unique<lst::dynamic_length_array_type>(
eda1aa02 639 type.alignment, std::move(uint8_element), type.length_field_location);
e7360180
JG
640
641 visit(*array);
642 }
643
d7bfb9b0
JG
644 virtual void visit(const lst::null_terminated_string_type& type) override final
645 {
24ed18f2 646 /* Defaults to UTF-8. */
65cd3c0c 647 if (type.encoding_ == lst::null_terminated_string_type::encoding::ASCII) {
d7bfb9b0
JG
648 _description += "string { encoding = ASCII }";
649 } else {
650 _description += "string";
651 }
652 }
653
654 virtual void visit(const lst::structure_type& type) override final
655 {
656 _indentation_level++;
657 _description += "struct {";
658
24ed18f2
JG
659 const auto previous_bypass_identifier_escape = _bypass_identifier_escape;
660 _bypass_identifier_escape = false;
da9dd521 661 for (const auto& field : type.fields_) {
d7bfb9b0
JG
662 _description += "\n";
663 _description.resize(_description.size() + _indentation_level, '\t');
664 field->accept(*this);
665 }
666
24ed18f2
JG
667 _bypass_identifier_escape = previous_bypass_identifier_escape;
668
d7bfb9b0 669 _indentation_level--;
da9dd521 670 if (type.fields_.size() != 0) {
d7bfb9b0
JG
671 _description += "\n";
672 _description.resize(_description.size() + _indentation_level, '\t');
673 }
674
24ed18f2 675 _description += "}";
d7bfb9b0
JG
676 }
677
45110cdd
JG
678 template <class MappingIntegerType>
679 void visit_variant(const lst::variant_type<MappingIntegerType>& type)
d7bfb9b0
JG
680 {
681 if (type.alignment != 0) {
24ed18f2 682 LTTNG_ASSERT(_current_field_name.size() > 0);
d7bfb9b0
JG
683 _description += fmt::format(
684 "struct {{ }} align({alignment}) {field_name}_padding;\n",
685 fmt::arg("alignment", type.alignment),
24ed18f2 686 fmt::arg("field_name", _current_field_name.top()));
d7bfb9b0
JG
687 _description.resize(_description.size() + _indentation_level, '\t');
688 }
689
690 _indentation_level++;
24ed18f2
JG
691 _description += fmt::format("variant <{}> {{\n",
692 _bypass_identifier_escape ?
eda1aa02
JG
693 *(type.selector_field_location.elements_.end() - 1) :
694 escape_tsdl_identifier(*(type.selector_field_location.elements_.end() - 1)));
d7bfb9b0 695
24ed18f2
JG
696 /*
697 * The CTF 1.8 specification only recommends that implementations ignore
698 * leading underscores in field names. Both babeltrace 1 and 2 expect the
699 * variant choice and enumeration mapping name to match perfectly. Given that we
700 * don't have access to the tag in this context, we have to assume they match.
701 */
702 const auto previous_bypass_identifier_escape = _bypass_identifier_escape;
703 _bypass_identifier_escape = true;
def9854d 704 for (const auto& field : type.choices_) {
d7bfb9b0 705 _description.resize(_description.size() + _indentation_level, '\t');
45110cdd
JG
706 field.second->accept(*this);
707 _description += fmt::format(" {};\n", field.first.name);
d7bfb9b0
JG
708 }
709
24ed18f2
JG
710 _bypass_identifier_escape = previous_bypass_identifier_escape;
711
d7bfb9b0 712 _indentation_level--;
24ed18f2
JG
713 _description.resize(_description.size() + _indentation_level, '\t');
714 _description += "}";
d7bfb9b0
JG
715 }
716
45110cdd
JG
717 virtual void visit(const lst::variant_type<lst::signed_enumeration_type::mapping::range_t::range_integer_t>& type) override final
718 {
719 visit_variant(type);
720 }
721
722 virtual void visit(const lst::variant_type<lst::unsigned_enumeration_type::mapping::range_t::range_integer_t>& type) override final
723 {
724 visit_variant(type);
725 }
726
d7bfb9b0
JG
727 lst::type::cuptr create_character_type(enum lst::string_type::encoding encoding)
728 {
729 _current_integer_encoding_override = encoding;
730 return lttng::make_unique<lst::integer_type>(8, _trace_abi.byte_order, 8,
731 lst::integer_type::signedness::UNSIGNED,
732 lst::integer_type::base::DECIMAL);
733 }
734
735 virtual void visit(const lst::static_length_string_type& type) override final
736 {
737 /*
738 * TSDL expresses static-length strings as arrays of 8-bit integer with
739 * an encoding specified.
740 */
741 const auto char_array = lttng::make_unique<lst::static_length_array_type>(
65cd3c0c 742 type.alignment, create_character_type(type.encoding_), type.length);
d7bfb9b0
JG
743
744 visit(*char_array);
745 }
746
747 virtual void visit(const lst::dynamic_length_string_type& type) override final
748 {
749 /*
750 * TSDL expresses dynamic-length strings as arrays of 8-bit integer with
751 * an encoding specified.
752 */
753 const auto char_sequence = lttng::make_unique<lst::dynamic_length_array_type>(
65cd3c0c 754 type.alignment, create_character_type(type.encoding_),
eda1aa02 755 type.length_field_location);
d7bfb9b0
JG
756
757 visit(*char_sequence);
758 }
759
24ed18f2 760 std::stack<std::string> _current_field_name;
d7bfb9b0
JG
761 /*
762 * Encoding to specify for the next serialized integer type.
763 * Since the integer_type does not allow an encoding to be specified (it is a TSDL-specific
764 * concept), this attribute is used when expressing static or dynamic length strings as
765 * arrays/sequences of bytes with an encoding.
766 */
767 nonstd::optional<enum lst::string_type::encoding> _current_integer_encoding_override;
768
769 unsigned int _indentation_level;
770 const lst::abi& _trace_abi;
771
772 std::queue<std::string> _type_suffixes;
773
774 /* Description in TSDL format. */
775 std::string _description;
24ed18f2
JG
776
777 bool _bypass_identifier_escape;
778 const char *_default_clock_class_name;
b6bbb1d6 779 const tsdl::details::type_overrider& _type_overrides;
d7bfb9b0 780};
da9dd521
JG
781
782class tsdl_trace_environment_visitor : public lst::trace_class_environment_visitor {
783public:
784 tsdl_trace_environment_visitor() : _environment{"env {\n"}
785 {
786 }
787
788 virtual void visit(const lst::environment_field<int64_t>& field) override
789 {
790 _environment += fmt::format(" {} = {};\n", field.name, field.value);
791 }
792
793 virtual void visit(const lst::environment_field<const char *>& field) override
794 {
795 _environment += fmt::format(" {} = \"{}\";\n", field.name,
796 escape_tsdl_env_string_value(field.value));
797 }
798
799 /* Only call once. */
c22ded12 800 std::string move_description()
da9dd521
JG
801 {
802 _environment += "};\n\n";
803 return std::move(_environment);
804 }
805
806private:
807 std::string _environment;
808};
d7bfb9b0
JG
809} /* namespace */
810
811tsdl::trace_class_visitor::trace_class_visitor(const lst::abi& trace_abi,
812 tsdl::append_metadata_fragment_function append_metadata_fragment) :
24ed18f2
JG
813 _trace_abi{trace_abi},
814 _append_metadata_fragment(append_metadata_fragment)
d7bfb9b0
JG
815{
816}
817
818void tsdl::trace_class_visitor::append_metadata_fragment(const std::string& fragment) const
819{
820 _append_metadata_fragment(fragment);
821}
822
823void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::trace_class& trace_class)
824{
b6bbb1d6
JG
825 /* Ensure this instance is not used against multiple trace classes. */
826 LTTNG_ASSERT(!_current_trace_class || _current_trace_class == &trace_class);
827 _current_trace_class = &trace_class;
828
829 tsdl_field_visitor packet_header_visitor{trace_class.abi, 1, _sanitized_types_overrides};
24ed18f2 830
4bcf2294 831 trace_class.packet_header()->accept(packet_header_visitor);
24ed18f2 832
d7bfb9b0
JG
833 /* Declare type aliases, trace class, and packet header. */
834 auto trace_class_tsdl = fmt::format(
835 "/* CTF {ctf_major}.{ctf_minor} */\n\n"
d7bfb9b0
JG
836 "trace {{\n"
837 " major = {ctf_major};\n"
838 " minor = {ctf_minor};\n"
839 " uuid = \"{uuid}\";\n"
840 " byte_order = {byte_order};\n"
24ed18f2 841 " packet.header := {packet_header_layout};\n"
d7bfb9b0
JG
842 "}};\n\n",
843 fmt::arg("ctf_major", ctf_spec_major),
844 fmt::arg("ctf_minor", ctf_spec_minor),
845 fmt::arg("uint8_t_alignment", trace_class.abi.uint8_t_alignment),
846 fmt::arg("uint16_t_alignment", trace_class.abi.uint16_t_alignment),
847 fmt::arg("uint32_t_alignment", trace_class.abi.uint32_t_alignment),
848 fmt::arg("uint64_t_alignment", trace_class.abi.uint64_t_alignment),
849 fmt::arg("long_alignment", trace_class.abi.long_alignment),
850 fmt::arg("long_size", trace_class.abi.long_alignment),
851 fmt::arg("bits_per_long", trace_class.abi.bits_per_long),
852 fmt::arg("uuid", lttng::utils::uuid_to_str(trace_class.uuid)),
853 fmt::arg("byte_order",
24ed18f2 854 trace_class.abi.byte_order == lst::byte_order::BIG_ENDIAN_ ? "be" : "le"),
c22ded12 855 fmt::arg("packet_header_layout", packet_header_visitor.move_description()));
d7bfb9b0
JG
856
857 /* Declare trace scope and type aliases. */
a57c248a 858 append_metadata_fragment(trace_class_tsdl);
da9dd521
JG
859
860 tsdl_trace_environment_visitor environment_visitor;
861 trace_class.accept(environment_visitor);
c22ded12 862 append_metadata_fragment(environment_visitor.move_description());
d7bfb9b0
JG
863}
864
865void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::clock_class& clock_class)
866{
867 auto uuid_str = clock_class.uuid ?
868 fmt::format(" uuid = \"{}\";\n",
869 lttng::utils::uuid_to_str(*clock_class.uuid)) :
870 "";
871
872 /* Assumes a single clock that maps to specific stream class fields/roles. */
873 auto clock_class_str = fmt::format(
874 "clock {{\n"
875 " name = \"{name}\";\n"
876 /* Optional uuid. */
877 "{uuid}"
878 " description = \"{description}\";\n"
879 " freq = {frequency};\n"
880 " offset = {offset};\n"
881 "}};\n"
24ed18f2 882 "\n",
d7bfb9b0
JG
883 fmt::arg("name", clock_class.name),
884 fmt::arg("uuid", uuid_str),
885 fmt::arg("description", clock_class.description),
886 fmt::arg("frequency", clock_class.frequency),
24ed18f2 887 fmt::arg("offset", clock_class.offset));
d7bfb9b0 888
a57c248a 889 append_metadata_fragment(clock_class_str);
d7bfb9b0
JG
890}
891
892void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::stream_class& stream_class)
893{
b6bbb1d6
JG
894 _current_stream_class = &stream_class;
895 const auto clear_stream_class_on_exit = lttng::make_scope_exit(
896 [this]() noexcept { _current_stream_class = nullptr; });
897
d7bfb9b0 898 auto stream_class_str = fmt::format("stream {{\n"
24ed18f2 899 " id = {};\n", stream_class.id);
b6bbb1d6
JG
900 variant_tsdl_keyword_sanitizer variant_sanitizer(_sanitized_types_overrides,
901 [this](const lttng::sessiond::trace::field_location& location)
902 -> const lst::type& {
903 return _lookup_field_type(location);
904 });
d7bfb9b0 905
4bcf2294 906 const auto *event_header = stream_class.event_header();
24ed18f2 907 if (event_header) {
b6bbb1d6
JG
908 tsdl_field_visitor event_header_visitor{_trace_abi, 1, _sanitized_types_overrides,
909 stream_class.default_clock_class_name};
d7bfb9b0 910
b6bbb1d6 911 event_header->accept(variant_sanitizer);
24ed18f2
JG
912 event_header->accept(event_header_visitor);
913 stream_class_str += fmt::format(" event.header := {};\n",
c22ded12 914 event_header_visitor.move_description());
24ed18f2
JG
915 }
916
4bcf2294 917 const auto *packet_context = stream_class.packet_context();
24ed18f2 918 if (packet_context) {
b6bbb1d6
JG
919 tsdl_field_visitor packet_context_visitor{_trace_abi, 1, _sanitized_types_overrides,
920 stream_class.default_clock_class_name};
24ed18f2 921
b6bbb1d6 922 packet_context->accept(variant_sanitizer);
24ed18f2
JG
923 packet_context->accept(packet_context_visitor);
924 stream_class_str += fmt::format(" packet.context := {};\n",
c22ded12 925 packet_context_visitor.move_description());
24ed18f2
JG
926 }
927
4bcf2294 928 const auto *event_context = stream_class.event_context();
24ed18f2 929 if (event_context) {
b6bbb1d6 930 tsdl_field_visitor event_context_visitor{_trace_abi, 1, _sanitized_types_overrides};
24ed18f2 931
b6bbb1d6 932 event_context->accept(variant_sanitizer);
24ed18f2
JG
933 event_context->accept(event_context_visitor);
934 stream_class_str += fmt::format(" event.context := {};\n",
c22ded12 935 event_context_visitor.move_description());
24ed18f2 936 }
d7bfb9b0 937
24ed18f2 938 stream_class_str += "};\n\n";
d7bfb9b0
JG
939
940 append_metadata_fragment(stream_class_str);
941}
942
943void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::event_class& event_class)
944{
b6bbb1d6
JG
945 _current_event_class = &event_class;
946 const auto clear_event_class_on_exit = lttng::make_scope_exit(
947 [this]() noexcept { _current_event_class = nullptr; });
948
d7bfb9b0
JG
949 auto event_class_str = fmt::format("event {{\n"
950 " name = \"{name}\";\n"
951 " id = {id};\n"
952 " stream_id = {stream_class_id};\n"
953 " loglevel = {log_level};\n",
954 fmt::arg("name", event_class.name),
955 fmt::arg("id", event_class.id),
956 fmt::arg("stream_class_id", event_class.stream_class_id),
957 fmt::arg("log_level", event_class.log_level));
958
959 if (event_class.model_emf_uri) {
960 event_class_str += fmt::format(
961 " model.emf.uri = \"{}\";\n", *event_class.model_emf_uri);
962 }
963
b6bbb1d6
JG
964 tsdl_field_visitor payload_visitor{_trace_abi, 1, _sanitized_types_overrides};
965 variant_tsdl_keyword_sanitizer variant_sanitizer(_sanitized_types_overrides,
966 [this](const lttng::sessiond::trace::field_location& location)
967 -> const lst::type& {
968 return _lookup_field_type(location);
969 });
d7bfb9b0 970
b6bbb1d6
JG
971 event_class.payload->accept(variant_sanitizer);
972 event_class.payload->accept(payload_visitor);
d7bfb9b0
JG
973
974 event_class_str += fmt::format(
c22ded12 975 " fields := {};\n}};\n\n", payload_visitor.move_description());
d7bfb9b0
JG
976
977 append_metadata_fragment(event_class_str);
978}
b6bbb1d6
JG
979
980void tsdl::details::type_overrider::publish(
981 const lttng::sessiond::trace::type& original_type,
982 lttng::sessiond::trace::type::cuptr new_type_override)
983{
984 auto current_override = _overriden_types.find(&original_type);
985
986 if (current_override != _overriden_types.end()) {
987 current_override->second = std::move(new_type_override);
988 } else {
989 _overriden_types.insert(std::make_pair(&original_type, std::move(new_type_override)));
990 }
991}
992
993const lst::type& tsdl::details::type_overrider::type(
994 const lttng::sessiond::trace::type& original) const noexcept
995{
996 const auto result = _overriden_types.find(&original);
997
998 if (result != _overriden_types.end()) {
999 /* Provide the overriden type. */
1000 return *result->second;
1001 }
1002
1003 /* Pass the original type through. */
1004 return original;
1005}
1006
1007namespace {
1008const lttng::sessiond::trace::type& lookup_type_from_root_type(
1009 const lttng::sessiond::trace::type& root_type,
1010 const lttng::sessiond::trace::field_location& field_location)
1011{
1012 const auto *type = &root_type;
1013
1014 for (const auto& location_element : field_location.elements_) {
1015 /* Only structures can be traversed. */
1016 const auto *struct_type = dynamic_cast<const lst::structure_type *>(type);
1017
1018 /*
1019 * Traverse the type by following the field location path.
1020 *
1021 * While field paths are assumed to have been validated before-hand,
1022 * a dynamic cast is performed here as an additional precaution
1023 * since none of this is performance-critical; it can be removed
1024 * safely.
1025 */
1026 if (!struct_type) {
1027 LTTNG_THROW_ERROR(fmt::format(
1028 "Encountered a type that is not a structure while traversing field location: field-location=`{}`",
1029 field_location));
1030 }
1031
1032 const auto field_found_it = std::find_if(struct_type->fields_.cbegin(),
1033 struct_type->fields_.cend(),
1034 [&location_element](const lst::field::cuptr& struct_field) {
1035 return struct_field->name == location_element;
1036 });
1037
1038 if (field_found_it == struct_type->fields_.cend()) {
1039 LTTNG_THROW_ERROR(fmt::format(
1040 "Failed to find field using field location: field-name:=`{field_name}`, field-location=`{field_location}`",
1041 fmt::arg("field_location", field_location),
1042 fmt::arg("field_name", location_element)));
1043 }
1044
1045 type = &(*field_found_it)->get_type();
1046 }
1047
1048 return *type;
1049}
1050} /* anonymous namespace. */
1051
1052/*
1053 * The trace hierarchy is assumed to have been validated on creation.
1054 * This function can only fail due to a validation error, hence
1055 * why it throws on any unexpected/invalid field location.
1056 *
1057 * Does not return an overriden field type; it returns the original field type
1058 * as found in the trace hierarchy.
1059 */
1060const lttng::sessiond::trace::type& lttng::sessiond::tsdl::trace_class_visitor::_lookup_field_type(
1061 const lttng::sessiond::trace::field_location& location) const
1062{
1063 /* Validate the look-up is happening in a valid visit context. */
1064 switch (location.root_) {
1065 case lst::field_location::root::EVENT_RECORD_HEADER:
1066 case lst::field_location::root::EVENT_RECORD_PAYLOAD:
1067 if (!_current_event_class) {
1068 LTTNG_THROW_ERROR(
1069 "Field type look-up failure: no current event class in visitor's context");
1070 }
1071 /* fall through. */
1072 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT:
1073 case lst::field_location::root::PACKET_CONTEXT:
1074 if (!_current_stream_class) {
1075 LTTNG_THROW_ERROR(
1076 "Field type look-up failure: no current stream class in visitor's context");
1077 }
1078 /* fall through. */
1079 case lst::field_location::root::PACKET_HEADER:
1080 if (!_current_trace_class) {
1081 LTTNG_THROW_ERROR(
1082 "Field type look-up failure: no current trace class in visitor's context");
1083 }
1084
1085 break;
1086 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT:
1087 LTTNG_THROW_UNSUPPORTED_ERROR(
1088 "Field type look-up failure: event-record specific contexts are not supported");
1089 default:
1090 LTTNG_THROW_UNSUPPORTED_ERROR(
1091 "Field type look-up failure: unknown field location root");
1092 }
1093
1094 switch (location.root_) {
1095 case lst::field_location::root::PACKET_HEADER:
1096 return lookup_type_from_root_type(
4bcf2294 1097 *_current_trace_class->packet_header(), location);
b6bbb1d6
JG
1098 case lst::field_location::root::PACKET_CONTEXT:
1099 return lookup_type_from_root_type(
4bcf2294 1100 *_current_stream_class->packet_context(), location);
b6bbb1d6
JG
1101 case lst::field_location::root::EVENT_RECORD_HEADER:
1102 return lookup_type_from_root_type(
4bcf2294 1103 *_current_stream_class->event_header(), location);
b6bbb1d6
JG
1104 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT:
1105 return lookup_type_from_root_type(
4bcf2294 1106 *_current_stream_class->event_context(), location);
b6bbb1d6
JG
1107 case lst::field_location::root::EVENT_RECORD_PAYLOAD:
1108 return lookup_type_from_root_type(
1109 *_current_event_class->payload, location);
1110 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT:
1111 default:
1112 /* Unreachable as it was checked before. */
1113 abort();
1114 }
1115}
This page took 0.07414 seconds and 4 git commands to generate.