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