Tests: Add test to check shared-memory FD leaks after relayd dies
[lttng-tools.git] / src / bin / lttng-sessiond / field.hpp
1 /*
2 * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8 #ifndef LTTNG_FIELD_H
9 #define LTTNG_FIELD_H
10
11 #include <common/format.hpp>
12 #include <common/make-unique.hpp>
13
14 #include <vendor/optional.hpp>
15
16 #include <algorithm>
17 #include <memory>
18 #include <string>
19 #include <type_traits>
20 #include <vector>
21
22 namespace lttng {
23 namespace sessiond {
24 namespace trace {
25
26 class field_visitor;
27 class type_visitor;
28
29 enum class byte_order {
30 BIG_ENDIAN_,
31 LITTLE_ENDIAN_,
32 };
33
34 class field_location {
35 public:
36 enum class root {
37 PACKET_HEADER,
38 PACKET_CONTEXT,
39 EVENT_RECORD_HEADER,
40 EVENT_RECORD_COMMON_CONTEXT,
41 EVENT_RECORD_SPECIFIC_CONTEXT,
42 EVENT_RECORD_PAYLOAD,
43 };
44
45 using elements = std::vector<std::string>;
46
47 field_location(root lookup_root, elements elements);
48 bool operator==(const field_location& other) const noexcept;
49
50 const root root_;
51 const elements elements_;
52 };
53
54 /*
55 * Field, and the various field types, represents fields as exposed by the
56 * LTTng tracers. These classes do not attempt to describe the complete spectrum of the CTF
57 * specification.
58 */
59
60 class type {
61 public:
62 using cuptr = std::unique_ptr<const type>;
63
64 static byte_order reverse_byte_order(byte_order byte_order) noexcept;
65
66 bool operator==(const type& other) const noexcept;
67 bool operator!=(const type& other) const noexcept;
68
69 virtual ~type();
70 type(const type&) = delete;
71 type(type&&) = delete;
72 type& operator=(type&&) = delete;
73 type& operator=(const type&) = delete;
74
75 /* Obtain an independent copy of `type`. */
76 virtual type::cuptr copy() const = 0;
77
78 virtual void accept(type_visitor& visitor) const = 0;
79
80 const unsigned int alignment;
81
82 protected:
83 explicit type(unsigned int alignment);
84
85 private:
86 virtual bool _is_equal(const type& rhs) const noexcept = 0;
87 };
88
89 class field {
90 public:
91 using uptr = std::unique_ptr<field>;
92 using cuptr = std::unique_ptr<const field>;
93
94 field(std::string name, type::cuptr type);
95 void accept(field_visitor& visitor) const;
96 bool operator==(const field& other) const noexcept;
97
98 const type& get_type() const;
99 type::cuptr move_type() noexcept;
100
101 const std::string name;
102
103 private:
104 type::cuptr _type;
105 };
106
107 class integer_type : public type {
108 public:
109 enum class signedness {
110 SIGNED,
111 UNSIGNED,
112 };
113
114 enum class base {
115 BINARY = 2,
116 OCTAL = 8,
117 DECIMAL = 10,
118 HEXADECIMAL = 16,
119 };
120
121 enum class role {
122 DEFAULT_CLOCK_TIMESTAMP,
123 /* Packet header field class specific roles. */
124 DATA_STREAM_CLASS_ID,
125 DATA_STREAM_ID,
126 PACKET_MAGIC_NUMBER,
127 /* Packet context field class specific roles. */
128 DISCARDED_EVENT_RECORD_COUNTER_SNAPSHOT,
129 PACKET_CONTENT_LENGTH,
130 PACKET_END_DEFAULT_CLOCK_TIMESTAMP,
131 PACKET_SEQUENCE_NUMBER,
132 PACKET_TOTAL_LENGTH,
133 /* Event record field class roles. */
134 EVENT_RECORD_CLASS_ID,
135 };
136
137 using roles = std::vector<role>;
138
139 integer_type(unsigned int alignment,
140 byte_order byte_order,
141 unsigned int size,
142 signedness signedness,
143 base base,
144 roles roles = {});
145
146 type::cuptr copy() const override;
147
148 void accept(type_visitor& visitor) const override;
149
150 const enum byte_order byte_order;
151 const unsigned int size;
152 /*
153 * signedness and base are suffixed with '_' to work-around a bug in older
154 * GCCs (before 6) that do not recognize hidden/shadowed enumeration as valid
155 * nested-name-specifiers.
156 */
157 const signedness signedness_;
158 const base base_;
159 const roles roles_;
160
161 protected:
162 bool _is_equal(const type& other) const noexcept override;
163 };
164
165 class floating_point_type : public type {
166 public:
167 floating_point_type(unsigned int alignment,
168 byte_order byte_order,
169 unsigned int exponent_digits,
170 unsigned int mantissa_digits);
171
172 type::cuptr copy() const final;
173
174 void accept(type_visitor& visitor) const final;
175
176 const enum byte_order byte_order;
177 const unsigned int exponent_digits;
178 const unsigned int mantissa_digits;
179
180 private:
181 bool _is_equal(const type& other) const noexcept final;
182 };
183
184 class enumeration_type : public integer_type {
185 public:
186 ~enumeration_type() override = default;
187 enumeration_type(const enumeration_type&) = delete;
188 enumeration_type(enumeration_type&&) = delete;
189 enumeration_type& operator=(enumeration_type&&) = delete;
190 enumeration_type& operator=(const enumeration_type&) = delete;
191
192 protected:
193 enumeration_type(unsigned int alignment,
194 enum byte_order byte_order,
195 unsigned int size,
196 enum signedness signedness,
197 enum base base,
198 integer_type::roles roles = {});
199
200 void accept(type_visitor& visitor) const override = 0;
201 };
202
203 namespace details {
204 template <class MappingIntegerType>
205 class enumeration_mapping_range {
206 public:
207 using range_integer_t = MappingIntegerType;
208
209 enumeration_mapping_range(MappingIntegerType in_begin, MappingIntegerType in_end) :
210 begin{in_begin}, end{in_end}
211 {
212 }
213
214 const range_integer_t begin, end;
215 };
216
217 template <class MappingIntegerType>
218 bool operator==(const enumeration_mapping_range<MappingIntegerType>& lhs,
219 const enumeration_mapping_range<MappingIntegerType>& rhs) noexcept
220 {
221 return lhs.begin == rhs.begin && lhs.end == rhs.end;
222 }
223
224 template <class MappingIntegerType>
225 class enumeration_mapping {
226 public:
227 using range_t = enumeration_mapping_range<MappingIntegerType>;
228
229 enumeration_mapping(std::string in_name, MappingIntegerType value) :
230 name{ std::move(in_name) }, range{ value, value }
231 {
232 }
233
234 enumeration_mapping(std::string in_name, range_t in_range) :
235 name{ std::move(in_name) }, range{ in_range }
236 {
237 }
238
239 enumeration_mapping(const enumeration_mapping<MappingIntegerType>& other) = default;
240 enumeration_mapping(enumeration_mapping<MappingIntegerType>&& other) noexcept :
241 name{ std::move(other.name) }, range{ other.range }
242 {
243 }
244
245 enumeration_mapping& operator=(enumeration_mapping&&) = delete;
246 enumeration_mapping& operator=(const enumeration_mapping&) = delete;
247 ~enumeration_mapping() = default;
248
249 const std::string name;
250 /*
251 * Only one range per mapping is supported for the moment as
252 * the tracers (and CTF 1.8) can't express multiple ranges per
253 * mapping, which is allowed by CTF 2.
254 */
255 const range_t range;
256 };
257
258 template <class MappingIntegerType>
259 bool operator==(const enumeration_mapping<MappingIntegerType>& lhs,
260 const enumeration_mapping<MappingIntegerType>& rhs) noexcept
261 {
262 return lhs.name == rhs.name && lhs.range == rhs.range;
263 }
264 } /* namespace details */
265
266 template <typename MappingIntegerType>
267 class typed_enumeration_type : public enumeration_type {
268 public:
269 using mapping = details::enumeration_mapping<MappingIntegerType>;
270 using mappings = std::vector<mapping>;
271
272 static_assert(std::is_integral<MappingIntegerType>::value &&
273 sizeof(MappingIntegerType) == 8,
274 "MappingIntegerType must be either int64_t or uint64_t");
275
276 typed_enumeration_type(unsigned int in_alignment,
277 enum byte_order in_byte_order,
278 unsigned int in_size,
279 enum base in_base,
280 const std::shared_ptr<const mappings>& in_mappings,
281 integer_type::roles in_roles = {}) :
282 enumeration_type(in_alignment,
283 in_byte_order,
284 in_size,
285 std::is_signed<MappingIntegerType>::value ?
286 integer_type::signedness::SIGNED :
287 integer_type::signedness::UNSIGNED,
288 in_base,
289 std::move(in_roles)),
290 mappings_{std::move(in_mappings)}
291 {
292 }
293
294 type::cuptr copy() const override
295 {
296 return lttng::make_unique<typed_enumeration_type<MappingIntegerType>>(
297 alignment, byte_order, size, base_, mappings_, roles_);
298 }
299
300 void accept(type_visitor& visitor) const final;
301
302 const std::shared_ptr<const mappings> mappings_;
303
304 private:
305 bool _is_equal(const type& base_other) const noexcept final
306 {
307 const auto& other = static_cast<const typed_enumeration_type<MappingIntegerType>&>(
308 base_other);
309
310 return integer_type::_is_equal(base_other) && *this->mappings_ == *other.mappings_;
311 }
312 };
313
314 /* Aliases for all allowed enumeration mapping types. */
315 using signed_enumeration_type = typed_enumeration_type<int64_t>;
316 using unsigned_enumeration_type = typed_enumeration_type<uint64_t>;
317
318 class array_type : public type {
319 public:
320 array_type(unsigned int alignment, type::cuptr element_type);
321
322 const type::cuptr element_type;
323
324 protected:
325 bool _is_equal(const type& base_other) const noexcept override;
326 };
327
328 class static_length_array_type : public array_type {
329 public:
330 static_length_array_type(unsigned int alignment,
331 type::cuptr element_type,
332 uint64_t in_length);
333
334 type::cuptr copy() const final;
335
336 void accept(type_visitor& visitor) const final;
337
338 const uint64_t length;
339
340 private:
341 bool _is_equal(const type& base_other) const noexcept final;
342 };
343
344 class dynamic_length_array_type : public array_type {
345 public:
346 dynamic_length_array_type(unsigned int alignment,
347 type::cuptr element_type,
348 field_location length_field_location);
349
350 type::cuptr copy() const final;
351
352 void accept(type_visitor& visitor) const final;
353
354 const field_location length_field_location;
355
356 private:
357 bool _is_equal(const type& base_other) const noexcept final;
358 };
359
360 class static_length_blob_type : public type {
361 public:
362 enum class role {
363 /* Packet header field class specific role. */
364 METADATA_STREAM_UUID,
365 };
366
367 using roles = std::vector<role>;
368
369 static_length_blob_type(unsigned int alignment, uint64_t in_length_bytes, roles roles = {});
370
371 type::cuptr copy() const final;
372
373 void accept(type_visitor& visitor) const final;
374
375 const uint64_t length_bytes;
376 const roles roles_;
377
378 private:
379 bool _is_equal(const type& base_other) const noexcept final;
380 };
381
382 class dynamic_length_blob_type : public type {
383 public:
384 dynamic_length_blob_type(unsigned int alignment, field_location length_field_location);
385
386 type::cuptr copy() const final;
387
388 void accept(type_visitor& visitor) const final;
389
390 const field_location length_field_location;
391
392 private:
393 bool _is_equal(const type& base_other) const noexcept final;
394 };
395
396 class string_type : public type {
397 public:
398 enum class encoding {
399 ASCII,
400 UTF8,
401 };
402
403 string_type(unsigned int alignment, enum encoding encoding);
404
405 /*
406 * encoding is suffixed with '_' to work-around a bug in older
407 * GCCs (before 6) that do not recognize hidden/shadowed enumeration as valid
408 * nested-name-specifiers.
409 */
410 const encoding encoding_;
411
412 protected:
413 bool _is_equal(const type& base_other) const noexcept override;
414 };
415
416 class static_length_string_type : public string_type {
417 public:
418 static_length_string_type(
419 unsigned int alignment, enum encoding in_encoding, uint64_t length);
420
421 type::cuptr copy() const final;
422
423 void accept(type_visitor& visitor) const final;
424
425 const uint64_t length;
426
427 private:
428 bool _is_equal(const type& base_other) const noexcept final;
429 };
430
431 class dynamic_length_string_type : public string_type {
432 public:
433 dynamic_length_string_type(unsigned int alignment,
434 enum encoding in_encoding,
435 field_location length_field_location);
436
437 type::cuptr copy() const final;
438
439 void accept(type_visitor& visitor) const final;
440
441 const field_location length_field_location;
442
443 private:
444 bool _is_equal(const type& base_other) const noexcept final;
445 };
446
447 class null_terminated_string_type : public string_type {
448 public:
449 null_terminated_string_type(unsigned int alignment, enum encoding in_encoding);
450
451 type::cuptr copy() const final;
452
453 void accept(type_visitor& visitor) const final;
454 };
455
456 class structure_type : public type {
457 public:
458 using fields = std::vector<field::cuptr>;
459
460 structure_type(unsigned int alignment, fields in_fields);
461
462 type::cuptr copy() const final;
463
464 void accept(type_visitor& visitor) const final;
465
466 const fields fields_;
467
468 private:
469 bool _is_equal(const type& base_other) const noexcept final;
470 };
471
472 template <typename MappingIntegerType>
473 class variant_type : public type {
474 static_assert(std::is_same<MappingIntegerType,
475 unsigned_enumeration_type::mapping::range_t::
476 range_integer_t>::value ||
477 std::is_same<MappingIntegerType,
478 signed_enumeration_type::mapping::range_t::
479 range_integer_t>::value,
480 "Variant mapping integer type must be one of those allowed by typed_enumeration_type");
481
482 public:
483 using choice = std::pair<const details::enumeration_mapping<MappingIntegerType>, type::cuptr>;
484 using choices = std::vector<choice>;
485
486 variant_type(unsigned int in_alignment,
487 field_location in_selector_field_location,
488 choices in_choices) :
489 type(in_alignment),
490 selector_field_location{std::move(in_selector_field_location)},
491 choices_{std::move(in_choices)}
492 {
493 }
494
495 type::cuptr copy() const final
496 {
497 choices copy_of_choices;
498
499 copy_of_choices.reserve(choices_.size());
500 for (const auto& current_choice : choices_) {
501 copy_of_choices.emplace_back(
502 current_choice.first, current_choice.second->copy());
503 }
504
505 return lttng::make_unique<variant_type<MappingIntegerType>>(
506 alignment, selector_field_location, std::move(copy_of_choices));
507 }
508
509 void accept(type_visitor& visitor) const final;
510
511 const field_location selector_field_location;
512 const choices choices_;
513
514 private:
515 static bool _choices_are_equal(const choices& a, const choices& b)
516 {
517 if (a.size() != b.size()) {
518 return false;
519 }
520
521 return std::equal(a.cbegin(), a.cend(), b.cbegin(),
522 [](const choice& choice_a, const choice& choice_b) {
523 return choice_a.first == choice_b.first &&
524 *choice_a.second == *choice_b.second;
525 });
526 }
527
528 bool _is_equal(const type& base_other) const noexcept final
529 {
530 const auto& other = static_cast<decltype(*this)&>(base_other);
531
532 return selector_field_location == other.selector_field_location &&
533 _choices_are_equal(choices_, other.choices_);
534 }
535 };
536
537 class field_visitor {
538 public:
539 virtual ~field_visitor() = default;
540 field_visitor(field_visitor&&) = delete;
541 field_visitor(const field_visitor&) = delete;
542 field_visitor& operator=(const field_visitor&) = delete;
543 field_visitor& operator=(field_visitor&&) = delete;
544
545 virtual void visit(const field& field) = 0;
546
547 protected:
548 field_visitor() = default;
549 };
550
551 class type_visitor {
552 public:
553 virtual ~type_visitor() = default;
554 type_visitor(type_visitor&&) = delete;
555 type_visitor(const type_visitor&) = delete;
556 type_visitor& operator=(const type_visitor&) = delete;
557 type_visitor& operator=(type_visitor&&) = delete;
558
559 virtual void visit(const integer_type& type) = 0;
560 virtual void visit(const floating_point_type& type) = 0;
561 virtual void visit(const signed_enumeration_type& type) = 0;
562 virtual void visit(const unsigned_enumeration_type& type) = 0;
563 virtual void visit(const static_length_array_type& type) = 0;
564 virtual void visit(const dynamic_length_array_type& type) = 0;
565 virtual void visit(const static_length_blob_type& type) = 0;
566 virtual void visit(const dynamic_length_blob_type& type) = 0;
567 virtual void visit(const null_terminated_string_type& type) = 0;
568 virtual void visit(const static_length_string_type& type) = 0;
569 virtual void visit(const dynamic_length_string_type& type) = 0;
570 virtual void visit(const structure_type& type) = 0;
571 virtual void visit(const variant_type<signed_enumeration_type::mapping::range_t::range_integer_t>& type) = 0;
572 virtual void visit(const variant_type<unsigned_enumeration_type::mapping::range_t::range_integer_t>& type) = 0;
573
574 protected:
575 type_visitor() = default;
576 };
577
578 } /* namespace trace */
579 } /* namespace sessiond */
580 } /* namespace lttng */
581
582 /*
583 * Field formatters for libfmt.
584 *
585 * Due to a bug in g++ < 7.1, this specialization must be enclosed in the fmt namespace,
586 * see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480.
587 */
588 namespace fmt {
589 template <>
590 struct formatter<lttng::sessiond::trace::field_location> : formatter<std::string> {
591 template <typename FormatContextType>
592 typename FormatContextType::iterator format(
593 const lttng::sessiond::trace::field_location& location, FormatContextType& ctx)
594 {
595 std::string location_str{"["};
596
597 switch (location.root_) {
598 case lttng::sessiond::trace::field_location::root::PACKET_HEADER:
599 location_str += "\"packet-header\"";
600 break;
601 case lttng::sessiond::trace::field_location::root::PACKET_CONTEXT:
602 location_str += "\"packet-context\"";
603 break;
604 case lttng::sessiond::trace::field_location::root::EVENT_RECORD_HEADER:
605 location_str += "\"event-record-header\"";
606 break;
607 case lttng::sessiond::trace::field_location::root::EVENT_RECORD_COMMON_CONTEXT:
608 location_str += "\"event-record-common-context\"";
609 break;
610 case lttng::sessiond::trace::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT:
611 location_str += "\"event-record-specific-context\"";
612 break;
613 case lttng::sessiond::trace::field_location::root::EVENT_RECORD_PAYLOAD:
614 location_str += "\"event-record-payload\"";
615 break;
616 }
617
618 for (const auto &name : location.elements_) {
619 location_str += ", \"" + name + "\"";
620 }
621
622 location_str += "]";
623 return format_to(ctx.out(), location_str);
624 }
625 };
626
627 namespace details {
628 template <typename MappingIntegerType>
629 ::std::string format_mapping_range(typename lttng::sessiond::trace::typed_enumeration_type<
630 MappingIntegerType>::mapping::range_t range)
631 {
632 if (range.begin == range.end) {
633 return ::fmt::format("[{}]", range.begin);
634 } else {
635 return ::fmt::format("[{}, {}]", range.begin, range.end);
636 }
637 }
638 } /* namespace details */
639
640 template <>
641 struct formatter<typename lttng::sessiond::trace::signed_enumeration_type::mapping::range_t>
642 : formatter<std::string> {
643 template <typename FormatContextType>
644 typename FormatContextType::iterator
645 format(typename lttng::sessiond::trace::signed_enumeration_type::mapping::range_t range,
646 FormatContextType& ctx)
647 {
648 return format_to(ctx.out(),
649 details::format_mapping_range<
650 lttng::sessiond::trace::signed_enumeration_type::
651 mapping::range_t::range_integer_t>(
652 range));
653 }
654 };
655
656 template <>
657 struct formatter<typename lttng::sessiond::trace::unsigned_enumeration_type::mapping::range_t>
658 : formatter<std::string> {
659 template <typename FormatContextType>
660 typename FormatContextType::iterator
661 format(typename lttng::sessiond::trace::unsigned_enumeration_type::mapping::range_t range,
662 FormatContextType& ctx)
663 {
664 return format_to(ctx.out(),
665 details::format_mapping_range<
666 lttng::sessiond::trace::unsigned_enumeration_type::
667 mapping::range_t::range_integer_t>(
668 range));
669 }
670 };
671
672 } /* namespace fmt */
673
674 #endif /* LTTNG_FIELD_H */
This page took 0.041518 seconds and 4 git commands to generate.