2f583cdf89525d382a5d5a9d9b13efc33af4b0b8
[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 "clock-class.hpp"
10 #include "ctf2-trace-class-visitor.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(),
59 location.elements_.end(),
60 std::back_inserter(location_array));
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. */
120 json::json move_fragment()
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. */
143 json::json move_fragment()
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;
155 _fragment["field-class"] = field_type_visitor.move_fragment();
156 }
157
158 virtual void visit(const lst::integer_type& type) override final
159 {
160 _fragment["type"] = type.signedness_ == lst::integer_type::signedness::SIGNED ?
161 "fixed-length-signed-integer" :
162 "fixed-length-unsigned-integer";
163 _fragment["length"] = type.size;
164 _fragment["byte-order"] = type.byte_order == lst::byte_order::BIG_ENDIAN_ ?
165 "big-endian" :
166 "little-endian";
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_ ?
186 "big-endian" :
187 "little-endian";
188 _fragment["alignment"] = type.alignment;
189 }
190
191 template <class EnumerationType>
192 void visit_enumeration(const EnumerationType& type)
193 {
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";
199 _fragment["length"] = type.size;
200 _fragment["byte-order"] = type.byte_order == lst::byte_order::BIG_ENDIAN_ ?
201 "big-endian" :
202 "little-endian";
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::
208 range_integer_t>::value) {
209 LTTNG_THROW_ERROR(
210 fmt::format("Failed to serialize {}: unexpected role",
211 _fragment["type"]));
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(
225 "Failed to serialize {}: enumeration must have at least one mapping",
226 _fragment["type"]));
227 }
228
229 json::json mappings_value;
230 for (const auto& mapping : *type.mappings_) {
231 mappings_value[mapping.name] = { { mapping.range.begin,
232 mapping.range.end } };
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);
254 _fragment["element-field-class"] = element_visitor.move_fragment();
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);
269 _fragment["element-field-class"] = element_visitor.move_fragment();
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
301 __attribute__((unused))) override final
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();
315 for (const auto& field : type.fields_) {
316 ::ctf2::field_visitor member_visitor;
317 json::json member_class;
318
319 field->accept(member_visitor);
320 member_classes_value.emplace_back(member_visitor.move_fragment());
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. */
338 member_class["selector-field-ranges"] = { { option.first.range.begin,
339 option.first.range.end } };
340 option.second->accept(option_visitor);
341 member_class["field-class"] = option_visitor.move_fragment();
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
376 lsc::trace_class_visitor::trace_class_visitor(
377 lsc::append_metadata_fragment_function append_metadata_fragment) :
378 _append_metadata_fragment(append_metadata_fragment)
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);
396 trace_class_fragment["environment"] = environment_visitor.move_fragment();
397
398 const auto packet_header = trace_class.packet_header();
399 if (packet_header) {
400 ::ctf2::field_visitor field_visitor;
401
402 packet_header->accept(field_visitor);
403 trace_class_fragment["packet-header-field-class"] = field_visitor.move_fragment();
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 {
411 auto clock_class_fragment = make_json_fragment("clock-class");
412
413 json::json offset;
414 offset.update({ { "seconds", clock_class.offset / clock_class.frequency },
415 { "cycles", clock_class.offset % clock_class.frequency } });
416
417 clock_class_fragment.update({ { "name", clock_class.name },
418 { "description", clock_class.description },
419 { "frequency", clock_class.frequency },
420 { "offset", std::move(offset) } });
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"] =
436 *stream_class.default_clock_class_name;
437 }
438
439 const auto packet_context = stream_class.packet_context();
440 if (packet_context) {
441 ::ctf2::field_visitor visitor;
442
443 packet_context->accept(visitor);
444 stream_class_fragment["packet-context-field-class"] = visitor.move_fragment();
445 }
446
447 const auto event_header = stream_class.event_header();
448 if (event_header) {
449 ::ctf2::field_visitor visitor;
450
451 event_header->accept(visitor);
452 stream_class_fragment["event-record-header-field-class"] = visitor.move_fragment();
453 }
454
455 const auto event_context = stream_class.event_context();
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"] =
461 visitor.move_fragment();
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);
479 event_class_fragment["payload-field-class"] = visitor.move_fragment();
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 }
This page took 0.039346 seconds and 4 git commands to generate.