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: | |
cd9adb8b | 106 | trace_environment_visitor() = default; |
da9dd521 | 107 | |
cd9adb8b | 108 | void visit(const lst::environment_field<int64_t>& field) override |
da9dd521 JG |
109 | { |
110 | _visit(field); | |
111 | } | |
112 | ||
cd9adb8b | 113 | void visit(const lst::environment_field<const char *>& field) override |
da9dd521 JG |
114 | { |
115 | _visit(field); | |
116 | } | |
117 | ||
118 | /* Only call once. */ | |
c22ded12 | 119 | json::json move_fragment() |
da9dd521 JG |
120 | { |
121 | return std::move(_environment); | |
122 | } | |
123 | ||
124 | private: | |
125 | template <class FieldType> | |
126 | void _visit(const FieldType& field) | |
127 | { | |
128 | _environment[field.name] = field.value; | |
129 | } | |
130 | ||
131 | json::json _environment; | |
132 | }; | |
133 | ||
134 | class field_visitor : public lttng::sessiond::trace::field_visitor, | |
135 | public lttng::sessiond::trace::type_visitor { | |
136 | public: | |
cd9adb8b | 137 | field_visitor() = default; |
da9dd521 JG |
138 | |
139 | /* Only call once. */ | |
c22ded12 | 140 | json::json move_fragment() |
da9dd521 JG |
141 | { |
142 | return std::move(_fragment); | |
143 | } | |
144 | ||
145 | private: | |
cd9adb8b | 146 | void visit(const lst::field& field) final |
da9dd521 JG |
147 | { |
148 | field_visitor field_type_visitor; | |
149 | field.get_type().accept(field_type_visitor); | |
150 | ||
151 | _fragment["name"] = field.name; | |
c22ded12 | 152 | _fragment["field-class"] = field_type_visitor.move_fragment(); |
da9dd521 JG |
153 | } |
154 | ||
cd9adb8b | 155 | void visit(const lst::integer_type& type) final |
da9dd521 JG |
156 | { |
157 | _fragment["type"] = type.signedness_ == lst::integer_type::signedness::SIGNED ? | |
28ab034a JG |
158 | "fixed-length-signed-integer" : |
159 | "fixed-length-unsigned-integer"; | |
da9dd521 JG |
160 | _fragment["length"] = type.size; |
161 | _fragment["byte-order"] = type.byte_order == lst::byte_order::BIG_ENDIAN_ ? | |
28ab034a JG |
162 | "big-endian" : |
163 | "little-endian"; | |
da9dd521 JG |
164 | _fragment["alignment"] = type.alignment; |
165 | _fragment["preferred-display-base"] = (unsigned int) type.base_; | |
166 | ||
167 | if (type.roles_.size() > 0) { | |
168 | json::json role_array = json::json::array(); | |
169 | ||
170 | for (const auto role : type.roles_) { | |
171 | role_array.push_back(get_role_name(role)); | |
172 | } | |
173 | ||
174 | _fragment["roles"] = std::move(role_array); | |
175 | } | |
176 | } | |
177 | ||
cd9adb8b | 178 | void visit(const lst::floating_point_type& type) final |
da9dd521 JG |
179 | { |
180 | _fragment["type"] = "fixed-length-floating-point-number"; | |
181 | _fragment["length"] = type.exponent_digits + type.mantissa_digits; | |
182 | _fragment["byte-order"] = type.byte_order == lst::byte_order::BIG_ENDIAN_ ? | |
28ab034a JG |
183 | "big-endian" : |
184 | "little-endian"; | |
da9dd521 JG |
185 | _fragment["alignment"] = type.alignment; |
186 | } | |
187 | ||
188 | template <class EnumerationType> | |
189 | void visit_enumeration(const EnumerationType& type) | |
190 | { | |
28ab034a JG |
191 | _fragment["type"] = |
192 | std::is_signed< | |
193 | typename EnumerationType::mapping::range_t::range_integer_t>::value ? | |
194 | "fixed-length-signed-enumeration" : | |
195 | "fixed-length-unsigned-enumeration"; | |
da9dd521 JG |
196 | _fragment["length"] = type.size; |
197 | _fragment["byte-order"] = type.byte_order == lst::byte_order::BIG_ENDIAN_ ? | |
28ab034a JG |
198 | "big-endian" : |
199 | "little-endian"; | |
da9dd521 JG |
200 | _fragment["alignment"] = type.alignment; |
201 | _fragment["preferred-display-base"] = (unsigned int) type.base_; | |
202 | ||
203 | if (type.roles_.size() > 0) { | |
204 | if (std::is_signed<typename EnumerationType::mapping::range_t:: | |
28ab034a JG |
205 | range_integer_t>::value) { |
206 | LTTNG_THROW_ERROR( | |
207 | fmt::format("Failed to serialize {}: unexpected role", | |
208 | _fragment["type"])); | |
da9dd521 JG |
209 | } |
210 | ||
211 | auto role_array = json::json::array(); | |
212 | ||
213 | for (const auto role : type.roles_) { | |
214 | role_array.push_back(get_role_name(role)); | |
215 | } | |
216 | ||
217 | _fragment["roles"] = std::move(role_array); | |
218 | } | |
219 | ||
220 | if (type.mappings_->size() < 1) { | |
221 | LTTNG_THROW_ERROR(fmt::format( | |
28ab034a JG |
222 | "Failed to serialize {}: enumeration must have at least one mapping", |
223 | _fragment["type"])); | |
da9dd521 JG |
224 | } |
225 | ||
226 | json::json mappings_value; | |
28ab034a JG |
227 | for (const auto& mapping : *type.mappings_) { |
228 | mappings_value[mapping.name] = { { mapping.range.begin, | |
229 | mapping.range.end } }; | |
da9dd521 JG |
230 | } |
231 | ||
232 | _fragment["mappings"] = std::move(mappings_value); | |
233 | } | |
234 | ||
cd9adb8b | 235 | void visit(const lst::signed_enumeration_type& type) final |
da9dd521 JG |
236 | { |
237 | visit_enumeration(type); | |
238 | } | |
239 | ||
cd9adb8b | 240 | void visit(const lst::unsigned_enumeration_type& type) final |
da9dd521 JG |
241 | { |
242 | visit_enumeration(type); | |
243 | } | |
244 | ||
cd9adb8b | 245 | void visit(const lst::static_length_array_type& type) final |
da9dd521 JG |
246 | { |
247 | _fragment["type"] = "static-length-array"; | |
248 | ||
249 | ::ctf2::field_visitor element_visitor; | |
250 | type.element_type->accept(element_visitor); | |
c22ded12 | 251 | _fragment["element-field-class"] = element_visitor.move_fragment(); |
da9dd521 JG |
252 | |
253 | if (type.alignment != 0) { | |
254 | _fragment["minimum-alignment"] = type.alignment; | |
255 | } | |
256 | ||
257 | _fragment["length"] = type.length; | |
258 | } | |
259 | ||
cd9adb8b | 260 | void visit(const lst::dynamic_length_array_type& type) final |
da9dd521 JG |
261 | { |
262 | _fragment["type"] = "dynamic-length-array"; | |
263 | ||
264 | ::ctf2::field_visitor element_visitor; | |
265 | type.element_type->accept(element_visitor); | |
c22ded12 | 266 | _fragment["element-field-class"] = element_visitor.move_fragment(); |
da9dd521 JG |
267 | |
268 | if (type.alignment != 0) { | |
269 | _fragment["minimum-alignment"] = type.alignment; | |
270 | } | |
271 | ||
272 | _fragment["length-field-location"] = to_json(type.length_field_location); | |
273 | } | |
274 | ||
cd9adb8b | 275 | void visit(const lst::static_length_blob_type& type) final |
da9dd521 JG |
276 | { |
277 | _fragment["type"] = "static-length-blob"; | |
278 | _fragment["length"] = type.length_bytes; | |
279 | ||
280 | if (type.roles_.size() > 0) { | |
281 | auto role_array = json::json::array(); | |
282 | ||
283 | for (const auto role : type.roles_) { | |
284 | role_array.push_back(get_role_name(role)); | |
285 | } | |
286 | ||
287 | _fragment["roles"] = std::move(role_array); | |
288 | } | |
289 | } | |
290 | ||
cd9adb8b | 291 | void visit(const lst::dynamic_length_blob_type& type) final |
da9dd521 JG |
292 | { |
293 | _fragment["type"] = "dynamic-length-blob"; | |
294 | _fragment["length-field-location"] = to_json(type.length_field_location); | |
295 | } | |
296 | ||
cd9adb8b | 297 | void visit(const lst::null_terminated_string_type& type __attribute__((unused))) final |
da9dd521 JG |
298 | { |
299 | _fragment["type"] = "null-terminated-string"; | |
300 | } | |
301 | ||
cd9adb8b | 302 | void visit(const lst::structure_type& type) final |
da9dd521 JG |
303 | { |
304 | _fragment["type"] = "structure"; | |
305 | ||
306 | if (type.alignment != 0) { | |
307 | _fragment["minimum-alignment"] = type.alignment; | |
308 | } | |
309 | ||
310 | auto member_classes_value = json::json::array(); | |
28ab034a | 311 | for (const auto& field : type.fields_) { |
da9dd521 JG |
312 | ::ctf2::field_visitor member_visitor; |
313 | json::json member_class; | |
314 | ||
315 | field->accept(member_visitor); | |
c22ded12 | 316 | member_classes_value.emplace_back(member_visitor.move_fragment()); |
da9dd521 JG |
317 | } |
318 | ||
319 | _fragment["member-classes"] = std::move(member_classes_value); | |
320 | } | |
321 | ||
322 | template <class MappingIntegerType> | |
323 | void visit_variant(const lst::variant_type<MappingIntegerType>& type) | |
324 | { | |
325 | _fragment["type"] = "variant"; | |
326 | _fragment["selector-field-location"] = to_json(type.selector_field_location); | |
327 | ||
328 | auto options_value = json::json::array(); | |
329 | for (const auto& option : type.choices_) { | |
330 | ::ctf2::field_visitor option_visitor; | |
331 | json::json member_class; | |
332 | ||
333 | /* TODO missing selector-field-range. */ | |
28ab034a JG |
334 | member_class["selector-field-ranges"] = { { option.first.range.begin, |
335 | option.first.range.end } }; | |
da9dd521 | 336 | option.second->accept(option_visitor); |
c22ded12 | 337 | member_class["field-class"] = option_visitor.move_fragment(); |
da9dd521 JG |
338 | options_value.emplace_back(std::move(member_class)); |
339 | } | |
340 | ||
341 | _fragment["options"] = std::move(options_value); | |
342 | } | |
343 | ||
cd9adb8b | 344 | void visit(const lst::variant_type<int64_t>& type) final |
da9dd521 JG |
345 | { |
346 | visit_variant(type); | |
347 | } | |
348 | ||
cd9adb8b | 349 | void visit(const lst::variant_type<uint64_t>& type) final |
da9dd521 JG |
350 | { |
351 | visit_variant(type); | |
352 | } | |
353 | ||
cd9adb8b | 354 | void visit(const lst::static_length_string_type& type) final |
da9dd521 JG |
355 | { |
356 | _fragment["type"] = "static-length-string"; | |
357 | _fragment["length"] = type.length; | |
358 | } | |
359 | ||
cd9adb8b | 360 | void visit(const lst::dynamic_length_string_type& type) final |
da9dd521 JG |
361 | { |
362 | _fragment["type"] = "dynamic-length-string"; | |
363 | _fragment["length-field-location"] = to_json(type.length_field_location); | |
364 | } | |
365 | ||
366 | json::json _fragment; | |
367 | }; | |
368 | } /* namespace ctf2 */ | |
369 | ||
370 | }; /* namespace */ | |
371 | ||
4f2da8b8 | 372 | lsc::trace_class_visitor::trace_class_visitor( |
28ab034a | 373 | lsc::append_metadata_fragment_function append_metadata_fragment) : |
cd9adb8b | 374 | _append_metadata_fragment(std::move(append_metadata_fragment)) |
da9dd521 JG |
375 | { |
376 | } | |
377 | ||
378 | void lsc::trace_class_visitor::visit(const lst::trace_class& trace_class) | |
379 | { | |
380 | { | |
381 | auto preamble_fragment = make_json_fragment("preamble"); | |
382 | ||
383 | preamble_fragment["version"] = 2; | |
384 | preamble_fragment["uuid"] = trace_class.uuid; | |
385 | append_metadata_fragment(preamble_fragment); | |
386 | } | |
387 | ||
388 | auto trace_class_fragment = make_json_fragment("trace-class"); | |
389 | ||
390 | ::ctf2::trace_environment_visitor environment_visitor; | |
391 | trace_class.accept(environment_visitor); | |
c22ded12 | 392 | trace_class_fragment["environment"] = environment_visitor.move_fragment(); |
da9dd521 | 393 | |
4bcf2294 | 394 | const auto packet_header = trace_class.packet_header(); |
da9dd521 JG |
395 | if (packet_header) { |
396 | ::ctf2::field_visitor field_visitor; | |
397 | ||
398 | packet_header->accept(field_visitor); | |
c22ded12 | 399 | trace_class_fragment["packet-header-field-class"] = field_visitor.move_fragment(); |
da9dd521 JG |
400 | } |
401 | ||
402 | append_metadata_fragment(trace_class_fragment); | |
403 | } | |
404 | ||
405 | void lsc::trace_class_visitor::visit(const lst::clock_class& clock_class) | |
406 | { | |
28ab034a | 407 | auto clock_class_fragment = make_json_fragment("clock-class"); |
da9dd521 JG |
408 | |
409 | json::json offset; | |
28ab034a JG |
410 | offset.update({ { "seconds", clock_class.offset / clock_class.frequency }, |
411 | { "cycles", clock_class.offset % clock_class.frequency } }); | |
da9dd521 | 412 | |
28ab034a JG |
413 | clock_class_fragment.update({ { "name", clock_class.name }, |
414 | { "description", clock_class.description }, | |
415 | { "frequency", clock_class.frequency }, | |
416 | { "offset", std::move(offset) } }); | |
da9dd521 JG |
417 | |
418 | if (clock_class.uuid) { | |
419 | clock_class_fragment["uuid"] = *clock_class.uuid; | |
420 | } | |
421 | ||
422 | append_metadata_fragment(clock_class_fragment); | |
423 | } | |
424 | ||
425 | void lsc::trace_class_visitor::visit(const lst::stream_class& stream_class) | |
426 | { | |
427 | auto stream_class_fragment = make_json_fragment("data-stream-class"); | |
428 | ||
429 | stream_class_fragment["id"] = stream_class.id; | |
430 | if (stream_class.default_clock_class_name) { | |
431 | stream_class_fragment["default-clock-class-name"] = | |
28ab034a | 432 | *stream_class.default_clock_class_name; |
da9dd521 JG |
433 | } |
434 | ||
4bcf2294 | 435 | const auto packet_context = stream_class.packet_context(); |
da9dd521 JG |
436 | if (packet_context) { |
437 | ::ctf2::field_visitor visitor; | |
438 | ||
439 | packet_context->accept(visitor); | |
c22ded12 | 440 | stream_class_fragment["packet-context-field-class"] = visitor.move_fragment(); |
da9dd521 JG |
441 | } |
442 | ||
4bcf2294 | 443 | const auto event_header = stream_class.event_header(); |
da9dd521 JG |
444 | if (event_header) { |
445 | ::ctf2::field_visitor visitor; | |
446 | ||
447 | event_header->accept(visitor); | |
28ab034a | 448 | stream_class_fragment["event-record-header-field-class"] = visitor.move_fragment(); |
da9dd521 JG |
449 | } |
450 | ||
4bcf2294 | 451 | const auto event_context = stream_class.event_context(); |
da9dd521 JG |
452 | if (event_context) { |
453 | ::ctf2::field_visitor visitor; | |
454 | ||
455 | event_context->accept(visitor); | |
456 | stream_class_fragment["event-record-common-context-field-class"] = | |
28ab034a | 457 | visitor.move_fragment(); |
da9dd521 JG |
458 | } |
459 | ||
460 | append_metadata_fragment(stream_class_fragment); | |
461 | } | |
462 | ||
463 | void lsc::trace_class_visitor::visit(const lst::event_class& event_class) | |
464 | { | |
465 | auto event_class_fragment = make_json_fragment("event-record-class"); | |
466 | ||
467 | event_class_fragment["id"] = event_class.id; | |
468 | event_class_fragment["data-stream-class-id"] = event_class.stream_class_id; | |
469 | event_class_fragment["name"] = event_class.name; | |
470 | ||
471 | if (event_class.payload) { | |
472 | ::ctf2::field_visitor visitor; | |
473 | ||
474 | event_class.payload->accept(visitor); | |
c22ded12 | 475 | event_class_fragment["payload-field-class"] = visitor.move_fragment(); |
da9dd521 JG |
476 | } |
477 | ||
478 | append_metadata_fragment(event_class_fragment); | |
479 | } | |
480 | ||
481 | void lsc::trace_class_visitor::append_metadata_fragment(const nlohmann::json& fragment) const | |
482 | { | |
483 | _append_metadata_fragment(record_separator + fragment.dump(spaces_per_indent).c_str()); | |
484 | } |