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