Commit | Line | Data |
---|---|---|
d0b96690 | 1 | /* |
d0b96690 DG |
2 | * Copyright (C) 2010-2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> |
3 | * | |
ab5be9fa | 4 | * SPDX-License-Identifier: GPL-2.0-only |
d0b96690 | 5 | * |
d0b96690 DG |
6 | */ |
7 | ||
7532fa3b MD |
8 | #include "common/bytecode/bytecode.hpp" |
9 | #include "common/macros.hpp" | |
10 | #include "common/uuid.hpp" | |
11 | #include <endian.h> | |
6c1c0768 | 12 | #define _LGPL_SOURCE |
d0b96690 DG |
13 | #include <stdint.h> |
14 | #include <string.h> | |
15 | #include <stdarg.h> | |
16 | #include <stdio.h> | |
17 | #include <limits.h> | |
18 | #include <unistd.h> | |
19 | #include <inttypes.h> | |
7532fa3b MD |
20 | #include <common/common.hpp> |
21 | #include <common/time.hpp> | |
22 | #include <vector> | |
23 | #include <vendor/nlohmann/json.hpp> | |
d0b96690 | 24 | |
7532fa3b MD |
25 | #include "ust-registry.hpp" |
26 | #include "ust-clock.hpp" | |
27 | #include "ust-app.hpp" | |
d0b96690 | 28 | |
8c645bb0 MD |
29 | #define NR_CLOCK_OFFSET_SAMPLES 10 |
30 | ||
7532fa3b MD |
31 | using json = nlohmann::json; |
32 | ||
8c645bb0 | 33 | struct offset_sample { |
c2636b57 | 34 | int64_t offset; /* correlation offset */ |
8c645bb0 MD |
35 | uint64_t measure_delta; /* lower is better */ |
36 | }; | |
37 | ||
7532fa3b MD |
38 | enum class byte_order |
39 | { | |
40 | BIG, | |
41 | LITTLE, | |
42 | }; | |
43 | ||
44 | enum class display_base | |
45 | { | |
46 | BINARY, | |
47 | OCTAL, | |
48 | DECIMAL, | |
49 | HEXADECIMAL, | |
50 | }; | |
51 | ||
52 | struct metadata_generation_exception : public std::runtime_error | |
53 | { | |
54 | metadata_generation_exception(const char *what) | |
55 | : std::runtime_error(what) | |
56 | {} | |
57 | }; | |
58 | ||
59 | struct lttng_ust_ctl_field_iterator | |
60 | { | |
61 | lttng_ust_ctl_field_iterator(const lttng_ust_ctl_field *array, | |
62 | size_t len) | |
63 | : _array(array), _len(len) | |
64 | {} | |
65 | ||
66 | /* Make sure we don't pass it by value, by mistake. */ | |
67 | lttng_ust_ctl_field_iterator(const lttng_ust_ctl_field_iterator &) = delete; | |
68 | void operator=(const lttng_ust_ctl_field_iterator &) = delete; | |
69 | ||
70 | const lttng_ust_ctl_field &get_next() | |
71 | { | |
72 | if (done()) { | |
73 | throw metadata_generation_exception( | |
74 | "Field iterator overflow"); | |
75 | }; | |
76 | ||
77 | return _array[_cur++]; | |
78 | } | |
79 | ||
80 | bool done() const | |
81 | { | |
82 | return _cur >= _len; | |
83 | } | |
84 | ||
85 | private: | |
86 | const lttng_ust_ctl_field *_array; | |
87 | size_t _cur = 0; | |
88 | size_t _len; | |
89 | }; | |
90 | ||
91 | static | |
92 | byte_order get_byte_order(const ust_registry_session &session, bool reverse) | |
93 | { | |
94 | if (session.byte_order == BIG_ENDIAN) { | |
95 | return reverse ? byte_order::LITTLE : byte_order::BIG; | |
96 | } else { | |
97 | return reverse ? byte_order::BIG : byte_order::LITTLE; | |
98 | } | |
99 | } | |
100 | ||
101 | struct bits | |
102 | { | |
103 | using underlying_type = uint64_t; | |
104 | explicit bits(uint64_t val) | |
105 | : _val(val) | |
106 | {} | |
107 | ||
108 | underlying_type to_underlying() const | |
109 | { | |
110 | return static_cast<underlying_type>(_val); | |
111 | } | |
112 | ||
113 | bool operator==(bits other) | |
114 | { | |
115 | return to_underlying() == other.to_underlying(); | |
116 | } | |
117 | ||
118 | bool operator!=(bits other) | |
119 | { | |
120 | return to_underlying() != other.to_underlying(); | |
121 | } | |
122 | ||
123 | private: | |
124 | const uint64_t _val; | |
125 | }; | |
126 | ||
127 | static | |
128 | bits operator"" _bits(unsigned long long val) | |
129 | { | |
130 | return bits(val); | |
131 | } | |
132 | ||
133 | static | |
134 | json make_fragment(const char *type) | |
135 | { | |
136 | return { | |
137 | { "type", type } | |
138 | }; | |
139 | } | |
140 | ||
141 | static | |
142 | json make_field_class(const char *type, const char *role) | |
143 | { | |
144 | json fc { | |
145 | { "type", type }, | |
146 | }; | |
147 | ||
148 | if (role) { | |
149 | fc["roles"] = {role}; | |
150 | } | |
151 | ||
152 | return fc; | |
153 | } | |
154 | ||
155 | static | |
156 | const char *make_byte_order(byte_order byte_order) | |
157 | { | |
158 | switch (byte_order) { | |
159 | case byte_order::BIG: | |
160 | return "big-endian"; | |
161 | case byte_order::LITTLE: | |
162 | return "little-endian"; | |
163 | default: | |
164 | abort(); | |
165 | } | |
166 | } | |
167 | ||
168 | static | |
169 | json make_fixed_length_bit_array_field_class(const char *type, bits length, | |
170 | byte_order byte_order, bits alignment, const char *role) | |
171 | { | |
172 | json fc = make_field_class(type, role); | |
173 | ||
174 | fc.update ({ | |
175 | { "length", length.to_underlying() }, | |
176 | { "byte-order", make_byte_order(byte_order) }, | |
177 | }); | |
178 | ||
179 | if (alignment != 0_bits) { | |
180 | fc["alignment"] = alignment.to_underlying(); | |
181 | } | |
182 | ||
183 | return fc; | |
184 | } | |
185 | ||
186 | static | |
187 | json make_display_base(display_base display_base) | |
188 | { | |
189 | switch (display_base) { | |
190 | case display_base::BINARY: | |
191 | return 2; | |
192 | case display_base::OCTAL: | |
193 | return 8; | |
194 | case display_base::DECIMAL: | |
195 | return 10; | |
196 | case display_base::HEXADECIMAL: | |
197 | return 16; | |
198 | default: | |
199 | abort(); | |
200 | } | |
201 | } | |
202 | ||
203 | static | |
204 | json make_abstract_integer_field_class(display_base preferred_display_base) | |
205 | { | |
206 | json base = json::object(); | |
207 | if (preferred_display_base != display_base::DECIMAL) { | |
208 | base = { | |
209 | { "preferred-display-base", | |
210 | make_display_base(preferred_display_base)}, | |
211 | ||
212 | }; | |
213 | } | |
214 | return base; | |
215 | } | |
216 | ||
217 | static | |
218 | json make_fixed_length_unsigned_integer_field_class(const char *type, | |
219 | bits length, byte_order byte_order, bits alignment, | |
220 | display_base preferred_display_base, const char *role) | |
221 | { | |
222 | json fc = make_fixed_length_bit_array_field_class(type, length, | |
223 | byte_order, alignment, role); | |
224 | fc.update(make_abstract_integer_field_class(preferred_display_base)); | |
225 | return fc; | |
226 | } | |
227 | ||
228 | static | |
229 | json make_fixed_length_signed_integer_field_class(const char *type, | |
230 | bits length, byte_order byte_order, bits alignment, | |
231 | display_base preferred_display_base, const char *role) | |
232 | { | |
233 | json fc = make_fixed_length_bit_array_field_class(type, length, | |
234 | byte_order, alignment, role); | |
235 | fc.update(make_abstract_integer_field_class(preferred_display_base)); | |
236 | return fc; | |
237 | } | |
238 | ||
239 | static | |
240 | json make_fixed_length_unsigned_integer_field_class(bits length, | |
241 | byte_order byte_order, bits alignment, | |
242 | display_base preferred_display_base, const char *role) | |
243 | { | |
244 | return make_fixed_length_unsigned_integer_field_class( | |
245 | "fixed-length-unsigned-integer", length, byte_order, | |
246 | alignment, preferred_display_base, role); | |
247 | } | |
248 | ||
249 | static | |
250 | json make_fixed_length_signed_integer_field_class(bits length, | |
251 | byte_order byte_order, bits alignment, | |
252 | display_base preferred_display_base, const char *role) | |
253 | { | |
254 | return make_fixed_length_signed_integer_field_class( | |
255 | "fixed-length-signed-integer", length, byte_order, | |
256 | alignment, preferred_display_base, role); | |
257 | } | |
258 | ||
259 | static | |
260 | json make_fixed_length_floating_point_number_field_class( | |
261 | bits length, byte_order byte_order, bits alignment) { | |
262 | return make_fixed_length_bit_array_field_class( | |
263 | "fixed-length-floating-point-number", | |
264 | length, byte_order, alignment, nullptr); | |
265 | } | |
266 | ||
267 | struct integer_range | |
268 | { | |
269 | integer_range(int64_t lower_bound_, int64_t upper_bound_) | |
270 | : is_signed(true), lower_bound(lower_bound_), upper_bound(upper_bound_) | |
271 | {} | |
272 | ||
273 | integer_range(uint64_t lower_bound_, uint64_t upper_bound_) | |
274 | : is_signed(false), lower_bound(lower_bound_), upper_bound(upper_bound_) | |
275 | {} | |
276 | ||
277 | const bool is_signed; | |
278 | const int64_t lower_bound, upper_bound; | |
279 | }; | |
280 | ||
281 | struct integer_range_set | |
282 | { | |
283 | using vec_type = std::vector<integer_range>; | |
284 | using const_iterator = vec_type::const_iterator; | |
285 | ||
286 | integer_range_set() = default; | |
287 | DISABLE_COPY_AND_ASSIGN(integer_range_set); | |
288 | integer_range_set(integer_range_set &&) = default; | |
289 | ||
290 | void add_range(int64_t lower_bound, int64_t upper_bound) | |
291 | { | |
292 | _ranges.emplace_back(lower_bound, upper_bound); | |
293 | } | |
294 | ||
295 | void add_range(uint64_t lower_bound, uint64_t upper_bound) | |
296 | { | |
297 | _ranges.emplace_back(lower_bound, upper_bound); | |
298 | } | |
299 | ||
300 | const_iterator begin() const | |
301 | { return _ranges.begin(); } | |
302 | ||
303 | const_iterator end() const | |
304 | { return _ranges.end(); } | |
305 | ||
306 | private: | |
307 | vec_type _ranges; | |
308 | }; | |
309 | ||
310 | struct enumeration_mapping | |
311 | { | |
312 | enumeration_mapping(const char *name_, json ranges_) | |
313 | : name(name_), ranges(std::move(ranges_)) | |
314 | {} | |
315 | ||
316 | const char *const name; | |
317 | json ranges; | |
318 | }; | |
319 | ||
320 | struct enumeration_mappings | |
321 | { | |
322 | using vec_type = std::vector<enumeration_mapping>; | |
323 | using const_iterator = vec_type::const_iterator; | |
324 | ||
325 | enumeration_mappings() = default; | |
326 | DISABLE_COPY_AND_ASSIGN(enumeration_mappings); | |
327 | enumeration_mappings(enumeration_mappings &&) = default; | |
328 | ||
329 | void add_mapping(const char *name_, json ranges_) | |
330 | { | |
331 | _mappings.emplace_back(name_, std::move(ranges_)); | |
332 | } | |
333 | ||
334 | const_iterator begin() const | |
335 | { return _mappings.begin(); } | |
336 | ||
337 | const_iterator end() const | |
338 | { return _mappings.end(); } | |
339 | ||
340 | private: | |
341 | vec_type _mappings; | |
342 | }; | |
343 | ||
344 | static | |
345 | json make_integer_range_set(const integer_range_set &ranges) | |
346 | { | |
347 | json rs = json::array(); | |
348 | ||
349 | for (const integer_range &range : ranges) { | |
350 | rs.push_back({ range.lower_bound, range.upper_bound }); | |
351 | } | |
352 | ||
353 | return rs; | |
354 | } | |
355 | ||
356 | static json | |
357 | make_enumeration_mappings(enumeration_mappings mappings) | |
358 | { | |
359 | json mappings_obj = json::object(); | |
360 | ||
361 | for (const enumeration_mapping &mapping : mappings) { | |
362 | mappings_obj[mapping.name] = std::move(mapping.ranges); | |
363 | } | |
364 | ||
365 | return mappings_obj; | |
366 | } | |
367 | ||
da860cab | 368 | static |
7532fa3b MD |
369 | json make_abstract_enumeration_field_class(display_base preferred_display_base, |
370 | enumeration_mappings mappings) | |
371 | { | |
372 | json fc = make_abstract_integer_field_class(preferred_display_base); | |
373 | fc["mappings"] = make_enumeration_mappings(std::move(mappings)); | |
374 | return fc; | |
375 | } | |
376 | ||
377 | ||
378 | static | |
379 | json make_fixed_length_unsigned_enumeration_field_class(bits length, | |
380 | byte_order byte_order, bits alignment, | |
381 | display_base preferred_display_base, const char *role, | |
382 | enumeration_mappings mappings) | |
383 | { | |
384 | json fc = make_fixed_length_unsigned_integer_field_class( | |
385 | "fixed-length-unsigned-enumeration", length, byte_order, | |
386 | alignment, preferred_display_base, role); | |
387 | fc.update(make_abstract_enumeration_field_class(preferred_display_base, | |
388 | std::move(mappings))); | |
389 | return fc; | |
390 | } | |
391 | ||
392 | static | |
393 | json make_fixed_length_signed_enumeration_field_class(bits length, | |
394 | byte_order byte_order, bits alignment, | |
395 | display_base preferred_display_base, const char *role, | |
396 | enumeration_mappings mappings) | |
397 | { | |
398 | json fc = make_fixed_length_signed_integer_field_class( | |
399 | "fixed-length-signed-enumeration", length, byte_order, | |
400 | alignment, preferred_display_base, role); | |
401 | fc.update(make_abstract_enumeration_field_class(preferred_display_base, | |
402 | std::move(mappings))); | |
403 | return fc; | |
404 | } | |
405 | ||
406 | static | |
407 | json make_fixed_length_enumeration_field_class(bool signedness, | |
408 | bits length, byte_order byte_order, bits alignment, | |
409 | display_base preferred_display_base, | |
410 | enumeration_mappings mappings) | |
411 | { | |
412 | if (signedness) { | |
413 | return make_fixed_length_signed_enumeration_field_class( | |
414 | length, byte_order, alignment, preferred_display_base, | |
415 | nullptr, std::move(mappings)); | |
416 | } else { | |
417 | return make_fixed_length_unsigned_enumeration_field_class( | |
418 | length, byte_order, alignment, preferred_display_base, | |
419 | nullptr, std::move(mappings)); | |
420 | } | |
421 | } | |
422 | ||
423 | static | |
424 | json make_static_length_blob_field_class(uint64_t length, const char *role) | |
425 | { | |
426 | json fc = make_field_class("static-length-blob", role); | |
427 | ||
428 | fc.update({ | |
429 | { "length", length }, | |
430 | }); | |
431 | ||
432 | return fc; | |
433 | } | |
434 | ||
435 | static | |
436 | json make_uuid_field_class(const char *role) | |
437 | { | |
438 | return make_static_length_blob_field_class(16, role); | |
439 | } | |
440 | ||
441 | struct structure_member | |
442 | { | |
443 | structure_member(const char *name_, json field_class_) | |
444 | : name(name_), field_class(std::move(field_class_)) | |
445 | {} | |
446 | ||
447 | DISABLE_COPY_AND_ASSIGN(structure_member); | |
448 | structure_member(structure_member &&) = default; | |
449 | ||
450 | const char *const name; | |
451 | json field_class; | |
452 | }; | |
453 | ||
454 | struct structure_members | |
455 | { | |
456 | using vec_type = std::vector<structure_member>; | |
457 | ||
458 | structure_members() = default; | |
459 | structure_members(const structure_members &) = delete; | |
460 | structure_members(structure_members &&) = default; | |
461 | structure_members &operator=(const structure_members &) = delete; | |
462 | ||
463 | void add_member(const char *name, json field_class) | |
464 | { | |
465 | _members.emplace_back(structure_member( | |
466 | name, std::move(field_class) | |
467 | )); | |
468 | } | |
469 | ||
470 | vec_type::iterator begin() | |
471 | { return _members.begin (); } | |
472 | ||
473 | vec_type::iterator end() | |
474 | { return _members.end (); } | |
475 | ||
476 | private: | |
477 | vec_type _members; | |
478 | }; | |
479 | ||
480 | static | |
481 | json make_struct_member(structure_member member) | |
482 | { | |
483 | return { | |
484 | { "name", member.name }, | |
485 | { "field-class", std::move(member.field_class) }, | |
486 | }; | |
487 | } | |
488 | ||
489 | static | |
490 | json make_structure_members(structure_members members) | |
491 | { | |
492 | json members_obj = json::array(); | |
493 | ||
494 | for (structure_member &member : members) { | |
495 | members_obj.push_back(make_struct_member(std::move(member))); | |
496 | } | |
497 | ||
498 | return members_obj; | |
499 | } | |
500 | ||
501 | static | |
502 | json make_structure_field_class(bits minimum_alignment, | |
503 | structure_members members) | |
504 | { | |
505 | json fc = make_field_class("structure", nullptr); | |
506 | ||
507 | fc["member-classes"] = make_structure_members(std::move(members)); | |
508 | ||
509 | if (minimum_alignment != 0_bits) { | |
510 | fc["minimum-alignment"] = minimum_alignment.to_underlying(); | |
511 | } | |
512 | ||
513 | return fc; | |
514 | } | |
515 | ||
516 | ||
517 | struct variant_option | |
518 | { | |
519 | variant_option(const char *name_, | |
520 | json selector_field_ranges_, | |
521 | json field_class_) | |
522 | : name(name_), | |
523 | selector_field_ranges(std::move(selector_field_ranges_)), | |
524 | field_class(std::move(field_class_)) | |
525 | {} | |
526 | ||
527 | variant_option(std::string name_, | |
528 | json selector_field_ranges_, | |
529 | json field_class_) | |
530 | : _name_buf(std::move(name_)), | |
531 | name(_name_buf.c_str()), | |
532 | selector_field_ranges(std::move(selector_field_ranges_)), | |
533 | field_class(std::move(field_class_)) | |
534 | {} | |
535 | ||
536 | DISABLE_COPY_AND_ASSIGN(variant_option); | |
537 | ||
538 | variant_option(variant_option &&other) | |
539 | : _name_buf(std::move(other._name_buf)), | |
540 | /* | |
541 | * If `_name_buf` is empty, `name` is not managed by us, | |
542 | * just copy the pointer. If `_name_buf` is not empty, | |
543 | * make `name` point to our local copy. | |
544 | */ | |
545 | name(_name_buf.empty() ? other.name : _name_buf.c_str()), | |
546 | selector_field_ranges(other.selector_field_ranges), | |
547 | field_class(other.field_class) | |
548 | ||
549 | {} | |
550 | ||
551 | private: | |
552 | /* | |
553 | * Buffer for `name`, if it is dynamically allocated andmanaged by us. | |
554 | */ | |
555 | const std::string _name_buf; | |
556 | ||
557 | public: | |
558 | const char *name; | |
559 | json selector_field_ranges; | |
560 | json field_class; | |
561 | }; | |
562 | ||
563 | struct variant_options | |
564 | { | |
565 | using vec_type = std::vector<variant_option>; | |
566 | using iterator = vec_type::iterator; | |
567 | using const_iterator = vec_type::const_iterator; | |
568 | ||
569 | variant_options() = default; | |
570 | DISABLE_COPY_AND_ASSIGN(variant_options); | |
571 | variant_options(variant_options &&) = default; | |
572 | ||
573 | void add_option(const char *name, json selector_field_ranges, | |
574 | json field_class) | |
575 | { | |
576 | _options.emplace_back(variant_option( | |
577 | name, std::move(selector_field_ranges), | |
578 | std::move(field_class) | |
579 | )); | |
580 | } | |
581 | ||
582 | void add_option(std::string name, json selector_field_ranges, | |
583 | json field_class) | |
584 | { | |
585 | _options.emplace_back(variant_option( | |
586 | std::move(name), std::move(selector_field_ranges), | |
587 | std::move(field_class) | |
588 | )); | |
589 | } | |
590 | ||
591 | iterator begin() | |
592 | { return _options.begin(); } | |
593 | ||
594 | iterator end() | |
595 | { return _options.end(); } | |
596 | ||
597 | const_iterator begin() const | |
598 | { return _options.begin(); } | |
599 | ||
600 | const_iterator end() const | |
601 | { return _options.end(); } | |
602 | ||
603 | private: | |
604 | vec_type _options; | |
605 | }; | |
606 | ||
607 | struct field_path | |
608 | { | |
609 | using vec_type = std::vector<const char *>; | |
610 | ||
611 | struct popper | |
612 | { | |
613 | popper(struct field_path &path) | |
614 | : _path(path) | |
615 | {} | |
616 | ||
617 | popper(const popper &) = delete; | |
618 | popper(popper &&) = default; | |
619 | void operator=(const popper &) = delete; | |
620 | ||
621 | ~popper() | |
622 | { | |
623 | _path._path.pop_back(); | |
624 | ||
625 | } | |
626 | ||
627 | private: | |
628 | field_path &_path; | |
629 | }; | |
630 | ||
631 | field_path(const char *root) | |
632 | : _path({root}) | |
633 | {} | |
634 | ||
635 | field_path(std::initializer_list<const char *> list) | |
636 | : _path(list) | |
637 | {} | |
638 | ||
639 | const vec_type &path() const | |
640 | { | |
641 | return _path; | |
642 | } | |
643 | ||
644 | popper push(const char *element) | |
645 | { | |
646 | _path.push_back(element); | |
647 | return popper(*this); | |
648 | } | |
649 | ||
650 | private: | |
651 | vec_type _path; | |
652 | }; | |
653 | ||
654 | static | |
655 | json make_variant_option(variant_option option) | |
656 | { | |
657 | return { | |
658 | { "name", option.name }, | |
659 | { "selector-field-ranges", std::move(option.selector_field_ranges) }, | |
660 | { "field-class", std::move(option.field_class ) }, | |
661 | }; | |
662 | } | |
663 | ||
664 | static | |
665 | json make_variant_options(variant_options options) | |
666 | { | |
667 | json options_obj = json::array(); | |
668 | ||
669 | for (variant_option &option : options) { | |
670 | options_obj.push_back(make_variant_option(std::move(option))); | |
671 | } | |
672 | ||
673 | return options_obj; | |
674 | } | |
675 | ||
676 | static | |
677 | json make_field_location(const field_path &field_location) | |
678 | { | |
679 | return field_location.path(); | |
680 | } | |
681 | ||
682 | static | |
683 | json make_variant_field_class(variant_options options, | |
684 | const field_path &selector_field_location) | |
685 | { | |
686 | json fc = make_field_class("variant", nullptr); | |
687 | ||
688 | fc["options"] = make_variant_options(std::move(options)); | |
689 | fc["selector-field-location"] = | |
690 | make_field_location(selector_field_location); | |
691 | ||
692 | return fc; | |
693 | } | |
694 | ||
695 | static | |
696 | json make_array_field_class(const char *type, json element_field_class, | |
697 | bits minimum_alignment) | |
698 | { | |
699 | json fc = make_field_class(type, nullptr); | |
700 | ||
701 | fc["element-field-class"] = std::move(element_field_class); | |
702 | ||
703 | if (minimum_alignment != 0_bits) { | |
704 | fc["minimum-alignment"] = minimum_alignment.to_underlying(); | |
705 | } | |
706 | ||
707 | return fc; | |
708 | } | |
709 | ||
710 | static | |
711 | json make_static_length_array_field_class(json element_field_class, | |
712 | uint64_t length, bits minimum_alignment) | |
713 | { | |
714 | json fc = make_array_field_class("static-length-array", | |
715 | element_field_class, minimum_alignment); | |
716 | ||
717 | fc["length"] = length; | |
718 | ||
719 | return fc; | |
720 | } | |
721 | ||
722 | static | |
723 | json make_dynamic_length_array_field_class(json element_field_class, | |
724 | const field_path &length_field_location, bits minimum_alignment) | |
725 | { | |
726 | json fc = make_array_field_class("dynamic-length-array", | |
727 | element_field_class, minimum_alignment); | |
728 | ||
729 | fc["length-field-location"] = | |
730 | make_field_location(length_field_location); | |
731 | ||
732 | return fc; | |
733 | } | |
734 | ||
735 | static | |
736 | json make_string_field_class() | |
737 | { | |
738 | return make_field_class("null-terminated-string", nullptr); | |
739 | } | |
740 | ||
741 | static | |
742 | json make_packet_header_field_class(const ust_registry_session &session) | |
743 | { | |
744 | byte_order bo = get_byte_order(session, false); | |
745 | ||
746 | structure_members members; | |
747 | ||
748 | members.add_member("magic", | |
749 | make_fixed_length_unsigned_integer_field_class( | |
750 | 32_bits, bo, 8_bits, display_base::HEXADECIMAL, | |
751 | "packet-magic-number")); | |
752 | members.add_member("uuid", | |
753 | make_uuid_field_class("trace-class-uuid")); | |
754 | members.add_member("stream_id", | |
755 | make_fixed_length_unsigned_integer_field_class( | |
756 | 32_bits, bo, 8_bits, display_base::DECIMAL, | |
757 | "data-stream-class-id")); | |
758 | members.add_member("stream_instance_id", | |
759 | make_fixed_length_unsigned_integer_field_class( | |
760 | 64_bits, bo, 8_bits, display_base::DECIMAL, | |
761 | "data-stream-id")); | |
762 | ||
763 | return make_structure_field_class(0_bits, std::move(members)); | |
764 | } | |
765 | ||
766 | static | |
767 | json make_uuid(uint8_t uuid_bytes[LTTNG_UUID_LEN]) | |
768 | { | |
769 | json uuid = json::array(); | |
770 | ||
771 | for (size_t i = 0; i < LTTNG_UUID_LEN; ++i) { | |
772 | uuid.push_back(uuid_bytes[i]); | |
773 | } | |
774 | ||
775 | return uuid; | |
776 | } | |
777 | ||
778 | static | |
779 | json make_clock_offset(int64_t offset_cycles, uint64_t freq) | |
780 | { | |
781 | // FIXME: not tested at all, especially not for negative values of offset | |
782 | ||
783 | /* Whole seconds. */ | |
784 | int64_t s = offset_cycles / freq; | |
785 | ||
786 | /* Remaining cycles. */ | |
787 | int64_t cycles = offset_cycles % freq; | |
788 | ||
789 | return { | |
790 | { "seconds", s }, | |
791 | { "cycles", cycles }, | |
792 | }; | |
793 | } | |
da860cab | 794 | |
d0b96690 DG |
795 | static inline |
796 | int get_count_order(unsigned int count) | |
797 | { | |
798 | int order; | |
799 | ||
78ddfe55 | 800 | order = lttng_fls(count) - 1; |
46b4dda6 | 801 | if (count & (count - 1)) { |
d0b96690 | 802 | order++; |
46b4dda6 | 803 | } |
a0377dfe | 804 | LTTNG_ASSERT(order >= 0); |
d0b96690 DG |
805 | return order; |
806 | } | |
807 | ||
808 | /* | |
809 | * Returns offset where to write in metadata array, or negative error value on error. | |
810 | */ | |
811 | static | |
7532fa3b | 812 | ssize_t metadata_reserve(ust_registry_session &session, size_t len) |
d0b96690 | 813 | { |
7532fa3b | 814 | size_t new_len = session.metadata_len + len; |
d0b96690 | 815 | size_t new_alloc_len = new_len; |
7532fa3b | 816 | size_t old_alloc_len = session.metadata_alloc_len; |
d0b96690 DG |
817 | ssize_t ret; |
818 | ||
819 | if (new_alloc_len > (UINT32_MAX >> 1)) | |
820 | return -EINVAL; | |
821 | if ((old_alloc_len << 1) > (UINT32_MAX >> 1)) | |
822 | return -EINVAL; | |
823 | ||
824 | if (new_alloc_len > old_alloc_len) { | |
825 | char *newptr; | |
826 | ||
827 | new_alloc_len = | |
7532fa3b MD |
828 | std::max<size_t>(1U << get_count_order(new_alloc_len), old_alloc_len << 1); |
829 | newptr = (char *) realloc(session.metadata, new_alloc_len); | |
d0b96690 DG |
830 | if (!newptr) |
831 | return -ENOMEM; | |
7532fa3b | 832 | session.metadata = newptr; |
d0b96690 | 833 | /* We zero directly the memory from start of allocation. */ |
7532fa3b MD |
834 | memset(&session.metadata[old_alloc_len], 0, new_alloc_len - old_alloc_len); |
835 | session.metadata_alloc_len = new_alloc_len; | |
d0b96690 | 836 | } |
7532fa3b MD |
837 | ret = session.metadata_len; |
838 | session.metadata_len += len; | |
d0b96690 DG |
839 | return ret; |
840 | } | |
841 | ||
d7ba1388 | 842 | static |
7532fa3b | 843 | int metadata_file_append(ust_registry_session &session, |
d7ba1388 MD |
844 | const char *str, size_t len) |
845 | { | |
846 | ssize_t written; | |
847 | ||
7532fa3b | 848 | if (session.metadata_fd < 0) { |
d7ba1388 MD |
849 | return 0; |
850 | } | |
851 | /* Write to metadata file */ | |
7532fa3b | 852 | written = lttng_write(session.metadata_fd, str, len); |
d7ba1388 MD |
853 | if (written != len) { |
854 | return -1; | |
855 | } | |
856 | return 0; | |
857 | } | |
858 | ||
d0b96690 DG |
859 | /* |
860 | * We have exclusive access to our metadata buffer (protected by the | |
861 | * ust_lock), so we can do racy operations such as looking for | |
862 | * remaining space left in packet and write, since mutual exclusion | |
863 | * protects us from concurrent writes. | |
864 | */ | |
d22ad5f8 | 865 | static ATTR_FORMAT_PRINTF(2, 3) |
7532fa3b | 866 | int lttng_metadata_printf(ust_registry_session &session, |
d0b96690 DG |
867 | const char *fmt, ...) |
868 | { | |
869 | char *str = NULL; | |
870 | size_t len; | |
871 | va_list ap; | |
872 | ssize_t offset; | |
873 | int ret; | |
874 | ||
875 | va_start(ap, fmt); | |
876 | ret = vasprintf(&str, fmt, ap); | |
877 | va_end(ap); | |
878 | if (ret < 0) | |
879 | return -ENOMEM; | |
880 | ||
881 | len = strlen(str); | |
882 | offset = metadata_reserve(session, len); | |
883 | if (offset < 0) { | |
884 | ret = offset; | |
885 | goto end; | |
886 | } | |
7532fa3b | 887 | memcpy(&session.metadata[offset], str, len); |
d7ba1388 MD |
888 | ret = metadata_file_append(session, str, len); |
889 | if (ret) { | |
890 | PERROR("Error appending to metadata file"); | |
891 | goto end; | |
892 | } | |
d0b96690 DG |
893 | DBG3("Append to metadata: \"%s\"", str); |
894 | ret = 0; | |
895 | ||
896 | end: | |
897 | free(str); | |
898 | return ret; | |
899 | } | |
900 | ||
7532fa3b | 901 | |
da860cab | 902 | static |
7532fa3b MD |
903 | void lttng_metadata_print_fragment(ust_registry_session &session, |
904 | const json &fragment) | |
da860cab | 905 | { |
7532fa3b | 906 | lttng_metadata_printf(session, "\x1e%s", fragment.dump(2).c_str()); |
da860cab MD |
907 | } |
908 | ||
a1f68b22 | 909 | static |
7532fa3b | 910 | display_base int_to_display_base(int base) |
a1f68b22 | 911 | { |
7532fa3b MD |
912 | /* If we ever get an invalid value, default to 10. */ |
913 | switch (base) { | |
914 | case 2: | |
915 | return display_base::BINARY; | |
916 | case 8: | |
917 | return display_base::OCTAL; | |
918 | default: | |
919 | case 10: | |
920 | return display_base::DECIMAL; | |
921 | case 16: | |
922 | return display_base::HEXADECIMAL; | |
a1f68b22 MD |
923 | } |
924 | } | |
925 | ||
8de88061 | 926 | static |
7532fa3b MD |
927 | json make_ust_integer_field_class(const ust_registry_session &session, |
928 | const lttng_ust_ctl_integer_type &type) | |
929 | { | |
930 | if (type.signedness) { | |
931 | return make_fixed_length_signed_integer_field_class( | |
932 | bits(type.size), get_byte_order(session, type.reverse_byte_order), | |
933 | bits(type.alignment), int_to_display_base(type.base), | |
934 | nullptr); | |
935 | } else { | |
936 | return make_fixed_length_unsigned_integer_field_class( | |
937 | bits(type.size), get_byte_order(session, type.reverse_byte_order), | |
938 | bits(type.alignment), int_to_display_base(type.base), | |
939 | nullptr); | |
8de88061 | 940 | } |
8de88061 JR |
941 | } |
942 | ||
10b56aef MD |
943 | /* Called with session registry mutex held. */ |
944 | static | |
7532fa3b MD |
945 | json make_ust_enum_field_class(const ust_registry_session &session, |
946 | const char *name, uint64_t id, | |
947 | const lttng_ust_ctl_integer_type &container) | |
948 | { | |
10b56aef | 949 | rcu_read_lock(); |
7532fa3b MD |
950 | const ust_registry_enum *reg_enum = ust_registry_lookup_enum_by_id( |
951 | session, name, id); | |
10b56aef | 952 | rcu_read_unlock(); |
7532fa3b | 953 | |
10b56aef MD |
954 | /* reg_enum can still be used because session registry mutex is held. */ |
955 | if (!reg_enum) { | |
7532fa3b | 956 | return -ENOENT; |
10b56aef | 957 | } |
10b56aef | 958 | |
7532fa3b MD |
959 | bool is_signed = container.signedness; |
960 | enumeration_mappings mappings; | |
961 | ||
962 | const lttng_ust_ctl_enum_entry *entries = reg_enum->entries; | |
963 | ||
964 | union { | |
965 | int64_t sign; | |
966 | uint64_t nosign; | |
967 | } next_auto_low_bound; | |
968 | ||
969 | if (is_signed) { | |
970 | next_auto_low_bound.sign = 0; | |
971 | } else { | |
972 | next_auto_low_bound.nosign = 0; | |
10b56aef | 973 | } |
3b016e58 | 974 | |
7532fa3b MD |
975 | for (size_t i = 0; i < reg_enum->nr_entries; ++i) { |
976 | const lttng_ust_ctl_enum_entry &entry = entries[i]; | |
3b016e58 | 977 | |
7532fa3b | 978 | //len = strlen(entry->string); |
e85e3723 | 979 | |
7532fa3b MD |
980 | if (entry.u.extra.options & |
981 | LTTNG_UST_CTL_UST_ENUM_ENTRY_OPTION_IS_AUTO) { | |
982 | if (is_signed) { | |
983 | integer_range_set rs; | |
984 | rs.add_range(next_auto_low_bound.sign, next_auto_low_bound.sign); | |
985 | mappings.add_mapping(entry.string, make_integer_range_set(rs)); | |
986 | ++next_auto_low_bound.sign; | |
e85e3723 | 987 | } else { |
7532fa3b MD |
988 | integer_range_set rs; |
989 | rs.add_range(next_auto_low_bound.nosign, next_auto_low_bound.nosign); | |
990 | mappings.add_mapping(entry.string, make_integer_range_set(rs)); | |
991 | ++next_auto_low_bound.nosign; | |
e85e3723 | 992 | } |
7532fa3b MD |
993 | } else { |
994 | if (is_signed) { | |
995 | integer_range_set rs; | |
996 | // FIXME: not sure if those casts work as intended. | |
997 | rs.add_range((int64_t) entry.start.value, (int64_t) entry.end.value); | |
998 | mappings.add_mapping(entry.string, make_integer_range_set(rs)); | |
999 | } else { | |
1000 | integer_range_set rs; | |
1001 | rs.add_range(entry.start.value, entry.end.value); | |
1002 | mappings.add_mapping(entry.string, make_integer_range_set(rs)); | |
3b016e58 | 1003 | } |
10b56aef MD |
1004 | } |
1005 | } | |
7532fa3b MD |
1006 | |
1007 | return make_fixed_length_enumeration_field_class( | |
1008 | is_signed, bits(container.size), | |
1009 | get_byte_order(session, container.reverse_byte_order), | |
1010 | bits(container.alignment), int_to_display_base(container.base), | |
1011 | std::move(mappings)); | |
da860cab MD |
1012 | } |
1013 | ||
da860cab | 1014 | static |
7532fa3b MD |
1015 | void make_ust_field_class(const ust_registry_session &session, |
1016 | lttng_ust_ctl_field_iterator &field_iterator, | |
1017 | field_path ¤t_field_path, | |
1018 | std::function<void(const char *, json)> add_field, | |
1019 | std::function<const json &(const char *)> get_last_field); | |
da860cab | 1020 | |
7532fa3b MD |
1021 | static |
1022 | void make_ust_variant_field_class( | |
1023 | const ust_registry_session &session, | |
1024 | lttng_ust_ctl_field_iterator &field_iterator, | |
1025 | field_path ¤t_field_path, | |
1026 | const char *field_name, | |
1027 | uint32_t nr_options, | |
1028 | const char *selector_name, | |
1029 | std::function<void(const char *, json)> add_field, | |
1030 | std::function<const json &(const char *)> lookup_field) | |
1031 | { | |
1032 | variant_options options; | |
1033 | const json &selector_field = lookup_field (selector_name); | |
1034 | const json &mappings = selector_field["mappings"]; | |
1035 | ||
1036 | auto this_add_field = [&options, &mappings] (const char *option_name, json option_fc) { | |
1037 | /* UST prefixes the enumerators with and underscore. */ | |
1038 | std::string option_name_prefixed = std::string("_") + option_name; | |
1039 | const json &ranges = mappings[option_name_prefixed]; | |
1040 | /* | |
1041 | * In CTF 1.8, the variant options were prefixed with an | |
1042 | * underscore to match the enumerator names. It is not | |
1043 | * necessary to do it in CTF 2, as variant options don't rely | |
1044 | * on their names matchin an enumerator name. But do it anyway | |
1045 | * to keep the old names, which may help readers migrating from | |
1046 | * 1.8 to 2. | |
1047 | */ | |
1048 | options.add_option(std::move(option_name_prefixed), ranges, option_fc); | |
1049 | }; | |
da860cab | 1050 | |
7532fa3b MD |
1051 | { |
1052 | auto field_location_popper = current_field_path.push(field_name); | |
1053 | ||
1054 | for (uint32_t i = 0; i < nr_options; i++) { | |
1055 | make_ust_field_class(session, field_iterator, | |
1056 | current_field_path, this_add_field, lookup_field); | |
dc6403f3 | 1057 | } |
da860cab | 1058 | } |
7532fa3b MD |
1059 | |
1060 | auto field_location_popper = current_field_path.push(selector_name); | |
1061 | add_field(field_name, | |
1062 | make_variant_field_class(std::move(options), current_field_path)); | |
10b56aef MD |
1063 | } |
1064 | ||
d0b96690 | 1065 | static |
7532fa3b MD |
1066 | void make_ust_field_class(const ust_registry_session &session, |
1067 | lttng_ust_ctl_field_iterator &field_iterator, | |
1068 | field_path ¤t_field_path, | |
1069 | std::function<void(const char *, json)> add_field, | |
1070 | std::function<const json &(const char *)> lookup_field) | |
1071 | { | |
1072 | const lttng_ust_ctl_field &field = field_iterator.get_next(); | |
d0b96690 | 1073 | |
7532fa3b | 1074 | switch (field.type.atype) { |
b623cb6a | 1075 | case lttng_ust_ctl_atype_integer: |
7532fa3b MD |
1076 | add_field(field.name, |
1077 | make_ust_integer_field_class(session, | |
1078 | field.type.u.integer)); | |
d0b96690 | 1079 | break; |
7532fa3b | 1080 | |
b623cb6a | 1081 | case lttng_ust_ctl_atype_enum: |
7532fa3b MD |
1082 | { |
1083 | const auto &enumeration = field.type.u.legacy.basic.enumeration; | |
1084 | add_field(field.name, | |
1085 | make_ust_enum_field_class(session, | |
1086 | enumeration.name, enumeration.id, | |
1087 | enumeration.container_type)); | |
10b56aef | 1088 | break; |
7532fa3b MD |
1089 | } |
1090 | ||
b623cb6a | 1091 | case lttng_ust_ctl_atype_float: |
7532fa3b MD |
1092 | { |
1093 | const lttng_ust_ctl_float_type &t = field.type.u._float; | |
1094 | ||
1095 | add_field(field.name, | |
1096 | make_fixed_length_floating_point_number_field_class( | |
1097 | bits(t.exp_dig + t.mant_dig), | |
1098 | get_byte_order(session, t.reverse_byte_order), bits(t.alignment))); | |
1099 | ||
d0b96690 | 1100 | break; |
7532fa3b MD |
1101 | } |
1102 | ||
b623cb6a | 1103 | case lttng_ust_ctl_atype_array: |
d0b96690 | 1104 | { |
7532fa3b | 1105 | const auto &array_type = field.type.u.legacy.array; |
d0b96690 | 1106 | |
0d32d1a9 | 1107 | /* Only integers are currently supported in arrays. */ |
7532fa3b MD |
1108 | if (array_type.elem_type.atype != lttng_ust_ctl_atype_integer) { |
1109 | throw metadata_generation_exception( | |
1110 | "array element type not supported"); | |
0d32d1a9 | 1111 | } |
7532fa3b MD |
1112 | |
1113 | add_field(field.name, make_static_length_array_field_class( | |
1114 | make_ust_integer_field_class( | |
1115 | session, array_type.elem_type.u.basic.integer), | |
1116 | array_type.length, 0_bits)); | |
1117 | ||
0d32d1a9 MD |
1118 | break; |
1119 | } | |
7532fa3b | 1120 | |
b623cb6a | 1121 | case lttng_ust_ctl_atype_array_nestable: |
0d32d1a9 | 1122 | { |
7532fa3b MD |
1123 | const struct lttng_ust_ctl_type &element_type = |
1124 | field_iterator.get_next().type; | |
0d32d1a9 MD |
1125 | |
1126 | /* Only integers are currently supported in arrays. */ | |
7532fa3b MD |
1127 | if (element_type.atype != lttng_ust_ctl_atype_integer) { |
1128 | throw metadata_generation_exception( | |
1129 | "array element type not supported"); | |
0d32d1a9 MD |
1130 | } |
1131 | ||
7532fa3b | 1132 | const auto &array_type = field.type.u.array_nestable; |
0d32d1a9 | 1133 | |
7532fa3b MD |
1134 | /* The array alignment value we receive is in bytes. */ |
1135 | add_field(field.name, | |
1136 | make_static_length_array_field_class( | |
1137 | make_ust_integer_field_class( | |
1138 | session, element_type.u.integer), | |
1139 | array_type.length, bits(array_type.alignment * 8))); | |
d0b96690 DG |
1140 | break; |
1141 | } | |
7532fa3b | 1142 | |
b623cb6a | 1143 | case lttng_ust_ctl_atype_sequence: |
d0b96690 | 1144 | { |
7532fa3b | 1145 | const auto &t = field.type.u.legacy.sequence; |
0d32d1a9 MD |
1146 | |
1147 | /* Only integers are currently supported in sequences. */ | |
7532fa3b MD |
1148 | if (t.elem_type.atype != lttng_ust_ctl_atype_integer) { |
1149 | throw metadata_generation_exception( | |
1150 | "sequence element type not supported"); | |
0d32d1a9 MD |
1151 | } |
1152 | ||
7532fa3b MD |
1153 | const lttng_ust_ctl_integer_type &length_type |
1154 | = t.length_type.u.basic.integer; | |
d0b96690 | 1155 | |
7532fa3b MD |
1156 | std::stringstream sstream; |
1157 | sstream << "__" << field.name << "_length"; | |
1158 | add_field(sstream.str().c_str(), | |
1159 | make_ust_integer_field_class(session, length_type)); | |
1160 | ||
1161 | auto popper = current_field_path.push(sstream.str().c_str()); | |
1162 | add_field(field.name, | |
1163 | make_dynamic_length_array_field_class( | |
1164 | make_ust_integer_field_class( | |
1165 | session, t.elem_type.u.basic.integer), | |
1166 | current_field_path, 0_bits)); | |
d0b96690 DG |
1167 | break; |
1168 | } | |
b623cb6a | 1169 | case lttng_ust_ctl_atype_sequence_nestable: |
0d32d1a9 | 1170 | { |
7532fa3b MD |
1171 | const struct lttng_ust_ctl_type &element_type = |
1172 | field_iterator.get_next().type; | |
0d32d1a9 MD |
1173 | |
1174 | /* Only integers are currently supported in sequences. */ | |
7532fa3b MD |
1175 | if (element_type.atype != lttng_ust_ctl_atype_integer) { |
1176 | throw metadata_generation_exception( | |
1177 | "sequence element type not supported"); | |
0d32d1a9 MD |
1178 | } |
1179 | ||
7532fa3b MD |
1180 | const auto &array_type = field.type.u.sequence_nestable; |
1181 | auto popper = current_field_path.push( | |
1182 | array_type.length_name); | |
1183 | ||
1184 | /* The array alignment value we receive is in bytes. */ | |
1185 | add_field(field.name, | |
1186 | make_dynamic_length_array_field_class( | |
1187 | make_ust_integer_field_class( | |
1188 | session, element_type.u.integer), | |
1189 | current_field_path, | |
1190 | bits(array_type.alignment * 8))); | |
0d32d1a9 MD |
1191 | break; |
1192 | } | |
7532fa3b | 1193 | |
b623cb6a | 1194 | case lttng_ust_ctl_atype_string: |
7532fa3b | 1195 | add_field(field.name, make_string_field_class()); |
da860cab | 1196 | break; |
7532fa3b | 1197 | |
b623cb6a | 1198 | case lttng_ust_ctl_atype_variant: |
7532fa3b MD |
1199 | { |
1200 | const decltype(field.type.u.legacy.variant) &variant_type = | |
1201 | field.type.u.legacy.variant; | |
1202 | ||
1203 | make_ust_variant_field_class(session, field_iterator, | |
1204 | current_field_path, field.name, variant_type.nr_choices, | |
1205 | variant_type.tag_name, add_field, lookup_field); | |
0d32d1a9 | 1206 | break; |
7532fa3b MD |
1207 | } |
1208 | ||
b623cb6a | 1209 | case lttng_ust_ctl_atype_variant_nestable: |
7532fa3b MD |
1210 | { |
1211 | const decltype(field.type.u.variant_nestable) &variant_type = | |
1212 | field.type.u.variant_nestable; | |
1213 | ||
1214 | make_ust_variant_field_class(session, field_iterator, | |
1215 | current_field_path, field.name, variant_type.nr_choices, | |
1216 | variant_type.tag_name, add_field, lookup_field); | |
da860cab | 1217 | break; |
7532fa3b MD |
1218 | } |
1219 | ||
b623cb6a | 1220 | case lttng_ust_ctl_atype_struct: |
7532fa3b | 1221 | if (field.type.u.legacy._struct.nr_fields != 0) { |
0d32d1a9 | 1222 | /* Currently only 0-length structures are supported. */ |
7532fa3b | 1223 | throw metadata_generation_exception("Only 0-length structures are supported"); |
0d32d1a9 | 1224 | } |
7532fa3b MD |
1225 | |
1226 | /* Don't emit anything */ | |
d0b96690 | 1227 | break; |
7532fa3b | 1228 | |
b623cb6a | 1229 | case lttng_ust_ctl_atype_struct_nestable: |
7532fa3b | 1230 | if (field.type.u.struct_nestable.nr_fields != 0) { |
0d32d1a9 | 1231 | /* Currently only 0-length structures are supported. */ |
7532fa3b | 1232 | throw metadata_generation_exception("Only 0-length structures are supported"); |
0d32d1a9 | 1233 | } |
7532fa3b MD |
1234 | |
1235 | /* Don't emit anything */ | |
0d32d1a9 | 1236 | break; |
7532fa3b | 1237 | |
b623cb6a | 1238 | case lttng_ust_ctl_atype_enum_nestable: |
0d32d1a9 | 1239 | { |
7532fa3b MD |
1240 | const lttng_ust_ctl_field &container = |
1241 | field_iterator.get_next(); | |
0d32d1a9 MD |
1242 | |
1243 | /* Only integers are supported as container types. */ | |
7532fa3b MD |
1244 | if (container.type.atype != lttng_ust_ctl_atype_integer) { |
1245 | throw metadata_generation_exception( | |
1246 | "Enumeration container type not an integer"); | |
0d32d1a9 | 1247 | } |
7532fa3b MD |
1248 | |
1249 | const auto &enumeration = field.type.u.enum_nestable; | |
1250 | add_field(field.name, | |
1251 | make_ust_enum_field_class(session, | |
1252 | enumeration.name, enumeration.id, | |
1253 | container.type.u.integer)); | |
0d32d1a9 MD |
1254 | break; |
1255 | } | |
d0b96690 | 1256 | default: |
7532fa3b MD |
1257 | throw metadata_generation_exception( |
1258 | "Unhandled lttng-ust field type"); | |
d0b96690 | 1259 | } |
d0b96690 DG |
1260 | } |
1261 | ||
1262 | static | |
7532fa3b MD |
1263 | json make_record_common_context_field_class( |
1264 | const ust_registry_session &session, | |
1265 | const ust_registry_channel &chan) | |
d0b96690 | 1266 | { |
7532fa3b MD |
1267 | lttng_ust_ctl_field_iterator iter(chan.ctx_fields, chan.nr_ctx_fields); |
1268 | field_path fp("event-record-common-context"); | |
1269 | structure_members members; | |
d0b96690 | 1270 | |
7532fa3b MD |
1271 | auto add_field = [&members] (const char *name, json field_class) { |
1272 | members.add_member(name, field_class); | |
1273 | }; | |
1274 | ||
1275 | auto lookup_field = [&members] (const char *name) -> const json & { | |
1276 | for (const structure_member &member : members) { | |
1277 | if (strcmp(member.name, name) == 0) { | |
1278 | return member.field_class; | |
1279 | } | |
da860cab | 1280 | } |
7532fa3b MD |
1281 | |
1282 | throw metadata_generation_exception("failed to look up field"); | |
1283 | }; | |
1284 | ||
1285 | while (!iter.done()) { | |
1286 | make_ust_field_class(session, iter, fp, add_field, | |
1287 | lookup_field); | |
d0b96690 | 1288 | } |
7532fa3b MD |
1289 | |
1290 | return make_structure_field_class(0_bits, std::move(members)); | |
d0b96690 DG |
1291 | } |
1292 | ||
1293 | static | |
7532fa3b MD |
1294 | json make_event_record_class_payload_field_class( |
1295 | const ust_registry_session &session, | |
1296 | const ust_registry_event &event) | |
d0b96690 | 1297 | { |
7532fa3b MD |
1298 | lttng_ust_ctl_field_iterator iter(event.fields, event.nr_fields); |
1299 | field_path fp("event-record-payload"); | |
1300 | structure_members members; | |
d0b96690 | 1301 | |
7532fa3b MD |
1302 | auto add_field = [&members] (const char *name, json field_class) { |
1303 | members.add_member(name, field_class); | |
1304 | }; | |
1305 | ||
1306 | auto lookup_field = [&members] (const char *name) { | |
1307 | for (const structure_member &member : members) { | |
1308 | if (strcmp(member.name, name) == 0) { | |
1309 | return member.field_class; | |
1310 | } | |
da860cab | 1311 | } |
7532fa3b MD |
1312 | |
1313 | throw metadata_generation_exception("failed to look up field"); | |
1314 | }; | |
1315 | ||
1316 | while (!iter.done()) { | |
1317 | make_ust_field_class(session, iter, fp, add_field, | |
1318 | lookup_field); | |
d0b96690 | 1319 | } |
7532fa3b MD |
1320 | |
1321 | return make_structure_field_class(0_bits, std::move(members)); | |
1322 | } | |
1323 | ||
1324 | static | |
1325 | json make_user_attributes(json attributes) | |
1326 | { | |
1327 | return { | |
1328 | { "lttng.org,2009", attributes } | |
1329 | }; | |
1330 | } | |
1331 | ||
1332 | static | |
1333 | json make_event_record_class_user_attributes(const ust_registry_event &event) | |
1334 | { | |
1335 | json user_attributes = make_user_attributes({ | |
1336 | { "log-level", event.loglevel_value }, | |
1337 | }); | |
1338 | ||
1339 | if (event.model_emf_uri) { | |
1340 | user_attributes["emf-uri"] = event.model_emf_uri; | |
1341 | } | |
1342 | ||
1343 | return user_attributes; | |
d0b96690 DG |
1344 | } |
1345 | ||
1346 | /* | |
1347 | * Should be called with session registry mutex held. | |
1348 | */ | |
1349 | int ust_metadata_event_statedump(struct ust_registry_session *session, | |
1350 | struct ust_registry_channel *chan, | |
1351 | struct ust_registry_event *event) | |
1352 | { | |
d0b96690 DG |
1353 | /* Don't dump metadata events */ |
1354 | if (chan->chan_id == -1U) | |
1355 | return 0; | |
1356 | ||
7532fa3b MD |
1357 | /* |
1358 | * We don't want to output an event's metadata before its parent | |
1359 | * stream's metadata. If the stream's metadata hasn't been output yet, | |
1360 | * skip this event. Its metadata will be output when we output the | |
1361 | * stream's metadata. | |
1362 | */ | |
1363 | if (!chan->metadata_dumped) { | |
1364 | return 0; | |
0d32d1a9 | 1365 | } |
d0b96690 | 1366 | |
7532fa3b MD |
1367 | LTTNG_ASSERT(!event->metadata_dumped); |
1368 | ||
1369 | try { | |
1370 | json event_record_class = make_fragment("event-record-class"); | |
1371 | event_record_class.update({ | |
1372 | { "name", event->name }, | |
1373 | { "id", event->id }, | |
1374 | { "data-stream-class-id", chan->chan_id }, | |
1375 | { "user-attributes", make_event_record_class_user_attributes(*event) }, | |
1376 | }); | |
d0b96690 | 1377 | |
7532fa3b MD |
1378 | if (event->nr_fields) { |
1379 | event_record_class["payload-field-class"] = | |
1380 | make_event_record_class_payload_field_class(*session, *event); | |
0d32d1a9 | 1381 | } |
d0b96690 | 1382 | |
7532fa3b | 1383 | lttng_metadata_print_fragment(*session, event_record_class); |
d0b96690 | 1384 | |
7532fa3b | 1385 | event->metadata_dumped = 1; |
d0b96690 | 1386 | |
7532fa3b MD |
1387 | return 0; |
1388 | } catch (const std::exception &error) { | |
1389 | ERR("%s", error.what()); | |
1390 | return -LTTNG_ERR_UNK; | |
1391 | } catch (...) { | |
1392 | return -LTTNG_ERR_UNK; | |
0d32d1a9 | 1393 | } |
7532fa3b | 1394 | } |
d0b96690 | 1395 | |
7532fa3b MD |
1396 | static |
1397 | json make_packet_context_field_class(const ust_registry_session &session) | |
1398 | { | |
1399 | byte_order bo = get_byte_order(session, false); | |
1400 | structure_members members; | |
1401 | ||
1402 | members.add_member("timestamp_begin", | |
1403 | make_fixed_length_unsigned_integer_field_class( | |
1404 | 64_bits, bo, 8_bits, display_base::DECIMAL, | |
1405 | "packet-beginning-default-clock-timestamp")); | |
1406 | members.add_member("timestamp_end", | |
1407 | make_fixed_length_unsigned_integer_field_class( | |
1408 | 64_bits, bo, 8_bits, display_base::DECIMAL, | |
1409 | "packet-end-default-clock-timestamp")); | |
1410 | members.add_member("content_size", | |
1411 | make_fixed_length_unsigned_integer_field_class( | |
1412 | 64_bits, bo, 8_bits, display_base::DECIMAL, | |
1413 | "packet-content-size")); | |
1414 | members.add_member("packet_size", | |
1415 | make_fixed_length_unsigned_integer_field_class( | |
1416 | 64_bits, bo, 8_bits, display_base::DECIMAL, | |
1417 | "packet-total-size")); | |
1418 | members.add_member("packet_seq_num", | |
1419 | make_fixed_length_unsigned_integer_field_class( | |
1420 | 64_bits, bo, 8_bits, display_base::DECIMAL, | |
1421 | "packet-sequence-number")); | |
1422 | members.add_member("events_discarded", | |
1423 | make_fixed_length_unsigned_integer_field_class( | |
1424 | bits(session.bits_per_long), bo, | |
1425 | bits(session.long_alignment), | |
1426 | display_base::DECIMAL, | |
1427 | "discarded-event-record-counter-snapshot")); | |
1428 | members.add_member("cpu_id", | |
1429 | make_fixed_length_unsigned_integer_field_class( | |
1430 | 32_bits, bo, 8_bits, display_base::DECIMAL, nullptr)); | |
1431 | ||
1432 | return make_structure_field_class(0_bits, std::move(members)); | |
1433 | } | |
1434 | ||
1435 | static | |
1436 | json make_record_header_compact_field_class(const ust_registry_session &session) | |
1437 | { | |
1438 | byte_order bo = get_byte_order(session, false); | |
1439 | ||
1440 | structure_members compact_members; | |
1441 | compact_members.add_member("timestamp", | |
1442 | make_fixed_length_unsigned_integer_field_class( | |
1443 | 27_bits, bo, 1_bits, display_base::DECIMAL, | |
1444 | "default-clock-timestamp")); | |
1445 | ||
1446 | structure_members extended_members; | |
1447 | extended_members.add_member("id", | |
1448 | make_fixed_length_unsigned_integer_field_class( | |
1449 | 32_bits, bo, 8_bits, display_base::DECIMAL, | |
1450 | "event-record-class-id")); | |
1451 | extended_members.add_member("timestamp", | |
1452 | make_fixed_length_unsigned_integer_field_class( | |
1453 | 64_bits, bo, 8_bits, display_base::DECIMAL, | |
1454 | "default-clock-timestamp")); | |
1455 | ||
1456 | integer_range_set compact_ranges, extended_ranges; | |
1457 | compact_ranges.add_range(UINT64_C(0), 30); | |
1458 | extended_ranges.add_range(UINT64_C(31), 31); | |
1459 | ||
1460 | variant_options options; | |
1461 | options.add_option("compact", | |
1462 | make_integer_range_set(compact_ranges), | |
1463 | make_structure_field_class(0_bits, std::move(compact_members))); | |
1464 | options.add_option("extended", | |
1465 | make_integer_range_set(extended_ranges), | |
1466 | make_structure_field_class(0_bits, std::move(extended_members))); | |
1467 | ||
1468 | enumeration_mappings id_mappings; | |
1469 | id_mappings.add_mapping("compact", | |
1470 | make_integer_range_set(compact_ranges)); | |
1471 | id_mappings.add_mapping("extended", | |
1472 | make_integer_range_set(extended_ranges)); | |
1473 | ||
1474 | structure_members members; | |
1475 | members.add_member("id", | |
1476 | make_fixed_length_unsigned_enumeration_field_class( | |
1477 | 5_bits, bo, 8_bits, display_base::DECIMAL, | |
1478 | "event-record-class-id", std::move(id_mappings))); | |
1479 | members.add_member("v", | |
1480 | make_variant_field_class(std::move(options), | |
1481 | {"event-record-header", "id"})); | |
1482 | ||
1483 | return make_structure_field_class(8_bits, std::move(members)); | |
1484 | } | |
1485 | ||
1486 | static | |
1487 | json make_record_header_large_field_class(const ust_registry_session &session) | |
1488 | { | |
1489 | byte_order bo = get_byte_order(session, false); | |
1490 | ||
1491 | structure_members compact_members; | |
1492 | compact_members.add_member("timestamp", | |
1493 | make_fixed_length_unsigned_integer_field_class( | |
1494 | 32_bits, bo, 8_bits, display_base::DECIMAL, | |
1495 | "default-clock-timestamp")); | |
1496 | ||
1497 | structure_members extended_members; | |
1498 | extended_members.add_member("id", | |
1499 | make_fixed_length_unsigned_integer_field_class( | |
1500 | 32_bits, bo, 8_bits, display_base::DECIMAL, | |
1501 | "event-record-class-id")); | |
1502 | extended_members.add_member("timestamp", | |
1503 | make_fixed_length_unsigned_integer_field_class( | |
1504 | 64_bits, bo, 8_bits, display_base::DECIMAL, | |
1505 | "default-clock-timestamp")); | |
1506 | ||
1507 | integer_range_set compact_ranges, extended_ranges; | |
1508 | compact_ranges.add_range(UINT64_C(0), 65534); | |
1509 | extended_ranges.add_range(UINT64_C(65535), 65535); | |
1510 | ||
1511 | variant_options options; | |
1512 | options.add_option("compact", | |
1513 | make_integer_range_set(compact_ranges), | |
1514 | make_structure_field_class(0_bits, std::move(compact_members))); | |
1515 | options.add_option("extended", | |
1516 | make_integer_range_set(extended_ranges), | |
1517 | make_structure_field_class(0_bits, std::move(extended_members))); | |
1518 | ||
1519 | enumeration_mappings id_mappings; | |
1520 | id_mappings.add_mapping("compact", | |
1521 | make_integer_range_set(compact_ranges)); | |
1522 | id_mappings.add_mapping("extended", | |
1523 | make_integer_range_set(extended_ranges)); | |
1524 | ||
1525 | structure_members members; | |
1526 | members.add_member("id", | |
1527 | make_fixed_length_unsigned_enumeration_field_class( | |
1528 | 16_bits, bo, 8_bits, display_base::DECIMAL, | |
1529 | "event-record-class-id", std::move(id_mappings))); | |
1530 | members.add_member("v", | |
1531 | make_variant_field_class(std::move(options), | |
1532 | { "event-record-header", "id" })); | |
1533 | ||
1534 | return make_structure_field_class(8_bits, std::move(members)); | |
1535 | } | |
1536 | ||
1537 | static | |
1538 | json make_record_header_field_class(const ust_registry_session &session, | |
1539 | const ust_registry_channel &chan) | |
1540 | { | |
1541 | if (chan.header_type == LTTNG_UST_CTL_CHANNEL_HEADER_COMPACT) { | |
1542 | return make_record_header_compact_field_class(session); | |
1543 | } else { | |
1544 | return make_record_header_large_field_class(session); | |
1545 | } | |
d0b96690 DG |
1546 | } |
1547 | ||
1548 | /* | |
1549 | * Should be called with session registry mutex held. | |
1550 | */ | |
1551 | int ust_metadata_channel_statedump(struct ust_registry_session *session, | |
1552 | struct ust_registry_channel *chan) | |
1553 | { | |
d0b96690 DG |
1554 | /* Don't dump metadata events */ |
1555 | if (chan->chan_id == -1U) | |
1556 | return 0; | |
1557 | ||
1558 | if (!chan->header_type) | |
1559 | return -EINVAL; | |
1560 | ||
7532fa3b MD |
1561 | try { |
1562 | json data_stream_class = make_fragment("data-stream-class"); | |
1563 | data_stream_class.update({ | |
1564 | { "id", chan->chan_id }, | |
1565 | { "packet-context-field-class", | |
1566 | make_packet_context_field_class(*session) }, | |
1567 | { "event-record-header-field-class", | |
1568 | make_record_header_field_class(*session, *chan) }, | |
1569 | }); | |
1570 | ||
1571 | if (chan->ctx_fields) { | |
1572 | data_stream_class.update({ | |
1573 | { "event-record-common-context-field-class", | |
1574 | make_record_common_context_field_class(*session, *chan) } | |
1575 | }); | |
0d32d1a9 | 1576 | } |
d0b96690 | 1577 | |
7532fa3b | 1578 | lttng_metadata_print_fragment(*session, data_stream_class); |
d0b96690 | 1579 | |
7532fa3b MD |
1580 | /* Flag success of metadata dump. */ |
1581 | chan->metadata_dumped = 1; | |
d0b96690 | 1582 | |
7532fa3b MD |
1583 | /* |
1584 | * Output the metadata of any existing event. | |
1585 | * | |
1586 | * Sort the events by id. This is not necessary, but it's nice to have | |
1587 | * a more predictable order in the metadata file. | |
1588 | */ | |
1589 | std::vector<ust_registry_event *> events; | |
1590 | { | |
1591 | cds_lfht_iter event_iter; | |
1592 | ust_registry_event *event; | |
1593 | cds_lfht_for_each_entry(chan->events->ht, &event_iter, event, | |
1594 | node.node) { | |
1595 | events.push_back(event); | |
1596 | } | |
1597 | } | |
d0b96690 | 1598 | |
7532fa3b MD |
1599 | std::sort(events.begin(), events.end(), |
1600 | [] (ust_registry_event *a, ust_registry_event *b) { | |
1601 | return a->id < b->id; | |
1602 | }); | |
1603 | ||
1604 | for (ust_registry_event *event : events) { | |
1605 | ust_metadata_event_statedump(session, chan, event); | |
1606 | } | |
1607 | ||
1608 | return 0; | |
1609 | } catch (const std::exception &error) { | |
1610 | ERR("%s", error.what()); | |
1611 | return -LTTNG_ERR_UNK; | |
1612 | } catch (...) { | |
1613 | return -LTTNG_ERR_UNK; | |
1614 | } | |
d0b96690 DG |
1615 | } |
1616 | ||
dc113ec7 MD |
1617 | /* |
1618 | * The offset between monotonic and realtime clock can be negative if | |
1619 | * the system sets the REALTIME clock to 0 after boot. | |
dc113ec7 | 1620 | */ |
d0b96690 | 1621 | static |
8c645bb0 | 1622 | int measure_single_clock_offset(struct offset_sample *sample) |
d0b96690 | 1623 | { |
dc113ec7 | 1624 | uint64_t monotonic_avg, monotonic[2], measure_delta, realtime; |
fc0bb9fa | 1625 | uint64_t tcf = trace_clock_freq(); |
d0b96690 DG |
1626 | struct timespec rts = { 0, 0 }; |
1627 | int ret; | |
1628 | ||
1629 | monotonic[0] = trace_clock_read64(); | |
389fbf04 | 1630 | ret = lttng_clock_gettime(CLOCK_REALTIME, &rts); |
8c645bb0 MD |
1631 | if (ret < 0) { |
1632 | return ret; | |
1633 | } | |
d0b96690 | 1634 | monotonic[1] = trace_clock_read64(); |
8c645bb0 MD |
1635 | measure_delta = monotonic[1] - monotonic[0]; |
1636 | if (measure_delta > sample->measure_delta) { | |
1637 | /* | |
1638 | * Discard value if it took longer to read than the best | |
1639 | * sample so far. | |
1640 | */ | |
1641 | return 0; | |
1642 | } | |
dc113ec7 | 1643 | monotonic_avg = (monotonic[0] + monotonic[1]) >> 1; |
6e1b0543 MD |
1644 | realtime = (uint64_t) rts.tv_sec * tcf; |
1645 | if (tcf == NSEC_PER_SEC) { | |
1646 | realtime += rts.tv_nsec; | |
1647 | } else { | |
1648 | realtime += (uint64_t) rts.tv_nsec * tcf / NSEC_PER_SEC; | |
fc0bb9fa | 1649 | } |
c2636b57 | 1650 | sample->offset = (int64_t) realtime - monotonic_avg; |
8c645bb0 MD |
1651 | sample->measure_delta = measure_delta; |
1652 | return 0; | |
d0b96690 DG |
1653 | } |
1654 | ||
8c645bb0 MD |
1655 | /* |
1656 | * Approximation of NTP time of day to clock monotonic correlation, | |
1657 | * taken at start of trace. Keep the measurement that took the less time | |
1658 | * to complete, thus removing imprecision caused by preemption. | |
c2636b57 | 1659 | * May return a negative offset. |
8c645bb0 MD |
1660 | */ |
1661 | static | |
c2636b57 | 1662 | int64_t measure_clock_offset(void) |
8c645bb0 MD |
1663 | { |
1664 | int i; | |
1665 | struct offset_sample offset_best_sample = { | |
1666 | .offset = 0, | |
1667 | .measure_delta = UINT64_MAX, | |
1668 | }; | |
1669 | ||
1670 | for (i = 0; i < NR_CLOCK_OFFSET_SAMPLES; i++) { | |
1671 | if (measure_single_clock_offset(&offset_best_sample)) { | |
1672 | return 0; | |
1673 | } | |
1674 | } | |
1675 | return offset_best_sample.offset; | |
1676 | } | |
d0b96690 DG |
1677 | |
1678 | /* | |
1679 | * Should be called with session registry mutex held. | |
1680 | */ | |
1681 | int ust_metadata_session_statedump(struct ust_registry_session *session, | |
af6142cf MD |
1682 | struct ust_app *app, |
1683 | uint32_t major, | |
1684 | uint32_t minor) | |
d0b96690 | 1685 | { |
a0377dfe | 1686 | LTTNG_ASSERT(session); |
7972aab2 | 1687 | |
7532fa3b MD |
1688 | try { |
1689 | json preamble = make_fragment("preamble"); | |
1690 | preamble.update({ | |
1691 | { "version", 2 }, | |
1692 | }); | |
1693 | lttng_metadata_print_fragment(*session, preamble); | |
1694 | ||
1695 | json trace_class = make_fragment("trace-class"); | |
1696 | trace_class.update({ | |
1697 | { "packet-header-field-class", | |
1698 | make_packet_header_field_class(*session) }, | |
1699 | { "uuid", make_uuid (session->uuid) } | |
1700 | }); | |
1701 | lttng_metadata_print_fragment(*session, trace_class); | |
1702 | ||
1703 | uint64_t clock_freq = trace_clock_freq(); | |
1704 | json clock_class = make_fragment("clock-class"); | |
1705 | clock_class.update({ | |
1706 | { "name", trace_clock_name() }, | |
1707 | { "description", trace_clock_description() }, | |
1708 | { "frequency", clock_freq }, | |
1709 | { "offset", make_clock_offset(measure_clock_offset(), clock_freq) }, | |
1710 | }); | |
1711 | ||
1712 | char clock_uuid_str[LTTNG_UUID_STR_LEN]; | |
1713 | int ret = trace_clock_uuid(clock_uuid_str); | |
1714 | if (ret == 0) { | |
1715 | uint8_t clock_uuid[LTTNG_UUID_LEN]; | |
1716 | ret = lttng_uuid_from_str(clock_uuid_str, clock_uuid); | |
1717 | if (ret == 0) { | |
1718 | clock_class["uuid"] = make_uuid(clock_uuid); | |
1719 | } else { | |
1720 | // FIXME: warn? | |
1721 | } | |
1722 | } else { | |
1723 | // FIXME: warn? | |
0d32d1a9 | 1724 | } |
d0b96690 | 1725 | |
7532fa3b | 1726 | lttng_metadata_print_fragment(*session, clock_class); |
d0b96690 | 1727 | |
7532fa3b MD |
1728 | return 0; |
1729 | } catch (const std::exception &error) { | |
1730 | ERR("%s", error.what()); | |
1731 | return -LTTNG_ERR_UNK; | |
1732 | } catch (...) { | |
1733 | return -LTTNG_ERR_UNK; | |
0d32d1a9 | 1734 | } |
d0b96690 | 1735 | } |