2 * Copyright (C) 2022 Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 * Copyright (C) 2022 Simon Marchi <simon.marchi@efficios.com>
5 * SPDX-License-Identifier: GPL-2.0-only
9 #include "clock-class.hpp"
10 #include "ctf2-trace-class-visitor.hpp"
12 #include <common/exception.hpp>
13 #include <common/format.hpp>
15 #include <vendor/nlohmann/json.hpp>
20 namespace lsc
= lttng::sessiond::ctf2
;
21 namespace lst
= lttng::sessiond::trace
;
23 namespace json
= nlohmann
;
26 const unsigned int spaces_per_indent
= 2;
27 const std::string record_separator
= "\x1e";
29 json::json
make_json_fragment(const char *type
)
31 return { { "type", type
} };
34 json::json
to_json(const lst::field_location
& location
)
36 json::json location_array
;
38 switch (location
.root_
) {
39 case lst::field_location::root::PACKET_HEADER
:
40 location_array
.push_back("packet-header");
42 case lst::field_location::root::PACKET_CONTEXT
:
43 location_array
.push_back("packet-context");
45 case lst::field_location::root::EVENT_RECORD_HEADER
:
46 location_array
.push_back("event-record-header");
48 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT
:
49 location_array
.push_back("event-record-common-context");
51 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT
:
52 location_array
.push_back("event-record-specific-context");
54 case lst::field_location::root::EVENT_RECORD_PAYLOAD
:
55 location_array
.push_back("event-record-payload");
59 std::copy(location
.elements_
.begin(),
60 location
.elements_
.end(),
61 std::back_inserter(location_array
));
62 return location_array
;
65 const char *get_role_name(lst::integer_type::role 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";
93 const char *get_role_name(lst::static_length_blob_type::role role
)
96 case lst::static_length_blob_type::role::METADATA_STREAM_UUID
:
97 return "metadata-stream-uuid";
104 class trace_environment_visitor
: public lst::trace_class_environment_visitor
{
106 trace_environment_visitor() = default; /* NOLINT clang-tidy 14 identifies this as a move
109 void visit(const lst::environment_field
<int64_t>& field
) override
114 void visit(const lst::environment_field
<const char *>& field
) override
119 /* Only call once. */
120 json::json
move_fragment()
122 return std::move(_environment
);
126 template <class FieldType
>
127 void _visit(const FieldType
& field
)
129 _environment
[field
.name
] = field
.value
;
132 json::json _environment
;
135 class field_visitor
: public lttng::sessiond::trace::field_visitor
,
136 public lttng::sessiond::trace::type_visitor
{
138 field_visitor() = default; /* NOLINT clang-tidy 14 identifies this as a move constructor. */
140 /* Only call once. */
141 json::json
move_fragment()
143 return std::move(_fragment
);
147 void visit(const lst::field
& field
) final
149 field_visitor field_type_visitor
;
150 field
.get_type().accept(field_type_visitor
);
152 _fragment
["name"] = field
.name
;
153 _fragment
["field-class"] = field_type_visitor
.move_fragment();
156 void visit(const lst::integer_type
& type
) final
158 _fragment
["type"] = type
.signedness_
== lst::integer_type::signedness::SIGNED
?
159 "fixed-length-signed-integer" :
160 "fixed-length-unsigned-integer";
161 _fragment
["length"] = type
.size
;
162 _fragment
["byte-order"] = type
.byte_order
== lst::byte_order::BIG_ENDIAN_
?
165 _fragment
["alignment"] = type
.alignment
;
166 _fragment
["preferred-display-base"] = (unsigned int) type
.base_
;
168 if (!type
.roles_
.empty()) {
169 json::json role_array
= json::json::array();
171 for (const auto role
: type
.roles_
) {
172 role_array
.push_back(get_role_name(role
));
175 _fragment
["roles"] = std::move(role_array
);
179 void visit(const lst::floating_point_type
& type
) final
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_
?
186 _fragment
["alignment"] = type
.alignment
;
189 template <class EnumerationType
>
190 void visit_enumeration(const EnumerationType
& type
)
194 typename
EnumerationType::mapping::range_t::range_integer_t
>::value
?
195 "fixed-length-signed-enumeration" :
196 "fixed-length-unsigned-enumeration";
197 _fragment
["length"] = type
.size
;
198 _fragment
["byte-order"] = type
.byte_order
== lst::byte_order::BIG_ENDIAN_
?
201 _fragment
["alignment"] = type
.alignment
;
202 _fragment
["preferred-display-base"] = (unsigned int) type
.base_
;
204 if (!type
.roles_
.empty()) {
205 if (std::is_signed
<typename
EnumerationType::mapping::range_t::
206 range_integer_t
>::value
) {
208 lttng::format("Failed to serialize {}: unexpected role",
209 _fragment
["type"].get
<std::string
>()));
212 auto role_array
= json::json::array();
214 for (const auto role
: type
.roles_
) {
215 role_array
.push_back(get_role_name(role
));
218 _fragment
["roles"] = std::move(role_array
);
221 if (type
.mappings_
->size() < 1) {
222 LTTNG_THROW_ERROR(lttng::format(
223 "Failed to serialize {}: enumeration must have at least one mapping",
224 _fragment
["type"].get
<std::string
>()));
227 json::json mappings_value
;
228 for (const auto& mapping
: *type
.mappings_
) {
229 mappings_value
[mapping
.name
] = { { mapping
.range
.begin
,
230 mapping
.range
.end
} };
233 _fragment
["mappings"] = std::move(mappings_value
);
236 void visit(const lst::signed_enumeration_type
& type
) final
238 visit_enumeration(type
);
241 void visit(const lst::unsigned_enumeration_type
& type
) final
243 visit_enumeration(type
);
246 void visit(const lst::static_length_array_type
& type
) final
248 _fragment
["type"] = "static-length-array";
250 ::ctf2::field_visitor element_visitor
;
251 type
.element_type
->accept(element_visitor
);
252 _fragment
["element-field-class"] = element_visitor
.move_fragment();
254 if (type
.alignment
!= 0) {
255 _fragment
["minimum-alignment"] = type
.alignment
;
258 _fragment
["length"] = type
.length
;
261 void visit(const lst::dynamic_length_array_type
& type
) final
263 _fragment
["type"] = "dynamic-length-array";
265 ::ctf2::field_visitor element_visitor
;
266 type
.element_type
->accept(element_visitor
);
267 _fragment
["element-field-class"] = element_visitor
.move_fragment();
269 if (type
.alignment
!= 0) {
270 _fragment
["minimum-alignment"] = type
.alignment
;
273 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
276 void visit(const lst::static_length_blob_type
& type
) final
278 _fragment
["type"] = "static-length-blob";
279 _fragment
["length"] = type
.length_bytes
;
281 if (!type
.roles_
.empty()) {
282 auto role_array
= json::json::array();
284 for (const auto role
: type
.roles_
) {
285 role_array
.push_back(get_role_name(role
));
288 _fragment
["roles"] = std::move(role_array
);
292 void visit(const lst::dynamic_length_blob_type
& type
) final
294 _fragment
["type"] = "dynamic-length-blob";
295 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
298 void visit(const lst::null_terminated_string_type
& type
__attribute__((unused
))) final
300 _fragment
["type"] = "null-terminated-string";
303 void visit(const lst::structure_type
& type
) final
305 _fragment
["type"] = "structure";
307 if (type
.alignment
!= 0) {
308 _fragment
["minimum-alignment"] = type
.alignment
;
311 auto member_classes_value
= json::json::array();
312 for (const auto& field
: type
.fields_
) {
313 ::ctf2::field_visitor member_visitor
;
314 const json::json member_class
;
316 field
->accept(member_visitor
);
317 member_classes_value
.emplace_back(member_visitor
.move_fragment());
320 _fragment
["member-classes"] = std::move(member_classes_value
);
323 template <class MappingIntegerType
>
324 void visit_variant(const lst::variant_type
<MappingIntegerType
>& type
)
326 _fragment
["type"] = "variant";
327 _fragment
["selector-field-location"] = to_json(type
.selector_field_location
);
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
;
334 /* TODO missing selector-field-range. */
335 member_class
["selector-field-ranges"] = { { option
.first
.range
.begin
,
336 option
.first
.range
.end
} };
337 option
.second
->accept(option_visitor
);
338 member_class
["field-class"] = option_visitor
.move_fragment();
339 options_value
.emplace_back(std::move(member_class
));
342 _fragment
["options"] = std::move(options_value
);
345 void visit(const lst::variant_type
<int64_t>& type
) final
350 void visit(const lst::variant_type
<uint64_t>& type
) final
355 void visit(const lst::static_length_string_type
& type
) final
357 _fragment
["type"] = "static-length-string";
358 _fragment
["length"] = type
.length
;
361 void visit(const lst::dynamic_length_string_type
& type
) final
363 _fragment
["type"] = "dynamic-length-string";
364 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
367 json::json _fragment
;
369 } /* namespace ctf2 */
373 lsc::trace_class_visitor::trace_class_visitor(
374 lsc::append_metadata_fragment_function append_metadata_fragment
) :
375 _append_metadata_fragment(std::move(append_metadata_fragment
))
379 void lsc::trace_class_visitor::visit(const lst::trace_class
& trace_class
)
382 auto preamble_fragment
= make_json_fragment("preamble");
384 preamble_fragment
["version"] = 2;
385 preamble_fragment
["uuid"] = trace_class
.uuid
;
386 append_metadata_fragment(preamble_fragment
);
389 auto trace_class_fragment
= make_json_fragment("trace-class");
391 ::ctf2::trace_environment_visitor environment_visitor
;
392 trace_class
.accept(environment_visitor
);
393 trace_class_fragment
["environment"] = environment_visitor
.move_fragment();
395 const auto packet_header
= trace_class
.packet_header();
397 ::ctf2::field_visitor field_visitor
;
399 packet_header
->accept(field_visitor
);
400 trace_class_fragment
["packet-header-field-class"] = field_visitor
.move_fragment();
403 append_metadata_fragment(trace_class_fragment
);
406 void lsc::trace_class_visitor::visit(const lst::clock_class
& clock_class
)
408 auto clock_class_fragment
= make_json_fragment("clock-class");
411 offset
.update({ { "seconds", clock_class
.offset
/ clock_class
.frequency
},
412 { "cycles", clock_class
.offset
% clock_class
.frequency
} });
414 clock_class_fragment
.update({ { "name", clock_class
.name
},
415 { "description", clock_class
.description
},
416 { "frequency", clock_class
.frequency
},
417 { "offset", std::move(offset
) } });
419 if (clock_class
.uuid
) {
420 clock_class_fragment
["uuid"] = *clock_class
.uuid
;
423 append_metadata_fragment(clock_class_fragment
);
426 void lsc::trace_class_visitor::visit(const lst::stream_class
& stream_class
)
428 auto stream_class_fragment
= make_json_fragment("data-stream-class");
430 stream_class_fragment
["id"] = stream_class
.id
;
431 if (stream_class
.default_clock_class_name
) {
432 stream_class_fragment
["default-clock-class-name"] =
433 *stream_class
.default_clock_class_name
;
436 const auto packet_context
= stream_class
.packet_context();
437 if (packet_context
) {
438 ::ctf2::field_visitor visitor
;
440 packet_context
->accept(visitor
);
441 stream_class_fragment
["packet-context-field-class"] = visitor
.move_fragment();
444 const auto event_header
= stream_class
.event_header();
446 ::ctf2::field_visitor visitor
;
448 event_header
->accept(visitor
);
449 stream_class_fragment
["event-record-header-field-class"] = visitor
.move_fragment();
452 const auto event_context
= stream_class
.event_context();
454 ::ctf2::field_visitor visitor
;
456 event_context
->accept(visitor
);
457 stream_class_fragment
["event-record-common-context-field-class"] =
458 visitor
.move_fragment();
461 append_metadata_fragment(stream_class_fragment
);
464 void lsc::trace_class_visitor::visit(const lst::event_class
& event_class
)
466 auto event_class_fragment
= make_json_fragment("event-record-class");
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
;
472 if (event_class
.payload
) {
473 ::ctf2::field_visitor visitor
;
475 event_class
.payload
->accept(visitor
);
476 event_class_fragment
["payload-field-class"] = visitor
.move_fragment();
479 append_metadata_fragment(event_class_fragment
);
482 void lsc::trace_class_visitor::append_metadata_fragment(const nlohmann::json
& fragment
) const
484 _append_metadata_fragment(record_separator
+ fragment
.dump(spaces_per_indent
).c_str());