clang-tidy: add most bugprone warnings
[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>
cd9adb8b 26#include <utility>
d7bfb9b0
JG
27
28namespace lst = lttng::sessiond::trace;
29namespace tsdl = lttng::sessiond::tsdl;
30
31namespace {
32const auto ctf_spec_major = 1;
33const auto ctf_spec_minor = 8;
34
2f35b2f5
JG
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 */
28ab034a
JG
39const 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" };
2f35b2f5 53
d7bfb9b0
JG
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 */
62std::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
2f35b2f5
JG
68 if (safe_tsdl_identifiers.find(original_identifier) != safe_tsdl_identifiers.end()) {
69 return original_identifier;
70 }
71
d7bfb9b0
JG
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 '_'. */
28ab034a 78 std::locale c_locale{ "C" };
d7bfb9b0
JG
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
90std::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
b6bbb1d6
JG
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 */
135class variant_tsdl_keyword_sanitizer : public lttng::sessiond::trace::field_visitor,
136 public lttng::sessiond::trace::type_visitor {
137public:
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,
28ab034a 141 type_lookup_function lookup_type) :
cd9adb8b 142 _type_overrides{ type_overrides }, _lookup_type(std::move(lookup_type))
b6bbb1d6
JG
143 {
144 }
145
146private:
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
cd9adb8b 156 void visit(const lst::field& field) final
b6bbb1d6
JG
157 {
158 _type_overrides.type(field.get_type()).accept(*this);
159 }
160
cd9adb8b 161 void visit(const lst::integer_type& type __attribute__((unused))) final
b6bbb1d6
JG
162 {
163 }
164
cd9adb8b 165 void visit(const lst::floating_point_type& type __attribute__((unused))) final
b6bbb1d6
JG
166 {
167 }
168
cd9adb8b 169 void visit(const lst::signed_enumeration_type& type __attribute__((unused))) final
b6bbb1d6
JG
170 {
171 }
172
cd9adb8b 173 void visit(const lst::unsigned_enumeration_type& type __attribute__((unused))) final
b6bbb1d6
JG
174 {
175 }
176
cd9adb8b 177 void visit(const lst::static_length_array_type& type __attribute__((unused))) final
b6bbb1d6
JG
178 {
179 }
180
cd9adb8b 181 void visit(const lst::dynamic_length_array_type& type __attribute__((unused))) final
b6bbb1d6
JG
182 {
183 }
184
cd9adb8b 185 void visit(const lst::static_length_blob_type& type __attribute__((unused))) final
b6bbb1d6
JG
186 {
187 }
188
cd9adb8b 189 void visit(const lst::dynamic_length_blob_type& type __attribute__((unused))) final
b6bbb1d6
JG
190 {
191 }
192
cd9adb8b 193 void visit(const lst::null_terminated_string_type& type __attribute__((unused))) final
b6bbb1d6
JG
194 {
195 }
196
cd9adb8b 197 void visit(const lst::structure_type& type) final
b6bbb1d6
JG
198 {
199 /* Recurse into structure attributes. */
200 for (const auto& field : type.fields_) {
201 field->accept(*this);
202 }
203 }
204
205 /*
28ab034a
JG
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.
b6bbb1d6
JG
209 */
210 template <typename MappingIntegerType>
211 lst::type::cuptr _create_sanitized_selector(
28ab034a
JG
212 const lst::typed_enumeration_type<MappingIntegerType>& original_selector,
213 const unsafe_names& unsafe_names_found)
b6bbb1d6 214 {
28ab034a
JG
215 auto new_mappings = std::make_shared<
216 typename lst::typed_enumeration_type<MappingIntegerType>::mappings>();
b6bbb1d6
JG
217
218 for (const auto& mapping : *original_selector.mappings_) {
219 if (unsafe_names_found.find(mapping.name.c_str()) ==
28ab034a 220 unsafe_names_found.end()) {
b6bbb1d6
JG
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. */
28ab034a
JG
225 new_mappings->emplace_back(fmt::format("_{}", mapping.name),
226 mapping.range);
b6bbb1d6
JG
227 }
228 }
229
230 return lttng::make_unique<lst::typed_enumeration_type<MappingIntegerType>>(
28ab034a
JG
231 original_selector.alignment,
232 original_selector.byte_order,
233 original_selector.size,
234 original_selector.base_,
235 new_mappings);
b6bbb1d6
JG
236 }
237
238 template <typename MappingIntegerType>
239 const typename lst::typed_enumeration_type<MappingIntegerType>::mapping&
240 _find_enumeration_mapping_by_range(
28ab034a
JG
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)
b6bbb1d6
JG
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(
28ab034a
JG
252 "Failed to find mapping by range in enumeration while sanitizing a variant: target_mapping_range={}",
253 target_mapping_range));
b6bbb1d6
JG
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>
28ab034a
JG
261 lst::type::cuptr
262 _create_sanitized_variant(const lst::variant_type<MappingIntegerType>& original_variant)
b6bbb1d6
JG
263 {
264 typename lst::variant_type<MappingIntegerType>::choices new_choices;
28ab034a
JG
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)));
b6bbb1d6
JG
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
28ab034a
JG
278 new_choices.emplace_back(_find_enumeration_mapping_by_range(
279 sanitized_selector, choice.first.range),
280 sanitized_choice_type.copy());
b6bbb1d6
JG
281 }
282
283 return lttng::make_unique<lst::variant_type<MappingIntegerType>>(
28ab034a
JG
284 original_variant.alignment,
285 original_variant.selector_field_location,
286 std::move(new_choices));
b6bbb1d6
JG
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 = {
28ab034a
JG
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",
b6bbb1d6
JG
299 };
300
301 for (const auto& choice : type.choices_) {
28ab034a
JG
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. */
b6bbb1d6
JG
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(
28ab034a
JG
329 static_cast<const lst::typed_enumeration_type<MappingIntegerType>&>(
330 overriden_selector_type),
331 unsafe_names_found);
b6bbb1d6
JG
332 _type_overrides.publish(original_selector_type, std::move(sanitized_selector_type));
333
334 auto sanitized_variant_type = _create_sanitized_variant(
28ab034a 335 static_cast<const lst::variant_type<MappingIntegerType>&>(type));
b6bbb1d6
JG
336 _type_overrides.publish(type, std::move(sanitized_variant_type));
337 }
338
cd9adb8b
JG
339 void visit(const lst::variant_type<
340 lst::signed_enumeration_type::mapping::range_t::range_integer_t>& type) final
b6bbb1d6
JG
341 {
342 visit_variant(type);
343 }
344
cd9adb8b
JG
345 void visit(const lst::variant_type<
346 lst::unsigned_enumeration_type::mapping::range_t::range_integer_t>& type) final
b6bbb1d6
JG
347 {
348 visit_variant(type);
349 }
350
cd9adb8b 351 void visit(const lst::static_length_string_type& type __attribute__((unused))) final
b6bbb1d6
JG
352 {
353 }
354
cd9adb8b 355 void visit(const lst::dynamic_length_string_type& type __attribute__((unused))) final
b6bbb1d6
JG
356 {
357 }
358
359 tsdl::details::type_overrider& _type_overrides;
360 const type_lookup_function _lookup_type;
361};
362
d7bfb9b0
JG
363class tsdl_field_visitor : public lttng::sessiond::trace::field_visitor,
364 public lttng::sessiond::trace::type_visitor {
365public:
24ed18f2 366 tsdl_field_visitor(const lst::abi& abi,
28ab034a
JG
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 },
cd9adb8b 373
28ab034a
JG
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 }
d7bfb9b0
JG
378 {
379 }
380
da9dd521 381 /* Only call once. */
c22ded12 382 std::string move_description()
d7bfb9b0 383 {
da9dd521 384 return std::move(_description);
d7bfb9b0
JG
385 }
386
387private:
cd9adb8b 388 void visit(const lst::field& field) final
d7bfb9b0
JG
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 */
24ed18f2 397 _current_field_name.push(_bypass_identifier_escape ?
28ab034a
JG
398 field.name :
399 escape_tsdl_identifier(field.name));
b6bbb1d6 400 _type_overrides.type(field.get_type()).accept(*this);
d7bfb9b0 401 _description += " ";
24ed18f2
JG
402 _description += _current_field_name.top();
403 _current_field_name.pop();
d7bfb9b0
JG
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 += ";";
d7bfb9b0
JG
415 }
416
cd9adb8b 417 void visit(const lst::integer_type& type) final
d7bfb9b0
JG
418 {
419 _description += "integer { ";
420
421 /* Mandatory properties (no defaults). */
422 _description += fmt::format("size = {size}; align = {alignment};",
28ab034a
JG
423 fmt::arg("size", type.size),
424 fmt::arg("alignment", type.alignment));
d7bfb9b0
JG
425
426 /* Defaults to unsigned. */
65cd3c0c 427 if (type.signedness_ == lst::integer_type::signedness::SIGNED) {
d7bfb9b0
JG
428 _description += " signed = true;";
429 }
430
431 /* Defaults to 10. */
65cd3c0c 432 if (type.base_ != lst::integer_type::base::DECIMAL) {
d7bfb9b0
JG
433 unsigned int base;
434
65cd3c0c 435 switch (type.base_) {
d7bfb9b0
JG
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(
28ab034a
JG
447 "Unexpected base encountered while serializing integer type to TSDL: base = {}",
448 (int) type.base_));
d7bfb9b0
JG
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) {
28ab034a
JG
456 const auto byte_order_str =
457 type.byte_order == lst::byte_order::BIG_ENDIAN_ ? "be" : "le";
d7bfb9b0
JG
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(
28ab034a
JG
474 "Unexpected encoding encountered while serializing integer type to TSDL: encoding = {}",
475 (int) *_current_integer_encoding_override));
d7bfb9b0
JG
476 }
477
478 _description += fmt::format(" encoding = {};", encoding_str);
479 _current_integer_encoding_override.reset();
480 }
481
28ab034a
JG
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()) {
24ed18f2 490 LTTNG_ASSERT(_default_clock_class_name);
28ab034a
JG
491 _description +=
492 fmt::format(" map = clock.{}.value;", _default_clock_class_name);
24ed18f2
JG
493 }
494
d7bfb9b0
JG
495 _description += " }";
496 }
497
cd9adb8b 498 void visit(const lst::floating_point_type& type) final
d7bfb9b0
JG
499 {
500 _description += fmt::format(
28ab034a
JG
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));
d7bfb9b0
JG
505
506 /* Defaults to the trace's native byte order. */
507 if (type.byte_order != _trace_abi.byte_order) {
28ab034a
JG
508 const auto byte_order_str =
509 type.byte_order == lst::byte_order::BIG_ENDIAN_ ? "be" : "le";
d7bfb9b0
JG
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
b6bbb1d6
JG
523 visit(static_cast<const lst::integer_type&>(type));
524 _description += " {\n";
d7bfb9b0
JG
525
526 const auto mappings_indentation_level = _indentation_level + 1;
527
528 bool first_mapping = true;
da9dd521 529 for (const auto& mapping : *type.mappings_) {
d7bfb9b0
JG
530 if (!first_mapping) {
531 _description += ",\n";
532 }
533
534 _description.resize(_description.size() + mappings_indentation_level, '\t');
da9dd521 535 if (mapping.range.begin == mapping.range.end) {
28ab034a
JG
536 _description +=
537 fmt::format("\"{mapping_name}\" = {mapping_value}",
538 fmt::arg("mapping_name", mapping.name),
539 fmt::arg("mapping_value", mapping.range.begin));
d7bfb9b0
JG
540 } else {
541 _description += fmt::format(
28ab034a
JG
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));
d7bfb9b0
JG
546 }
547
548 first_mapping = false;
549 }
550
551 _description += "\n";
552 _description.resize(_description.size() + _indentation_level, '\t');
553 _description += "}";
554 }
555
cd9adb8b 556 void visit(const lst::signed_enumeration_type& type) final
d7bfb9b0
JG
557 {
558 visit_enumeration(type);
559 }
560
cd9adb8b 561 void visit(const lst::unsigned_enumeration_type& type) final
d7bfb9b0
JG
562 {
563 visit_enumeration(type);
564 }
565
cd9adb8b 566 void visit(const lst::static_length_array_type& type) final
d7bfb9b0
JG
567 {
568 if (type.alignment != 0) {
24ed18f2 569 LTTNG_ASSERT(_current_field_name.size() > 0);
d7bfb9b0 570 _description += fmt::format(
28ab034a
JG
571 "struct {{ }} align({alignment}) {field_name}_padding;\n",
572 fmt::arg("alignment", type.alignment),
573 fmt::arg("field_name", _current_field_name.top()));
d7bfb9b0
JG
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
cd9adb8b 581 void visit(const lst::dynamic_length_array_type& type) final
d7bfb9b0
JG
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 */
24ed18f2 590 LTTNG_ASSERT(_current_field_name.size() > 0);
d7bfb9b0 591 _description += fmt::format(
28ab034a
JG
592 "struct {{ }} align({alignment}) {field_name}_padding;\n",
593 fmt::arg("alignment", type.alignment),
594 fmt::arg("field_name", _current_field_name.top()));
d7bfb9b0
JG
595 _description.resize(_description.size() + _indentation_level, '\t');
596 }
597
598 type.element_type->accept(*this);
28ab034a
JG
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))));
d7bfb9b0
JG
605 }
606
cd9adb8b 607 void visit(const lst::static_length_blob_type& type) final
e7360180 608 {
28ab034a
JG
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,
e7360180
JG
617 lst::integer_type::base::HEXADECIMAL);
618 const auto array = lttng::make_unique<lst::static_length_array_type>(
28ab034a 619 type.alignment, std::move(uint8_element), type.length_bytes);
e7360180
JG
620
621 visit(*array);
622 }
623
cd9adb8b 624 void visit(const lst::dynamic_length_blob_type& type) final
e7360180 625 {
28ab034a
JG
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,
e7360180
JG
634 lst::integer_type::base::HEXADECIMAL);
635 const auto array = lttng::make_unique<lst::dynamic_length_array_type>(
28ab034a 636 type.alignment, std::move(uint8_element), type.length_field_location);
e7360180
JG
637
638 visit(*array);
639 }
640
cd9adb8b 641 void visit(const lst::null_terminated_string_type& type) final
d7bfb9b0 642 {
24ed18f2 643 /* Defaults to UTF-8. */
65cd3c0c 644 if (type.encoding_ == lst::null_terminated_string_type::encoding::ASCII) {
d7bfb9b0
JG
645 _description += "string { encoding = ASCII }";
646 } else {
647 _description += "string";
648 }
649 }
650
cd9adb8b 651 void visit(const lst::structure_type& type) final
d7bfb9b0
JG
652 {
653 _indentation_level++;
654 _description += "struct {";
655
24ed18f2
JG
656 const auto previous_bypass_identifier_escape = _bypass_identifier_escape;
657 _bypass_identifier_escape = false;
da9dd521 658 for (const auto& field : type.fields_) {
d7bfb9b0
JG
659 _description += "\n";
660 _description.resize(_description.size() + _indentation_level, '\t');
661 field->accept(*this);
662 }
663
24ed18f2
JG
664 _bypass_identifier_escape = previous_bypass_identifier_escape;
665
d7bfb9b0 666 _indentation_level--;
da9dd521 667 if (type.fields_.size() != 0) {
d7bfb9b0
JG
668 _description += "\n";
669 _description.resize(_description.size() + _indentation_level, '\t');
670 }
671
24ed18f2 672 _description += "}";
d7bfb9b0
JG
673 }
674
45110cdd
JG
675 template <class MappingIntegerType>
676 void visit_variant(const lst::variant_type<MappingIntegerType>& type)
d7bfb9b0
JG
677 {
678 if (type.alignment != 0) {
24ed18f2 679 LTTNG_ASSERT(_current_field_name.size() > 0);
d7bfb9b0 680 _description += fmt::format(
28ab034a
JG
681 "struct {{ }} align({alignment}) {field_name}_padding;\n",
682 fmt::arg("alignment", type.alignment),
683 fmt::arg("field_name", _current_field_name.top()));
d7bfb9b0
JG
684 _description.resize(_description.size() + _indentation_level, '\t');
685 }
686
687 _indentation_level++;
28ab034a
JG
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)));
d7bfb9b0 694
24ed18f2
JG
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;
def9854d 703 for (const auto& field : type.choices_) {
d7bfb9b0 704 _description.resize(_description.size() + _indentation_level, '\t');
45110cdd
JG
705 field.second->accept(*this);
706 _description += fmt::format(" {};\n", field.first.name);
d7bfb9b0
JG
707 }
708
24ed18f2
JG
709 _bypass_identifier_escape = previous_bypass_identifier_escape;
710
d7bfb9b0 711 _indentation_level--;
24ed18f2
JG
712 _description.resize(_description.size() + _indentation_level, '\t');
713 _description += "}";
d7bfb9b0
JG
714 }
715
cd9adb8b
JG
716 void visit(const lst::variant_type<
717 lst::signed_enumeration_type::mapping::range_t::range_integer_t>& type) final
45110cdd
JG
718 {
719 visit_variant(type);
720 }
721
cd9adb8b
JG
722 void visit(const lst::variant_type<
723 lst::unsigned_enumeration_type::mapping::range_t::range_integer_t>& type) final
45110cdd
JG
724 {
725 visit_variant(type);
726 }
727
d7bfb9b0
JG
728 lst::type::cuptr create_character_type(enum lst::string_type::encoding encoding)
729 {
730 _current_integer_encoding_override = encoding;
28ab034a
JG
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);
d7bfb9b0
JG
737 }
738
cd9adb8b 739 void visit(const lst::static_length_string_type& type) final
d7bfb9b0
JG
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>(
28ab034a 746 type.alignment, create_character_type(type.encoding_), type.length);
d7bfb9b0
JG
747
748 visit(*char_array);
749 }
750
cd9adb8b 751 void visit(const lst::dynamic_length_string_type& type) final
d7bfb9b0
JG
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>(
28ab034a
JG
758 type.alignment,
759 create_character_type(type.encoding_),
760 type.length_field_location);
d7bfb9b0
JG
761
762 visit(*char_sequence);
763 }
764
24ed18f2 765 std::stack<std::string> _current_field_name;
d7bfb9b0
JG
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;
24ed18f2 781
cd9adb8b 782 bool _bypass_identifier_escape{ false };
24ed18f2 783 const char *_default_clock_class_name;
b6bbb1d6 784 const tsdl::details::type_overrider& _type_overrides;
d7bfb9b0 785};
da9dd521
JG
786
787class tsdl_trace_environment_visitor : public lst::trace_class_environment_visitor {
788public:
5c7248cd 789 tsdl_trace_environment_visitor() = default;
da9dd521 790
cd9adb8b 791 void visit(const lst::environment_field<int64_t>& field) override
da9dd521
JG
792 {
793 _environment += fmt::format(" {} = {};\n", field.name, field.value);
794 }
795
cd9adb8b 796 void visit(const lst::environment_field<const char *>& field) override
da9dd521 797 {
cd9adb8b
JG
798 _environment += fmt::format(" {} = \"{}\";\n",
799 field.name,
800 escape_tsdl_env_string_value(field.value));
da9dd521
JG
801 }
802
803 /* Only call once. */
c22ded12 804 std::string move_description()
da9dd521
JG
805 {
806 _environment += "};\n\n";
807 return std::move(_environment);
808 }
809
810private:
5c7248cd 811 std::string _environment{ "env {\n" };
da9dd521 812};
d7bfb9b0
JG
813} /* namespace */
814
28ab034a
JG
815tsdl::trace_class_visitor::trace_class_visitor(
816 const lst::abi& trace_abi,
817 tsdl::append_metadata_fragment_function append_metadata_fragment) :
cd9adb8b 818 _trace_abi{ trace_abi }, _append_metadata_fragment(std::move(append_metadata_fragment))
d7bfb9b0
JG
819{
820}
821
822void tsdl::trace_class_visitor::append_metadata_fragment(const std::string& fragment) const
823{
824 _append_metadata_fragment(fragment);
825}
826
827void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::trace_class& trace_class)
828{
b6bbb1d6
JG
829 /* Ensure this instance is not used against multiple trace classes. */
830 LTTNG_ASSERT(!_current_trace_class || _current_trace_class == &trace_class);
831 _current_trace_class = &trace_class;
832
28ab034a 833 tsdl_field_visitor packet_header_visitor{ trace_class.abi, 1, _sanitized_types_overrides };
24ed18f2 834
4bcf2294 835 trace_class.packet_header()->accept(packet_header_visitor);
24ed18f2 836
d7bfb9b0
JG
837 /* Declare type aliases, trace class, and packet header. */
838 auto trace_class_tsdl = fmt::format(
28ab034a
JG
839 "/* CTF {ctf_major}.{ctf_minor} */\n\n"
840 "trace {{\n"
841 " major = {ctf_major};\n"
842 " minor = {ctf_minor};\n"
843 " uuid = \"{uuid}\";\n"
844 " byte_order = {byte_order};\n"
845 " packet.header := {packet_header_layout};\n"
846 "}};\n\n",
847 fmt::arg("ctf_major", ctf_spec_major),
848 fmt::arg("ctf_minor", ctf_spec_minor),
849 fmt::arg("uint8_t_alignment", trace_class.abi.uint8_t_alignment),
850 fmt::arg("uint16_t_alignment", trace_class.abi.uint16_t_alignment),
851 fmt::arg("uint32_t_alignment", trace_class.abi.uint32_t_alignment),
852 fmt::arg("uint64_t_alignment", trace_class.abi.uint64_t_alignment),
853 fmt::arg("long_alignment", trace_class.abi.long_alignment),
854 fmt::arg("long_size", trace_class.abi.long_alignment),
855 fmt::arg("bits_per_long", trace_class.abi.bits_per_long),
856 fmt::arg("uuid", lttng::utils::uuid_to_str(trace_class.uuid)),
857 fmt::arg("byte_order",
858 trace_class.abi.byte_order == lst::byte_order::BIG_ENDIAN_ ? "be" : "le"),
859 fmt::arg("packet_header_layout", packet_header_visitor.move_description()));
d7bfb9b0
JG
860
861 /* Declare trace scope and type aliases. */
a57c248a 862 append_metadata_fragment(trace_class_tsdl);
da9dd521
JG
863
864 tsdl_trace_environment_visitor environment_visitor;
865 trace_class.accept(environment_visitor);
c22ded12 866 append_metadata_fragment(environment_visitor.move_description());
d7bfb9b0
JG
867}
868
869void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::clock_class& clock_class)
870{
871 auto uuid_str = clock_class.uuid ?
28ab034a
JG
872 fmt::format(" uuid = \"{}\";\n", lttng::utils::uuid_to_str(*clock_class.uuid)) :
873 "";
d7bfb9b0
JG
874
875 /* Assumes a single clock that maps to specific stream class fields/roles. */
28ab034a
JG
876 auto clock_class_str = fmt::format("clock {{\n"
877 " name = \"{name}\";\n"
878 /* Optional uuid. */
879 "{uuid}"
880 " description = \"{description}\";\n"
881 " freq = {frequency};\n"
882 " offset = {offset};\n"
883 "}};\n"
884 "\n",
885 fmt::arg("name", clock_class.name),
886 fmt::arg("uuid", uuid_str),
887 fmt::arg("description", clock_class.description),
888 fmt::arg("frequency", clock_class.frequency),
889 fmt::arg("offset", clock_class.offset));
d7bfb9b0 890
a57c248a 891 append_metadata_fragment(clock_class_str);
d7bfb9b0
JG
892}
893
894void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::stream_class& stream_class)
895{
b6bbb1d6 896 _current_stream_class = &stream_class;
28ab034a
JG
897 const auto clear_stream_class_on_exit =
898 lttng::make_scope_exit([this]() noexcept { _current_stream_class = nullptr; });
b6bbb1d6 899
d7bfb9b0 900 auto stream_class_str = fmt::format("stream {{\n"
28ab034a
JG
901 " id = {};\n",
902 stream_class.id);
903 variant_tsdl_keyword_sanitizer variant_sanitizer(
904 _sanitized_types_overrides,
905 [this](const lttng::sessiond::trace::field_location& location) -> const lst::type& {
906 return _lookup_field_type(location);
907 });
d7bfb9b0 908
4bcf2294 909 const auto *event_header = stream_class.event_header();
24ed18f2 910 if (event_header) {
28ab034a
JG
911 tsdl_field_visitor event_header_visitor{ _trace_abi,
912 1,
913 _sanitized_types_overrides,
914 stream_class.default_clock_class_name };
d7bfb9b0 915
b6bbb1d6 916 event_header->accept(variant_sanitizer);
24ed18f2
JG
917 event_header->accept(event_header_visitor);
918 stream_class_str += fmt::format(" event.header := {};\n",
28ab034a 919 event_header_visitor.move_description());
24ed18f2
JG
920 }
921
4bcf2294 922 const auto *packet_context = stream_class.packet_context();
24ed18f2 923 if (packet_context) {
28ab034a
JG
924 tsdl_field_visitor packet_context_visitor{ _trace_abi,
925 1,
926 _sanitized_types_overrides,
927 stream_class.default_clock_class_name };
24ed18f2 928
b6bbb1d6 929 packet_context->accept(variant_sanitizer);
24ed18f2
JG
930 packet_context->accept(packet_context_visitor);
931 stream_class_str += fmt::format(" packet.context := {};\n",
28ab034a 932 packet_context_visitor.move_description());
24ed18f2
JG
933 }
934
4bcf2294 935 const auto *event_context = stream_class.event_context();
24ed18f2 936 if (event_context) {
28ab034a
JG
937 tsdl_field_visitor event_context_visitor{ _trace_abi,
938 1,
939 _sanitized_types_overrides };
24ed18f2 940
b6bbb1d6 941 event_context->accept(variant_sanitizer);
24ed18f2
JG
942 event_context->accept(event_context_visitor);
943 stream_class_str += fmt::format(" event.context := {};\n",
28ab034a 944 event_context_visitor.move_description());
24ed18f2 945 }
d7bfb9b0 946
24ed18f2 947 stream_class_str += "};\n\n";
d7bfb9b0
JG
948
949 append_metadata_fragment(stream_class_str);
950}
951
952void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::event_class& event_class)
953{
b6bbb1d6 954 _current_event_class = &event_class;
28ab034a
JG
955 const auto clear_event_class_on_exit =
956 lttng::make_scope_exit([this]() noexcept { _current_event_class = nullptr; });
b6bbb1d6 957
d7bfb9b0
JG
958 auto event_class_str = fmt::format("event {{\n"
959 " name = \"{name}\";\n"
960 " id = {id};\n"
961 " stream_id = {stream_class_id};\n"
962 " loglevel = {log_level};\n",
28ab034a
JG
963 fmt::arg("name", event_class.name),
964 fmt::arg("id", event_class.id),
965 fmt::arg("stream_class_id", event_class.stream_class_id),
966 fmt::arg("log_level", event_class.log_level));
d7bfb9b0
JG
967
968 if (event_class.model_emf_uri) {
28ab034a
JG
969 event_class_str +=
970 fmt::format(" model.emf.uri = \"{}\";\n", *event_class.model_emf_uri);
d7bfb9b0
JG
971 }
972
28ab034a
JG
973 tsdl_field_visitor payload_visitor{ _trace_abi, 1, _sanitized_types_overrides };
974 variant_tsdl_keyword_sanitizer variant_sanitizer(
975 _sanitized_types_overrides,
976 [this](const lttng::sessiond::trace::field_location& location) -> const lst::type& {
977 return _lookup_field_type(location);
978 });
d7bfb9b0 979
b6bbb1d6
JG
980 event_class.payload->accept(variant_sanitizer);
981 event_class.payload->accept(payload_visitor);
d7bfb9b0 982
28ab034a
JG
983 event_class_str +=
984 fmt::format(" fields := {};\n}};\n\n", payload_visitor.move_description());
d7bfb9b0
JG
985
986 append_metadata_fragment(event_class_str);
987}
b6bbb1d6 988
28ab034a
JG
989void tsdl::details::type_overrider::publish(const lttng::sessiond::trace::type& original_type,
990 lttng::sessiond::trace::type::cuptr new_type_override)
b6bbb1d6
JG
991{
992 auto current_override = _overriden_types.find(&original_type);
993
994 if (current_override != _overriden_types.end()) {
995 current_override->second = std::move(new_type_override);
996 } else {
28ab034a
JG
997 _overriden_types.insert(
998 std::make_pair(&original_type, std::move(new_type_override)));
b6bbb1d6
JG
999 }
1000}
1001
28ab034a
JG
1002const lst::type&
1003tsdl::details::type_overrider::type(const lttng::sessiond::trace::type& original) const noexcept
b6bbb1d6
JG
1004{
1005 const auto result = _overriden_types.find(&original);
1006
1007 if (result != _overriden_types.end()) {
1008 /* Provide the overriden type. */
1009 return *result->second;
1010 }
1011
1012 /* Pass the original type through. */
1013 return original;
1014}
1015
1016namespace {
28ab034a
JG
1017const lttng::sessiond::trace::type&
1018lookup_type_from_root_type(const lttng::sessiond::trace::type& root_type,
1019 const lttng::sessiond::trace::field_location& field_location)
b6bbb1d6
JG
1020{
1021 const auto *type = &root_type;
1022
1023 for (const auto& location_element : field_location.elements_) {
1024 /* Only structures can be traversed. */
1025 const auto *struct_type = dynamic_cast<const lst::structure_type *>(type);
1026
1027 /*
1028 * Traverse the type by following the field location path.
1029 *
1030 * While field paths are assumed to have been validated before-hand,
1031 * a dynamic cast is performed here as an additional precaution
1032 * since none of this is performance-critical; it can be removed
1033 * safely.
1034 */
1035 if (!struct_type) {
1036 LTTNG_THROW_ERROR(fmt::format(
28ab034a
JG
1037 "Encountered a type that is not a structure while traversing field location: field-location=`{}`",
1038 field_location));
b6bbb1d6
JG
1039 }
1040
28ab034a
JG
1041 const auto field_found_it =
1042 std::find_if(struct_type->fields_.cbegin(),
1043 struct_type->fields_.cend(),
1044 [&location_element](const lst::field::cuptr& struct_field) {
1045 return struct_field->name == location_element;
1046 });
b6bbb1d6
JG
1047
1048 if (field_found_it == struct_type->fields_.cend()) {
1049 LTTNG_THROW_ERROR(fmt::format(
28ab034a
JG
1050 "Failed to find field using field location: field-name:=`{field_name}`, field-location=`{field_location}`",
1051 fmt::arg("field_location", field_location),
1052 fmt::arg("field_name", location_element)));
b6bbb1d6
JG
1053 }
1054
1055 type = &(*field_found_it)->get_type();
1056 }
1057
1058 return *type;
1059}
1060} /* anonymous namespace. */
1061
1062/*
1063 * The trace hierarchy is assumed to have been validated on creation.
1064 * This function can only fail due to a validation error, hence
1065 * why it throws on any unexpected/invalid field location.
1066 *
1067 * Does not return an overriden field type; it returns the original field type
1068 * as found in the trace hierarchy.
1069 */
1070const lttng::sessiond::trace::type& lttng::sessiond::tsdl::trace_class_visitor::_lookup_field_type(
28ab034a 1071 const lttng::sessiond::trace::field_location& location) const
b6bbb1d6
JG
1072{
1073 /* Validate the look-up is happening in a valid visit context. */
1074 switch (location.root_) {
1075 case lst::field_location::root::EVENT_RECORD_HEADER:
1076 case lst::field_location::root::EVENT_RECORD_PAYLOAD:
1077 if (!_current_event_class) {
1078 LTTNG_THROW_ERROR(
28ab034a 1079 "Field type look-up failure: no current event class in visitor's context");
b6bbb1d6
JG
1080 }
1081 /* fall through. */
1082 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT:
1083 case lst::field_location::root::PACKET_CONTEXT:
1084 if (!_current_stream_class) {
1085 LTTNG_THROW_ERROR(
28ab034a 1086 "Field type look-up failure: no current stream class in visitor's context");
b6bbb1d6
JG
1087 }
1088 /* fall through. */
1089 case lst::field_location::root::PACKET_HEADER:
1090 if (!_current_trace_class) {
1091 LTTNG_THROW_ERROR(
28ab034a 1092 "Field type look-up failure: no current trace class in visitor's context");
b6bbb1d6
JG
1093 }
1094
1095 break;
1096 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT:
1097 LTTNG_THROW_UNSUPPORTED_ERROR(
28ab034a 1098 "Field type look-up failure: event-record specific contexts are not supported");
b6bbb1d6
JG
1099 default:
1100 LTTNG_THROW_UNSUPPORTED_ERROR(
28ab034a 1101 "Field type look-up failure: unknown field location root");
b6bbb1d6
JG
1102 }
1103
1104 switch (location.root_) {
1105 case lst::field_location::root::PACKET_HEADER:
28ab034a 1106 return lookup_type_from_root_type(*_current_trace_class->packet_header(), location);
b6bbb1d6 1107 case lst::field_location::root::PACKET_CONTEXT:
28ab034a
JG
1108 return lookup_type_from_root_type(*_current_stream_class->packet_context(),
1109 location);
b6bbb1d6 1110 case lst::field_location::root::EVENT_RECORD_HEADER:
28ab034a 1111 return lookup_type_from_root_type(*_current_stream_class->event_header(), location);
b6bbb1d6 1112 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT:
28ab034a
JG
1113 return lookup_type_from_root_type(*_current_stream_class->event_context(),
1114 location);
b6bbb1d6 1115 case lst::field_location::root::EVENT_RECORD_PAYLOAD:
28ab034a 1116 return lookup_type_from_root_type(*_current_event_class->payload, location);
b6bbb1d6
JG
1117 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT:
1118 default:
1119 /* Unreachable as it was checked before. */
1120 abort();
1121 }
1122}
This page took 0.079108 seconds and 4 git commands to generate.