Commit | Line | Data |
---|---|---|
da9dd521 JG |
1 | /* |
2 | * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
3 | * Copyright (C) 2022 Simon Marchi <simon.marchi@efficios.com> | |
4 | * | |
5 | * SPDX-License-Identifier: GPL-2.0-only | |
6 | * | |
7 | */ | |
8 | ||
da9dd521 | 9 | #include "clock-class.hpp" |
28ab034a | 10 | #include "ctf2-trace-class-visitor.hpp" |
da9dd521 JG |
11 | |
12 | #include <common/exception.hpp> | |
13 | #include <common/format.hpp> | |
14 | ||
15 | #include <vendor/nlohmann/json.hpp> | |
16 | ||
17 | #include <algorithm> | |
cd9adb8b | 18 | #include <utility> |
da9dd521 JG |
19 | |
20 | namespace lsc = lttng::sessiond::ctf2; | |
21 | namespace lst = lttng::sessiond::trace; | |
22 | ||
23 | namespace json = nlohmann; | |
24 | ||
25 | namespace { | |
26 | const unsigned int spaces_per_indent = 2; | |
27 | const std::string record_separator = "\x1e"; | |
28 | ||
29 | json::json make_json_fragment(const char *type) | |
30 | { | |
28ab034a | 31 | return { { "type", type } }; |
da9dd521 JG |
32 | } |
33 | ||
28ab034a | 34 | json::json to_json(const lst::field_location& location) |
da9dd521 JG |
35 | { |
36 | json::json location_array; | |
37 | ||
38 | switch (location.root_) { | |
39 | case lst::field_location::root::PACKET_HEADER: | |
40 | location_array.push_back("packet-header"); | |
41 | break; | |
42 | case lst::field_location::root::PACKET_CONTEXT: | |
43 | location_array.push_back("packet-context"); | |
44 | break; | |
45 | case lst::field_location::root::EVENT_RECORD_HEADER: | |
46 | location_array.push_back("event-record-header"); | |
47 | break; | |
48 | case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT: | |
49 | location_array.push_back("event-record-common-context"); | |
50 | break; | |
51 | case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT: | |
52 | location_array.push_back("event-record-specific-context"); | |
53 | break; | |
54 | case lst::field_location::root::EVENT_RECORD_PAYLOAD: | |
55 | location_array.push_back("event-record-payload"); | |
56 | break; | |
57 | } | |
58 | ||
28ab034a JG |
59 | std::copy(location.elements_.begin(), |
60 | location.elements_.end(), | |
61 | std::back_inserter(location_array)); | |
da9dd521 JG |
62 | return location_array; |
63 | } | |
64 | ||
65 | const char *get_role_name(lst::integer_type::role role) | |
66 | { | |
67 | switch (role) { | |
68 | case lst::integer_type::role::DEFAULT_CLOCK_TIMESTAMP: | |
69 | return "default-clock-timestamp"; | |
70 | case lst::integer_type::role::DATA_STREAM_CLASS_ID: | |
71 | return "data-stream-class-id"; | |
72 | case lst::integer_type::role::DATA_STREAM_ID: | |
73 | return "data-stream-id"; | |
74 | case lst::integer_type::role::PACKET_MAGIC_NUMBER: | |
75 | return "packet-magic-number"; | |
76 | case lst::integer_type::role::DISCARDED_EVENT_RECORD_COUNTER_SNAPSHOT: | |
77 | return "discarded-event-record-counter-snapshot"; | |
78 | case lst::integer_type::role::PACKET_CONTENT_LENGTH: | |
79 | return "packet-content-length"; | |
80 | case lst::integer_type::role::PACKET_END_DEFAULT_CLOCK_TIMESTAMP: | |
81 | return "packet-end-default-clock-timestamp"; | |
82 | case lst::integer_type::role::PACKET_SEQUENCE_NUMBER: | |
83 | return "packet-sequence-number"; | |
84 | case lst::integer_type::role::PACKET_TOTAL_LENGTH: | |
85 | return "packet-total-length"; | |
86 | case lst::integer_type::role::EVENT_RECORD_CLASS_ID: | |
87 | return "event-record-class-id"; | |
88 | default: | |
89 | abort(); | |
90 | } | |
91 | } | |
92 | ||
93 | const char *get_role_name(lst::static_length_blob_type::role role) | |
94 | { | |
95 | switch (role) { | |
96 | case lst::static_length_blob_type::role::METADATA_STREAM_UUID: | |
97 | return "metadata-stream-uuid"; | |
98 | default: | |
99 | abort(); | |
100 | } | |
101 | } | |
102 | ||
103 | namespace ctf2 { | |
104 | class trace_environment_visitor : public lst::trace_class_environment_visitor { | |
105 | public: | |
5c7248cd JG |
106 | trace_environment_visitor() = default; /* NOLINT clang-tidy 14 identifies this as a move |
107 | constructor. */ | |
da9dd521 | 108 | |
cd9adb8b | 109 | void visit(const lst::environment_field<int64_t>& field) override |
da9dd521 JG |
110 | { |
111 | _visit(field); | |
112 | } | |
113 | ||
cd9adb8b | 114 | void visit(const lst::environment_field<const char *>& field) override |
da9dd521 JG |
115 | { |
116 | _visit(field); | |
117 | } | |
118 | ||
119 | /* Only call once. */ | |
c22ded12 | 120 | json::json move_fragment() |
da9dd521 JG |
121 | { |
122 | return std::move(_environment); | |
123 | } | |
124 | ||
125 | private: | |
126 | template <class FieldType> | |
127 | void _visit(const FieldType& field) | |
128 | { | |
129 | _environment[field.name] = field.value; | |
130 | } | |
131 | ||
132 | json::json _environment; | |
133 | }; | |
134 | ||
135 | class field_visitor : public lttng::sessiond::trace::field_visitor, | |
136 | public lttng::sessiond::trace::type_visitor { | |
137 | public: | |
5c7248cd | 138 | field_visitor() = default; /* NOLINT clang-tidy 14 identifies this as a move constructor. */ |
da9dd521 JG |
139 | |
140 | /* Only call once. */ | |
c22ded12 | 141 | json::json move_fragment() |
da9dd521 JG |
142 | { |
143 | return std::move(_fragment); | |
144 | } | |
145 | ||
146 | private: | |
cd9adb8b | 147 | void visit(const lst::field& field) final |
da9dd521 JG |
148 | { |
149 | field_visitor field_type_visitor; | |
150 | field.get_type().accept(field_type_visitor); | |
151 | ||
152 | _fragment["name"] = field.name; | |
c22ded12 | 153 | _fragment["field-class"] = field_type_visitor.move_fragment(); |
da9dd521 JG |
154 | } |
155 | ||
cd9adb8b | 156 | void visit(const lst::integer_type& type) final |
da9dd521 JG |
157 | { |
158 | _fragment["type"] = type.signedness_ == lst::integer_type::signedness::SIGNED ? | |
28ab034a JG |
159 | "fixed-length-signed-integer" : |
160 | "fixed-length-unsigned-integer"; | |
da9dd521 JG |
161 | _fragment["length"] = type.size; |
162 | _fragment["byte-order"] = type.byte_order == lst::byte_order::BIG_ENDIAN_ ? | |
28ab034a JG |
163 | "big-endian" : |
164 | "little-endian"; | |
da9dd521 JG |
165 | _fragment["alignment"] = type.alignment; |
166 | _fragment["preferred-display-base"] = (unsigned int) type.base_; | |
167 | ||
168 | if (type.roles_.size() > 0) { | |
169 | json::json role_array = json::json::array(); | |
170 | ||
171 | for (const auto role : type.roles_) { | |
172 | role_array.push_back(get_role_name(role)); | |
173 | } | |
174 | ||
175 | _fragment["roles"] = std::move(role_array); | |
176 | } | |
177 | } | |
178 | ||
cd9adb8b | 179 | void visit(const lst::floating_point_type& type) final |
da9dd521 JG |
180 | { |
181 | _fragment["type"] = "fixed-length-floating-point-number"; | |
182 | _fragment["length"] = type.exponent_digits + type.mantissa_digits; | |
183 | _fragment["byte-order"] = type.byte_order == lst::byte_order::BIG_ENDIAN_ ? | |
28ab034a JG |
184 | "big-endian" : |
185 | "little-endian"; | |
da9dd521 JG |
186 | _fragment["alignment"] = type.alignment; |
187 | } | |
188 | ||
189 | template <class EnumerationType> | |
190 | void visit_enumeration(const EnumerationType& type) | |
191 | { | |
28ab034a JG |
192 | _fragment["type"] = |
193 | std::is_signed< | |
194 | typename EnumerationType::mapping::range_t::range_integer_t>::value ? | |
195 | "fixed-length-signed-enumeration" : | |
196 | "fixed-length-unsigned-enumeration"; | |
da9dd521 JG |
197 | _fragment["length"] = type.size; |
198 | _fragment["byte-order"] = type.byte_order == lst::byte_order::BIG_ENDIAN_ ? | |
28ab034a JG |
199 | "big-endian" : |
200 | "little-endian"; | |
da9dd521 JG |
201 | _fragment["alignment"] = type.alignment; |
202 | _fragment["preferred-display-base"] = (unsigned int) type.base_; | |
203 | ||
204 | if (type.roles_.size() > 0) { | |
205 | if (std::is_signed<typename EnumerationType::mapping::range_t:: | |
28ab034a JG |
206 | range_integer_t>::value) { |
207 | LTTNG_THROW_ERROR( | |
f9a41357 JG |
208 | lttng::format("Failed to serialize {}: unexpected role", |
209 | _fragment["type"])); | |
da9dd521 JG |
210 | } |
211 | ||
212 | auto role_array = json::json::array(); | |
213 | ||
214 | for (const auto role : type.roles_) { | |
215 | role_array.push_back(get_role_name(role)); | |
216 | } | |
217 | ||
218 | _fragment["roles"] = std::move(role_array); | |
219 | } | |
220 | ||
221 | if (type.mappings_->size() < 1) { | |
f9a41357 | 222 | LTTNG_THROW_ERROR(lttng::format( |
28ab034a JG |
223 | "Failed to serialize {}: enumeration must have at least one mapping", |
224 | _fragment["type"])); | |
da9dd521 JG |
225 | } |
226 | ||
227 | json::json mappings_value; | |
28ab034a JG |
228 | for (const auto& mapping : *type.mappings_) { |
229 | mappings_value[mapping.name] = { { mapping.range.begin, | |
230 | mapping.range.end } }; | |
da9dd521 JG |
231 | } |
232 | ||
233 | _fragment["mappings"] = std::move(mappings_value); | |
234 | } | |
235 | ||
cd9adb8b | 236 | void visit(const lst::signed_enumeration_type& type) final |
da9dd521 JG |
237 | { |
238 | visit_enumeration(type); | |
239 | } | |
240 | ||
cd9adb8b | 241 | void visit(const lst::unsigned_enumeration_type& type) final |
da9dd521 JG |
242 | { |
243 | visit_enumeration(type); | |
244 | } | |
245 | ||
cd9adb8b | 246 | void visit(const lst::static_length_array_type& type) final |
da9dd521 JG |
247 | { |
248 | _fragment["type"] = "static-length-array"; | |
249 | ||
250 | ::ctf2::field_visitor element_visitor; | |
251 | type.element_type->accept(element_visitor); | |
c22ded12 | 252 | _fragment["element-field-class"] = element_visitor.move_fragment(); |
da9dd521 JG |
253 | |
254 | if (type.alignment != 0) { | |
255 | _fragment["minimum-alignment"] = type.alignment; | |
256 | } | |
257 | ||
258 | _fragment["length"] = type.length; | |
259 | } | |
260 | ||
cd9adb8b | 261 | void visit(const lst::dynamic_length_array_type& type) final |
da9dd521 JG |
262 | { |
263 | _fragment["type"] = "dynamic-length-array"; | |
264 | ||
265 | ::ctf2::field_visitor element_visitor; | |
266 | type.element_type->accept(element_visitor); | |
c22ded12 | 267 | _fragment["element-field-class"] = element_visitor.move_fragment(); |
da9dd521 JG |
268 | |
269 | if (type.alignment != 0) { | |
270 | _fragment["minimum-alignment"] = type.alignment; | |
271 | } | |
272 | ||
273 | _fragment["length-field-location"] = to_json(type.length_field_location); | |
274 | } | |
275 | ||
cd9adb8b | 276 | void visit(const lst::static_length_blob_type& type) final |
da9dd521 JG |
277 | { |
278 | _fragment["type"] = "static-length-blob"; | |
279 | _fragment["length"] = type.length_bytes; | |
280 | ||
281 | if (type.roles_.size() > 0) { | |
282 | auto role_array = json::json::array(); | |
283 | ||
284 | for (const auto role : type.roles_) { | |
285 | role_array.push_back(get_role_name(role)); | |
286 | } | |
287 | ||
288 | _fragment["roles"] = std::move(role_array); | |
289 | } | |
290 | } | |
291 | ||
cd9adb8b | 292 | void visit(const lst::dynamic_length_blob_type& type) final |
da9dd521 JG |
293 | { |
294 | _fragment["type"] = "dynamic-length-blob"; | |
295 | _fragment["length-field-location"] = to_json(type.length_field_location); | |
296 | } | |
297 | ||
cd9adb8b | 298 | void visit(const lst::null_terminated_string_type& type __attribute__((unused))) final |
da9dd521 JG |
299 | { |
300 | _fragment["type"] = "null-terminated-string"; | |
301 | } | |
302 | ||
cd9adb8b | 303 | void visit(const lst::structure_type& type) final |
da9dd521 JG |
304 | { |
305 | _fragment["type"] = "structure"; | |
306 | ||
307 | if (type.alignment != 0) { | |
308 | _fragment["minimum-alignment"] = type.alignment; | |
309 | } | |
310 | ||
311 | auto member_classes_value = json::json::array(); | |
28ab034a | 312 | for (const auto& field : type.fields_) { |
da9dd521 JG |
313 | ::ctf2::field_visitor member_visitor; |
314 | json::json member_class; | |
315 | ||
316 | field->accept(member_visitor); | |
c22ded12 | 317 | member_classes_value.emplace_back(member_visitor.move_fragment()); |
da9dd521 JG |
318 | } |
319 | ||
320 | _fragment["member-classes"] = std::move(member_classes_value); | |
321 | } | |
322 | ||
323 | template <class MappingIntegerType> | |
324 | void visit_variant(const lst::variant_type<MappingIntegerType>& type) | |
325 | { | |
326 | _fragment["type"] = "variant"; | |
327 | _fragment["selector-field-location"] = to_json(type.selector_field_location); | |
328 | ||
329 | auto options_value = json::json::array(); | |
330 | for (const auto& option : type.choices_) { | |
331 | ::ctf2::field_visitor option_visitor; | |
332 | json::json member_class; | |
333 | ||
334 | /* TODO missing selector-field-range. */ | |
28ab034a JG |
335 | member_class["selector-field-ranges"] = { { option.first.range.begin, |
336 | option.first.range.end } }; | |
da9dd521 | 337 | option.second->accept(option_visitor); |
c22ded12 | 338 | member_class["field-class"] = option_visitor.move_fragment(); |
da9dd521 JG |
339 | options_value.emplace_back(std::move(member_class)); |
340 | } | |
341 | ||
342 | _fragment["options"] = std::move(options_value); | |
343 | } | |
344 | ||
cd9adb8b | 345 | void visit(const lst::variant_type<int64_t>& type) final |
da9dd521 JG |
346 | { |
347 | visit_variant(type); | |
348 | } | |
349 | ||
cd9adb8b | 350 | void visit(const lst::variant_type<uint64_t>& type) final |
da9dd521 JG |
351 | { |
352 | visit_variant(type); | |
353 | } | |
354 | ||
cd9adb8b | 355 | void visit(const lst::static_length_string_type& type) final |
da9dd521 JG |
356 | { |
357 | _fragment["type"] = "static-length-string"; | |
358 | _fragment["length"] = type.length; | |
359 | } | |
360 | ||
cd9adb8b | 361 | void visit(const lst::dynamic_length_string_type& type) final |
da9dd521 JG |
362 | { |
363 | _fragment["type"] = "dynamic-length-string"; | |
364 | _fragment["length-field-location"] = to_json(type.length_field_location); | |
365 | } | |
366 | ||
367 | json::json _fragment; | |
368 | }; | |
369 | } /* namespace ctf2 */ | |
370 | ||
371 | }; /* namespace */ | |
372 | ||
4f2da8b8 | 373 | lsc::trace_class_visitor::trace_class_visitor( |
28ab034a | 374 | lsc::append_metadata_fragment_function append_metadata_fragment) : |
cd9adb8b | 375 | _append_metadata_fragment(std::move(append_metadata_fragment)) |
da9dd521 JG |
376 | { |
377 | } | |
378 | ||
379 | void lsc::trace_class_visitor::visit(const lst::trace_class& trace_class) | |
380 | { | |
381 | { | |
382 | auto preamble_fragment = make_json_fragment("preamble"); | |
383 | ||
384 | preamble_fragment["version"] = 2; | |
385 | preamble_fragment["uuid"] = trace_class.uuid; | |
386 | append_metadata_fragment(preamble_fragment); | |
387 | } | |
388 | ||
389 | auto trace_class_fragment = make_json_fragment("trace-class"); | |
390 | ||
391 | ::ctf2::trace_environment_visitor environment_visitor; | |
392 | trace_class.accept(environment_visitor); | |
c22ded12 | 393 | trace_class_fragment["environment"] = environment_visitor.move_fragment(); |
da9dd521 | 394 | |
4bcf2294 | 395 | const auto packet_header = trace_class.packet_header(); |
da9dd521 JG |
396 | if (packet_header) { |
397 | ::ctf2::field_visitor field_visitor; | |
398 | ||
399 | packet_header->accept(field_visitor); | |
c22ded12 | 400 | trace_class_fragment["packet-header-field-class"] = field_visitor.move_fragment(); |
da9dd521 JG |
401 | } |
402 | ||
403 | append_metadata_fragment(trace_class_fragment); | |
404 | } | |
405 | ||
406 | void lsc::trace_class_visitor::visit(const lst::clock_class& clock_class) | |
407 | { | |
28ab034a | 408 | auto clock_class_fragment = make_json_fragment("clock-class"); |
da9dd521 JG |
409 | |
410 | json::json offset; | |
28ab034a JG |
411 | offset.update({ { "seconds", clock_class.offset / clock_class.frequency }, |
412 | { "cycles", clock_class.offset % clock_class.frequency } }); | |
da9dd521 | 413 | |
28ab034a JG |
414 | clock_class_fragment.update({ { "name", clock_class.name }, |
415 | { "description", clock_class.description }, | |
416 | { "frequency", clock_class.frequency }, | |
417 | { "offset", std::move(offset) } }); | |
da9dd521 JG |
418 | |
419 | if (clock_class.uuid) { | |
420 | clock_class_fragment["uuid"] = *clock_class.uuid; | |
421 | } | |
422 | ||
423 | append_metadata_fragment(clock_class_fragment); | |
424 | } | |
425 | ||
426 | void lsc::trace_class_visitor::visit(const lst::stream_class& stream_class) | |
427 | { | |
428 | auto stream_class_fragment = make_json_fragment("data-stream-class"); | |
429 | ||
430 | stream_class_fragment["id"] = stream_class.id; | |
431 | if (stream_class.default_clock_class_name) { | |
432 | stream_class_fragment["default-clock-class-name"] = | |
28ab034a | 433 | *stream_class.default_clock_class_name; |
da9dd521 JG |
434 | } |
435 | ||
4bcf2294 | 436 | const auto packet_context = stream_class.packet_context(); |
da9dd521 JG |
437 | if (packet_context) { |
438 | ::ctf2::field_visitor visitor; | |
439 | ||
440 | packet_context->accept(visitor); | |
c22ded12 | 441 | stream_class_fragment["packet-context-field-class"] = visitor.move_fragment(); |
da9dd521 JG |
442 | } |
443 | ||
4bcf2294 | 444 | const auto event_header = stream_class.event_header(); |
da9dd521 JG |
445 | if (event_header) { |
446 | ::ctf2::field_visitor visitor; | |
447 | ||
448 | event_header->accept(visitor); | |
28ab034a | 449 | stream_class_fragment["event-record-header-field-class"] = visitor.move_fragment(); |
da9dd521 JG |
450 | } |
451 | ||
4bcf2294 | 452 | const auto event_context = stream_class.event_context(); |
da9dd521 JG |
453 | if (event_context) { |
454 | ::ctf2::field_visitor visitor; | |
455 | ||
456 | event_context->accept(visitor); | |
457 | stream_class_fragment["event-record-common-context-field-class"] = | |
28ab034a | 458 | visitor.move_fragment(); |
da9dd521 JG |
459 | } |
460 | ||
461 | append_metadata_fragment(stream_class_fragment); | |
462 | } | |
463 | ||
464 | void lsc::trace_class_visitor::visit(const lst::event_class& event_class) | |
465 | { | |
466 | auto event_class_fragment = make_json_fragment("event-record-class"); | |
467 | ||
468 | event_class_fragment["id"] = event_class.id; | |
469 | event_class_fragment["data-stream-class-id"] = event_class.stream_class_id; | |
470 | event_class_fragment["name"] = event_class.name; | |
471 | ||
472 | if (event_class.payload) { | |
473 | ::ctf2::field_visitor visitor; | |
474 | ||
475 | event_class.payload->accept(visitor); | |
c22ded12 | 476 | event_class_fragment["payload-field-class"] = visitor.move_fragment(); |
da9dd521 JG |
477 | } |
478 | ||
479 | append_metadata_fragment(event_class_fragment); | |
480 | } | |
481 | ||
482 | void lsc::trace_class_visitor::append_metadata_fragment(const nlohmann::json& fragment) const | |
483 | { | |
484 | _append_metadata_fragment(record_separator + fragment.dump(spaces_per_indent).c_str()); | |
485 | } |