vscode: Add configurations to run the executables under the debugger
[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) :
5e3b23be 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. */
f9a41357 225 new_mappings->emplace_back(lttng::format("_{}", mapping.name),
28ab034a 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
f9a41357 251 LTTNG_THROW_ERROR(lttng::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) :
5e3b23be
MJ
371 _indentation_level(indentation_level),
372 _trace_abi(abi),
cd9adb8b 373
5e3b23be 374 _default_clock_class_name(in_default_clock_class_name ?
28f23191
JG
375 in_default_clock_class_name->c_str() :
376 nullptr),
5e3b23be 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). */
f9a41357
JG
422 _description += lttng::format("size = {size}; align = {alignment};",
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:
f9a41357 446 LTTNG_THROW_ERROR(lttng::format(
28ab034a
JG
447 "Unexpected base encountered while serializing integer type to TSDL: base = {}",
448 (int) type.base_));
d7bfb9b0
JG
449 }
450
f9a41357 451 _description += lttng::format(" base = {};", base);
d7bfb9b0
JG
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 458
f9a41357 459 _description += lttng::format(" byte_order = {};", byte_order_str);
d7bfb9b0
JG
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:
f9a41357 473 LTTNG_THROW_ERROR(lttng::format(
28ab034a
JG
474 "Unexpected encoding encountered while serializing integer type to TSDL: encoding = {}",
475 (int) *_current_integer_encoding_override));
d7bfb9b0
JG
476 }
477
f9a41357 478 _description += lttng::format(" encoding = {};", encoding_str);
d7bfb9b0
JG
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 491 _description +=
f9a41357 492 lttng::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 499 {
f9a41357 500 _description += lttng::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 510
f9a41357 511 _description += lttng::format(" byte_order = {};", byte_order_str);
d7bfb9b0
JG
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) {
f9a41357
JG
536 _description += lttng::format(
537 "\"{mapping_name}\" = {mapping_value}",
538 fmt::arg("mapping_name", mapping.name),
539 fmt::arg("mapping_value", mapping.range.begin));
d7bfb9b0 540 } else {
f9a41357 541 _description += lttng::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) {
20c4b46a 569 LTTNG_ASSERT(!_current_field_name.empty());
f9a41357 570 _description += lttng::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);
f9a41357 578 _type_suffixes.emplace(lttng::format("[{}]", type.length));
d7bfb9b0
JG
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 */
20c4b46a 590 LTTNG_ASSERT(!_current_field_name.empty());
f9a41357 591 _description += lttng::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);
f9a41357 599 _type_suffixes.emplace(lttng::format(
28ab034a
JG
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) {
20c4b46a 679 LTTNG_ASSERT(!_current_field_name.empty());
f9a41357 680 _description += lttng::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++;
f9a41357 688 _description += lttng::format(
28ab034a
JG
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 705 field.second->accept(*this);
f9a41357 706 _description += lttng::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 792 {
f9a41357 793 _environment += lttng::format(" {} = {};\n", field.name, field.value);
da9dd521
JG
794 }
795
cd9adb8b 796 void visit(const lst::environment_field<const char *>& field) override
da9dd521 797 {
f65ab3f7
SM
798 /*
799 * clang-format 14 oscillates between two formatting for this
800 * line, disable it locally.
801 */
802 /* clang-format off */
f9a41357
JG
803 _environment += lttng::format(
804 " {} = \"{}\";\n", field.name, escape_tsdl_env_string_value(field.value));
f65ab3f7 805 /* clang-format on */
da9dd521
JG
806 }
807
808 /* Only call once. */
c22ded12 809 std::string move_description()
da9dd521
JG
810 {
811 _environment += "};\n\n";
812 return std::move(_environment);
813 }
814
815private:
5c7248cd 816 std::string _environment{ "env {\n" };
da9dd521 817};
d7bfb9b0
JG
818} /* namespace */
819
28ab034a
JG
820tsdl::trace_class_visitor::trace_class_visitor(
821 const lst::abi& trace_abi,
822 tsdl::append_metadata_fragment_function append_metadata_fragment) :
5e3b23be 823 _trace_abi(trace_abi), _append_metadata_fragment(std::move(append_metadata_fragment))
d7bfb9b0
JG
824{
825}
826
827void tsdl::trace_class_visitor::append_metadata_fragment(const std::string& fragment) const
828{
829 _append_metadata_fragment(fragment);
830}
831
832void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::trace_class& trace_class)
833{
b6bbb1d6
JG
834 /* Ensure this instance is not used against multiple trace classes. */
835 LTTNG_ASSERT(!_current_trace_class || _current_trace_class == &trace_class);
836 _current_trace_class = &trace_class;
837
28ab034a 838 tsdl_field_visitor packet_header_visitor{ trace_class.abi, 1, _sanitized_types_overrides };
24ed18f2 839
4bcf2294 840 trace_class.packet_header()->accept(packet_header_visitor);
24ed18f2 841
d7bfb9b0 842 /* Declare type aliases, trace class, and packet header. */
f9a41357 843 auto trace_class_tsdl = lttng::format(
28ab034a
JG
844 "/* CTF {ctf_major}.{ctf_minor} */\n\n"
845 "trace {{\n"
846 " major = {ctf_major};\n"
847 " minor = {ctf_minor};\n"
848 " uuid = \"{uuid}\";\n"
849 " byte_order = {byte_order};\n"
850 " packet.header := {packet_header_layout};\n"
851 "}};\n\n",
852 fmt::arg("ctf_major", ctf_spec_major),
853 fmt::arg("ctf_minor", ctf_spec_minor),
854 fmt::arg("uint8_t_alignment", trace_class.abi.uint8_t_alignment),
855 fmt::arg("uint16_t_alignment", trace_class.abi.uint16_t_alignment),
856 fmt::arg("uint32_t_alignment", trace_class.abi.uint32_t_alignment),
857 fmt::arg("uint64_t_alignment", trace_class.abi.uint64_t_alignment),
858 fmt::arg("long_alignment", trace_class.abi.long_alignment),
859 fmt::arg("long_size", trace_class.abi.long_alignment),
860 fmt::arg("bits_per_long", trace_class.abi.bits_per_long),
861 fmt::arg("uuid", lttng::utils::uuid_to_str(trace_class.uuid)),
862 fmt::arg("byte_order",
863 trace_class.abi.byte_order == lst::byte_order::BIG_ENDIAN_ ? "be" : "le"),
864 fmt::arg("packet_header_layout", packet_header_visitor.move_description()));
d7bfb9b0
JG
865
866 /* Declare trace scope and type aliases. */
a57c248a 867 append_metadata_fragment(trace_class_tsdl);
da9dd521
JG
868
869 tsdl_trace_environment_visitor environment_visitor;
870 trace_class.accept(environment_visitor);
c22ded12 871 append_metadata_fragment(environment_visitor.move_description());
d7bfb9b0
JG
872}
873
874void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::clock_class& clock_class)
875{
876 auto uuid_str = clock_class.uuid ?
f9a41357 877 lttng::format(" uuid = \"{}\";\n", lttng::utils::uuid_to_str(*clock_class.uuid)) :
28ab034a 878 "";
d7bfb9b0
JG
879
880 /* Assumes a single clock that maps to specific stream class fields/roles. */
f9a41357
JG
881 auto clock_class_str = lttng::format("clock {{\n"
882 " name = \"{name}\";\n"
883 /* Optional uuid. */
884 "{uuid}"
885 " description = \"{description}\";\n"
886 " freq = {frequency};\n"
887 " offset = {offset};\n"
888 "}};\n"
889 "\n",
890 fmt::arg("name", clock_class.name),
891 fmt::arg("uuid", uuid_str),
892 fmt::arg("description", clock_class.description),
893 fmt::arg("frequency", clock_class.frequency),
894 fmt::arg("offset", clock_class.offset));
d7bfb9b0 895
a57c248a 896 append_metadata_fragment(clock_class_str);
d7bfb9b0
JG
897}
898
899void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::stream_class& stream_class)
900{
b6bbb1d6 901 _current_stream_class = &stream_class;
28ab034a
JG
902 const auto clear_stream_class_on_exit =
903 lttng::make_scope_exit([this]() noexcept { _current_stream_class = nullptr; });
b6bbb1d6 904
f9a41357
JG
905 auto stream_class_str = lttng::format("stream {{\n"
906 " id = {};\n",
907 stream_class.id);
28ab034a
JG
908 variant_tsdl_keyword_sanitizer variant_sanitizer(
909 _sanitized_types_overrides,
910 [this](const lttng::sessiond::trace::field_location& location) -> const lst::type& {
911 return _lookup_field_type(location);
912 });
d7bfb9b0 913
4bcf2294 914 const auto *event_header = stream_class.event_header();
24ed18f2 915 if (event_header) {
28ab034a
JG
916 tsdl_field_visitor event_header_visitor{ _trace_abi,
917 1,
918 _sanitized_types_overrides,
919 stream_class.default_clock_class_name };
d7bfb9b0 920
b6bbb1d6 921 event_header->accept(variant_sanitizer);
24ed18f2 922 event_header->accept(event_header_visitor);
f9a41357
JG
923 stream_class_str += lttng::format(" event.header := {};\n",
924 event_header_visitor.move_description());
24ed18f2
JG
925 }
926
4bcf2294 927 const auto *packet_context = stream_class.packet_context();
24ed18f2 928 if (packet_context) {
28ab034a
JG
929 tsdl_field_visitor packet_context_visitor{ _trace_abi,
930 1,
931 _sanitized_types_overrides,
932 stream_class.default_clock_class_name };
24ed18f2 933
b6bbb1d6 934 packet_context->accept(variant_sanitizer);
24ed18f2 935 packet_context->accept(packet_context_visitor);
f9a41357
JG
936 stream_class_str += lttng::format(" packet.context := {};\n",
937 packet_context_visitor.move_description());
24ed18f2
JG
938 }
939
4bcf2294 940 const auto *event_context = stream_class.event_context();
24ed18f2 941 if (event_context) {
28ab034a
JG
942 tsdl_field_visitor event_context_visitor{ _trace_abi,
943 1,
944 _sanitized_types_overrides };
24ed18f2 945
b6bbb1d6 946 event_context->accept(variant_sanitizer);
24ed18f2 947 event_context->accept(event_context_visitor);
f9a41357
JG
948 stream_class_str += lttng::format(" event.context := {};\n",
949 event_context_visitor.move_description());
24ed18f2 950 }
d7bfb9b0 951
24ed18f2 952 stream_class_str += "};\n\n";
d7bfb9b0
JG
953
954 append_metadata_fragment(stream_class_str);
955}
956
957void tsdl::trace_class_visitor::visit(const lttng::sessiond::trace::event_class& event_class)
958{
b6bbb1d6 959 _current_event_class = &event_class;
28ab034a
JG
960 const auto clear_event_class_on_exit =
961 lttng::make_scope_exit([this]() noexcept { _current_event_class = nullptr; });
b6bbb1d6 962
f9a41357
JG
963 auto event_class_str =
964 lttng::format("event {{\n"
965 " name = \"{name}\";\n"
966 " id = {id};\n"
967 " stream_id = {stream_class_id};\n"
968 " loglevel = {log_level};\n",
969 fmt::arg("name", event_class.name),
970 fmt::arg("id", event_class.id),
971 fmt::arg("stream_class_id", event_class.stream_class_id),
972 fmt::arg("log_level", event_class.log_level));
d7bfb9b0
JG
973
974 if (event_class.model_emf_uri) {
28ab034a 975 event_class_str +=
f9a41357 976 lttng::format(" model.emf.uri = \"{}\";\n", *event_class.model_emf_uri);
d7bfb9b0
JG
977 }
978
28ab034a
JG
979 tsdl_field_visitor payload_visitor{ _trace_abi, 1, _sanitized_types_overrides };
980 variant_tsdl_keyword_sanitizer variant_sanitizer(
981 _sanitized_types_overrides,
982 [this](const lttng::sessiond::trace::field_location& location) -> const lst::type& {
983 return _lookup_field_type(location);
984 });
d7bfb9b0 985
b6bbb1d6
JG
986 event_class.payload->accept(variant_sanitizer);
987 event_class.payload->accept(payload_visitor);
d7bfb9b0 988
28ab034a 989 event_class_str +=
f9a41357 990 lttng::format(" fields := {};\n}};\n\n", payload_visitor.move_description());
d7bfb9b0
JG
991
992 append_metadata_fragment(event_class_str);
993}
b6bbb1d6 994
28ab034a
JG
995void tsdl::details::type_overrider::publish(const lttng::sessiond::trace::type& original_type,
996 lttng::sessiond::trace::type::cuptr new_type_override)
b6bbb1d6
JG
997{
998 auto current_override = _overriden_types.find(&original_type);
999
1000 if (current_override != _overriden_types.end()) {
1001 current_override->second = std::move(new_type_override);
1002 } else {
28ab034a
JG
1003 _overriden_types.insert(
1004 std::make_pair(&original_type, std::move(new_type_override)));
b6bbb1d6
JG
1005 }
1006}
1007
28ab034a
JG
1008const lst::type&
1009tsdl::details::type_overrider::type(const lttng::sessiond::trace::type& original) const noexcept
b6bbb1d6
JG
1010{
1011 const auto result = _overriden_types.find(&original);
1012
1013 if (result != _overriden_types.end()) {
1014 /* Provide the overriden type. */
1015 return *result->second;
1016 }
1017
1018 /* Pass the original type through. */
1019 return original;
1020}
1021
1022namespace {
28ab034a
JG
1023const lttng::sessiond::trace::type&
1024lookup_type_from_root_type(const lttng::sessiond::trace::type& root_type,
1025 const lttng::sessiond::trace::field_location& field_location)
b6bbb1d6
JG
1026{
1027 const auto *type = &root_type;
1028
1029 for (const auto& location_element : field_location.elements_) {
1030 /* Only structures can be traversed. */
1031 const auto *struct_type = dynamic_cast<const lst::structure_type *>(type);
1032
1033 /*
1034 * Traverse the type by following the field location path.
1035 *
1036 * While field paths are assumed to have been validated before-hand,
1037 * a dynamic cast is performed here as an additional precaution
1038 * since none of this is performance-critical; it can be removed
1039 * safely.
1040 */
1041 if (!struct_type) {
f9a41357 1042 LTTNG_THROW_ERROR(lttng::format(
28ab034a
JG
1043 "Encountered a type that is not a structure while traversing field location: field-location=`{}`",
1044 field_location));
b6bbb1d6
JG
1045 }
1046
28ab034a
JG
1047 const auto field_found_it =
1048 std::find_if(struct_type->fields_.cbegin(),
1049 struct_type->fields_.cend(),
1050 [&location_element](const lst::field::cuptr& struct_field) {
1051 return struct_field->name == location_element;
1052 });
b6bbb1d6
JG
1053
1054 if (field_found_it == struct_type->fields_.cend()) {
f9a41357 1055 LTTNG_THROW_ERROR(lttng::format(
28ab034a
JG
1056 "Failed to find field using field location: field-name:=`{field_name}`, field-location=`{field_location}`",
1057 fmt::arg("field_location", field_location),
1058 fmt::arg("field_name", location_element)));
b6bbb1d6
JG
1059 }
1060
1061 type = &(*field_found_it)->get_type();
1062 }
1063
1064 return *type;
1065}
1066} /* anonymous namespace. */
1067
1068/*
1069 * The trace hierarchy is assumed to have been validated on creation.
1070 * This function can only fail due to a validation error, hence
1071 * why it throws on any unexpected/invalid field location.
1072 *
1073 * Does not return an overriden field type; it returns the original field type
1074 * as found in the trace hierarchy.
1075 */
1076const lttng::sessiond::trace::type& lttng::sessiond::tsdl::trace_class_visitor::_lookup_field_type(
28ab034a 1077 const lttng::sessiond::trace::field_location& location) const
b6bbb1d6
JG
1078{
1079 /* Validate the look-up is happening in a valid visit context. */
1080 switch (location.root_) {
1081 case lst::field_location::root::EVENT_RECORD_HEADER:
1082 case lst::field_location::root::EVENT_RECORD_PAYLOAD:
1083 if (!_current_event_class) {
1084 LTTNG_THROW_ERROR(
28ab034a 1085 "Field type look-up failure: no current event class in visitor's context");
b6bbb1d6
JG
1086 }
1087 /* fall through. */
1088 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT:
1089 case lst::field_location::root::PACKET_CONTEXT:
1090 if (!_current_stream_class) {
1091 LTTNG_THROW_ERROR(
28ab034a 1092 "Field type look-up failure: no current stream class in visitor's context");
b6bbb1d6
JG
1093 }
1094 /* fall through. */
1095 case lst::field_location::root::PACKET_HEADER:
1096 if (!_current_trace_class) {
1097 LTTNG_THROW_ERROR(
28ab034a 1098 "Field type look-up failure: no current trace class in visitor's context");
b6bbb1d6
JG
1099 }
1100
1101 break;
1102 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT:
1103 LTTNG_THROW_UNSUPPORTED_ERROR(
28ab034a 1104 "Field type look-up failure: event-record specific contexts are not supported");
b6bbb1d6
JG
1105 default:
1106 LTTNG_THROW_UNSUPPORTED_ERROR(
28ab034a 1107 "Field type look-up failure: unknown field location root");
b6bbb1d6
JG
1108 }
1109
1110 switch (location.root_) {
1111 case lst::field_location::root::PACKET_HEADER:
28ab034a 1112 return lookup_type_from_root_type(*_current_trace_class->packet_header(), location);
b6bbb1d6 1113 case lst::field_location::root::PACKET_CONTEXT:
28ab034a
JG
1114 return lookup_type_from_root_type(*_current_stream_class->packet_context(),
1115 location);
b6bbb1d6 1116 case lst::field_location::root::EVENT_RECORD_HEADER:
28ab034a 1117 return lookup_type_from_root_type(*_current_stream_class->event_header(), location);
b6bbb1d6 1118 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT:
28ab034a
JG
1119 return lookup_type_from_root_type(*_current_stream_class->event_context(),
1120 location);
b6bbb1d6 1121 case lst::field_location::root::EVENT_RECORD_PAYLOAD:
28ab034a 1122 return lookup_type_from_root_type(*_current_event_class->payload, location);
b6bbb1d6
JG
1123 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT:
1124 default:
1125 /* Unreachable as it was checked before. */
1126 abort();
1127 }
1128}
This page took 0.094429 seconds and 4 git commands to generate.