169d370bd77339e4d32518204699d92d0a790bdd
[lttng-tools.git] / src / bin / lttng-sessiond / ctf2-trace-class-visitor.cpp
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
9 #include "ctf2-trace-class-visitor.hpp"
10 #include "clock-class.hpp"
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 {
30 return {{"type", type}};
31 }
32
33 json::json to_json(const lst::field_location &location)
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
58 std::copy(location.elements_.begin(), location.elements_.end(),
59 std::back_inserter(location_array));
60 return location_array;
61 }
62
63 const char *get_role_name(lst::integer_type::role role)
64 {
65 switch (role) {
66 case lst::integer_type::role::DEFAULT_CLOCK_TIMESTAMP:
67 return "default-clock-timestamp";
68 case lst::integer_type::role::DATA_STREAM_CLASS_ID:
69 return "data-stream-class-id";
70 case lst::integer_type::role::DATA_STREAM_ID:
71 return "data-stream-id";
72 case lst::integer_type::role::PACKET_MAGIC_NUMBER:
73 return "packet-magic-number";
74 case lst::integer_type::role::DISCARDED_EVENT_RECORD_COUNTER_SNAPSHOT:
75 return "discarded-event-record-counter-snapshot";
76 case lst::integer_type::role::PACKET_CONTENT_LENGTH:
77 return "packet-content-length";
78 case lst::integer_type::role::PACKET_END_DEFAULT_CLOCK_TIMESTAMP:
79 return "packet-end-default-clock-timestamp";
80 case lst::integer_type::role::PACKET_SEQUENCE_NUMBER:
81 return "packet-sequence-number";
82 case lst::integer_type::role::PACKET_TOTAL_LENGTH:
83 return "packet-total-length";
84 case lst::integer_type::role::EVENT_RECORD_CLASS_ID:
85 return "event-record-class-id";
86 default:
87 abort();
88 }
89 }
90
91 const char *get_role_name(lst::static_length_blob_type::role role)
92 {
93 switch (role) {
94 case lst::static_length_blob_type::role::METADATA_STREAM_UUID:
95 return "metadata-stream-uuid";
96 default:
97 abort();
98 }
99 }
100
101 namespace ctf2 {
102 class trace_environment_visitor : public lst::trace_class_environment_visitor {
103 public:
104 trace_environment_visitor()
105 {
106 }
107
108 virtual void visit(const lst::environment_field<int64_t>& field) override
109 {
110 _visit(field);
111 }
112
113 virtual void visit(const lst::environment_field<const char *>& field) override
114 {
115 _visit(field);
116 }
117
118 /* Only call once. */
119 json::json transfer_fragment()
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:
137 field_visitor()
138 {
139 }
140
141 /* Only call once. */
142 json::json transfer_fragment()
143 {
144 return std::move(_fragment);
145 }
146
147 private:
148 virtual void visit(const lst::field& field) override final
149 {
150 field_visitor field_type_visitor;
151 field.get_type().accept(field_type_visitor);
152
153 _fragment["name"] = field.name;
154 _fragment["field-class"] = field_type_visitor.transfer_fragment();
155 }
156
157 virtual void visit(const lst::integer_type& type) override final
158 {
159 _fragment["type"] = type.signedness_ == lst::integer_type::signedness::SIGNED ?
160 "fixed-length-signed-integer" :
161 "fixed-length-unsigned-integer";
162 _fragment["length"] = type.size;
163 _fragment["byte-order"] = type.byte_order == lst::byte_order::BIG_ENDIAN_ ?
164 "big-endian" :
165 "little-endian";
166 _fragment["alignment"] = type.alignment;
167 _fragment["preferred-display-base"] = (unsigned int) type.base_;
168
169 if (type.roles_.size() > 0) {
170 json::json role_array = json::json::array();
171
172 for (const auto role : type.roles_) {
173 role_array.push_back(get_role_name(role));
174 }
175
176 _fragment["roles"] = std::move(role_array);
177 }
178 }
179
180 virtual void visit(const lst::floating_point_type& type) override final
181 {
182 _fragment["type"] = "fixed-length-floating-point-number";
183 _fragment["length"] = type.exponent_digits + type.mantissa_digits;
184 _fragment["byte-order"] = type.byte_order == lst::byte_order::BIG_ENDIAN_ ?
185 "big-endian" :
186 "little-endian";
187 _fragment["alignment"] = type.alignment;
188 }
189
190 template <class EnumerationType>
191 void visit_enumeration(const EnumerationType& type)
192 {
193 _fragment["type"] = std::is_signed<typename EnumerationType::mapping::range_t::range_integer_t>::value ?
194 "fixed-length-signed-enumeration" :
195 "fixed-length-unsigned-enumeration";
196 _fragment["length"] = type.size;
197 _fragment["byte-order"] = type.byte_order == lst::byte_order::BIG_ENDIAN_ ?
198 "big-endian" :
199 "little-endian";
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::
205 range_integer_t>::value) {
206 LTTNG_THROW_ERROR(fmt::format(
207 "Failed to serialize {}: unexpected role",
208 _fragment["type"]));
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(
222 "Failed to serialize {}: enumeration must have at least one mapping",
223 _fragment["type"]));
224 }
225
226 json::json mappings_value;
227 for (const auto &mapping : *type.mappings_) {
228 mappings_value[mapping.name] = {{mapping.range.begin, mapping.range.end}};
229 }
230
231 _fragment["mappings"] = std::move(mappings_value);
232 }
233
234 virtual void visit(const lst::signed_enumeration_type& type) override final
235 {
236 visit_enumeration(type);
237 }
238
239 virtual void visit(const lst::unsigned_enumeration_type& type) override final
240 {
241 visit_enumeration(type);
242 }
243
244 virtual void visit(const lst::static_length_array_type& type) override final
245 {
246 _fragment["type"] = "static-length-array";
247
248 ::ctf2::field_visitor element_visitor;
249 type.element_type->accept(element_visitor);
250 _fragment["element-field-class"] = element_visitor.transfer_fragment();
251
252 if (type.alignment != 0) {
253 _fragment["minimum-alignment"] = type.alignment;
254 }
255
256 _fragment["length"] = type.length;
257 }
258
259 virtual void visit(const lst::dynamic_length_array_type& type) override final
260 {
261 _fragment["type"] = "dynamic-length-array";
262
263 ::ctf2::field_visitor element_visitor;
264 type.element_type->accept(element_visitor);
265 _fragment["element-field-class"] = element_visitor.transfer_fragment();
266
267 if (type.alignment != 0) {
268 _fragment["minimum-alignment"] = type.alignment;
269 }
270
271 _fragment["length-field-location"] = to_json(type.length_field_location);
272 }
273
274 virtual void visit(const lst::static_length_blob_type& type) override final
275 {
276 _fragment["type"] = "static-length-blob";
277 _fragment["length"] = type.length_bytes;
278
279 if (type.roles_.size() > 0) {
280 auto role_array = json::json::array();
281
282 for (const auto role : type.roles_) {
283 role_array.push_back(get_role_name(role));
284 }
285
286 _fragment["roles"] = std::move(role_array);
287 }
288 }
289
290 virtual void visit(const lst::dynamic_length_blob_type& type) override final
291 {
292 _fragment["type"] = "dynamic-length-blob";
293 _fragment["length-field-location"] = to_json(type.length_field_location);
294 }
295
296 virtual void visit(const lst::null_terminated_string_type& type
297 __attribute__((unused))) override final
298 {
299 _fragment["type"] = "null-terminated-string";
300 }
301
302 virtual void visit(const lst::structure_type& type) override final
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();
311 for (const auto &field : type.fields_) {
312 ::ctf2::field_visitor member_visitor;
313 json::json member_class;
314
315 field->accept(member_visitor);
316 member_classes_value.emplace_back(member_visitor.transfer_fragment());
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. */
334 member_class["selector-field-ranges"] = {{option.first.range.begin, option.first.range.end}};
335 option.second->accept(option_visitor);
336 member_class["field-class"] = option_visitor.transfer_fragment();
337 options_value.emplace_back(std::move(member_class));
338 }
339
340 _fragment["options"] = std::move(options_value);
341 }
342
343 virtual void visit(const lst::variant_type<int64_t>& type) override final
344 {
345 visit_variant(type);
346 }
347
348 virtual void visit(const lst::variant_type<uint64_t>& type) override final
349 {
350 visit_variant(type);
351 }
352
353 virtual void visit(const lst::static_length_string_type& type) override final
354 {
355 _fragment["type"] = "static-length-string";
356 _fragment["length"] = type.length;
357 }
358
359 virtual void visit(const lst::dynamic_length_string_type& type) override final
360 {
361 _fragment["type"] = "dynamic-length-string";
362 _fragment["length-field-location"] = to_json(type.length_field_location);
363 }
364
365 json::json _fragment;
366 };
367 } /* namespace ctf2 */
368
369 }; /* namespace */
370
371 lsc::trace_class_visitor::trace_class_visitor(const lst::abi& trace_abi,
372 lsc::append_metadata_fragment_function append_metadata_fragment) :
373 _trace_abi{trace_abi}, _append_metadata_fragment(append_metadata_fragment)
374 {
375 }
376
377 void lsc::trace_class_visitor::visit(const lst::trace_class& trace_class)
378 {
379 {
380 auto preamble_fragment = make_json_fragment("preamble");
381
382 preamble_fragment["version"] = 2;
383 preamble_fragment["uuid"] = trace_class.uuid;
384 append_metadata_fragment(preamble_fragment);
385 }
386
387 auto trace_class_fragment = make_json_fragment("trace-class");
388
389 ::ctf2::trace_environment_visitor environment_visitor;
390 trace_class.accept(environment_visitor);
391 trace_class_fragment["environment"] = environment_visitor.transfer_fragment();
392
393 const auto packet_header = trace_class.get_packet_header();
394 if (packet_header) {
395 ::ctf2::field_visitor field_visitor;
396
397 packet_header->accept(field_visitor);
398 trace_class_fragment["packet-header-field-class"] = field_visitor.transfer_fragment();
399 }
400
401 append_metadata_fragment(trace_class_fragment);
402 }
403
404 void lsc::trace_class_visitor::visit(const lst::clock_class& clock_class)
405 {
406 auto clock_class_fragment = make_json_fragment("clock-class");
407
408 json::json offset;
409 offset.update({{"seconds", clock_class.offset / clock_class.frequency},
410 {"cycles", clock_class.offset % clock_class.frequency}});
411
412 clock_class_fragment.update({
413 {"name", clock_class.name},
414 {"description", clock_class.description},
415 {"frequency", clock_class.frequency},
416 {"offset", std::move(offset)}});
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"] =
432 *stream_class.default_clock_class_name;
433 }
434
435 const auto packet_context = stream_class.get_packet_context();
436 if (packet_context) {
437 ::ctf2::field_visitor visitor;
438
439 packet_context->accept(visitor);
440 stream_class_fragment["packet-context-field-class"] = visitor.transfer_fragment();
441 }
442
443 const auto event_header = stream_class.get_event_header();
444 if (event_header) {
445 ::ctf2::field_visitor visitor;
446
447 event_header->accept(visitor);
448 stream_class_fragment["event-record-header-field-class"] =
449 visitor.transfer_fragment();
450 }
451
452 const auto event_context = stream_class.get_event_context();
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"] =
458 visitor.transfer_fragment();
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);
476 event_class_fragment["payload-field-class"] = visitor.transfer_fragment();
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 }
This page took 0.03834 seconds and 3 git commands to generate.