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;
108 void visit(const lst::environment_field
<int64_t>& field
) override
113 void visit(const lst::environment_field
<const char *>& field
) override
118 /* Only call once. */
119 json::json
move_fragment()
121 return std::move(_environment
);
125 template <class FieldType
>
126 void _visit(const FieldType
& field
)
128 _environment
[field
.name
] = field
.value
;
131 json::json _environment
;
134 class field_visitor
: public lttng::sessiond::trace::field_visitor
,
135 public lttng::sessiond::trace::type_visitor
{
137 field_visitor() = default;
139 /* Only call once. */
140 json::json
move_fragment()
142 return std::move(_fragment
);
146 void visit(const lst::field
& field
) final
148 field_visitor field_type_visitor
;
149 field
.get_type().accept(field_type_visitor
);
151 _fragment
["name"] = field
.name
;
152 _fragment
["field-class"] = field_type_visitor
.move_fragment();
155 void visit(const lst::integer_type
& type
) final
157 _fragment
["type"] = type
.signedness_
== lst::integer_type::signedness::SIGNED
?
158 "fixed-length-signed-integer" :
159 "fixed-length-unsigned-integer";
160 _fragment
["length"] = type
.size
;
161 _fragment
["byte-order"] = type
.byte_order
== lst::byte_order::BIG_ENDIAN_
?
164 _fragment
["alignment"] = type
.alignment
;
165 _fragment
["preferred-display-base"] = (unsigned int) type
.base_
;
167 if (type
.roles_
.size() > 0) {
168 json::json role_array
= json::json::array();
170 for (const auto role
: type
.roles_
) {
171 role_array
.push_back(get_role_name(role
));
174 _fragment
["roles"] = std::move(role_array
);
178 void visit(const lst::floating_point_type
& type
) final
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_
?
185 _fragment
["alignment"] = type
.alignment
;
188 template <class EnumerationType
>
189 void visit_enumeration(const EnumerationType
& type
)
193 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_
?
200 _fragment
["alignment"] = type
.alignment
;
201 _fragment
["preferred-display-base"] = (unsigned int) type
.base_
;
203 if (type
.roles_
.size() > 0) {
204 if (std::is_signed
<typename
EnumerationType::mapping::range_t::
205 range_integer_t
>::value
) {
207 fmt::format("Failed to serialize {}: unexpected role",
211 auto role_array
= json::json::array();
213 for (const auto role
: type
.roles_
) {
214 role_array
.push_back(get_role_name(role
));
217 _fragment
["roles"] = std::move(role_array
);
220 if (type
.mappings_
->size() < 1) {
221 LTTNG_THROW_ERROR(fmt::format(
222 "Failed to serialize {}: enumeration must have at least one mapping",
226 json::json mappings_value
;
227 for (const auto& mapping
: *type
.mappings_
) {
228 mappings_value
[mapping
.name
] = { { mapping
.range
.begin
,
229 mapping
.range
.end
} };
232 _fragment
["mappings"] = std::move(mappings_value
);
235 void visit(const lst::signed_enumeration_type
& type
) final
237 visit_enumeration(type
);
240 void visit(const lst::unsigned_enumeration_type
& type
) final
242 visit_enumeration(type
);
245 void visit(const lst::static_length_array_type
& type
) final
247 _fragment
["type"] = "static-length-array";
249 ::ctf2::field_visitor element_visitor
;
250 type
.element_type
->accept(element_visitor
);
251 _fragment
["element-field-class"] = element_visitor
.move_fragment();
253 if (type
.alignment
!= 0) {
254 _fragment
["minimum-alignment"] = type
.alignment
;
257 _fragment
["length"] = type
.length
;
260 void visit(const lst::dynamic_length_array_type
& type
) final
262 _fragment
["type"] = "dynamic-length-array";
264 ::ctf2::field_visitor element_visitor
;
265 type
.element_type
->accept(element_visitor
);
266 _fragment
["element-field-class"] = element_visitor
.move_fragment();
268 if (type
.alignment
!= 0) {
269 _fragment
["minimum-alignment"] = type
.alignment
;
272 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
275 void visit(const lst::static_length_blob_type
& type
) final
277 _fragment
["type"] = "static-length-blob";
278 _fragment
["length"] = type
.length_bytes
;
280 if (type
.roles_
.size() > 0) {
281 auto role_array
= json::json::array();
283 for (const auto role
: type
.roles_
) {
284 role_array
.push_back(get_role_name(role
));
287 _fragment
["roles"] = std::move(role_array
);
291 void visit(const lst::dynamic_length_blob_type
& type
) final
293 _fragment
["type"] = "dynamic-length-blob";
294 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
297 void visit(const lst::null_terminated_string_type
& type
__attribute__((unused
))) final
299 _fragment
["type"] = "null-terminated-string";
302 void visit(const lst::structure_type
& type
) final
304 _fragment
["type"] = "structure";
306 if (type
.alignment
!= 0) {
307 _fragment
["minimum-alignment"] = type
.alignment
;
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
;
315 field
->accept(member_visitor
);
316 member_classes_value
.emplace_back(member_visitor
.move_fragment());
319 _fragment
["member-classes"] = std::move(member_classes_value
);
322 template <class MappingIntegerType
>
323 void visit_variant(const lst::variant_type
<MappingIntegerType
>& type
)
325 _fragment
["type"] = "variant";
326 _fragment
["selector-field-location"] = to_json(type
.selector_field_location
);
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
;
333 /* TODO missing selector-field-range. */
334 member_class
["selector-field-ranges"] = { { option
.first
.range
.begin
,
335 option
.first
.range
.end
} };
336 option
.second
->accept(option_visitor
);
337 member_class
["field-class"] = option_visitor
.move_fragment();
338 options_value
.emplace_back(std::move(member_class
));
341 _fragment
["options"] = std::move(options_value
);
344 void visit(const lst::variant_type
<int64_t>& type
) final
349 void visit(const lst::variant_type
<uint64_t>& type
) final
354 void visit(const lst::static_length_string_type
& type
) final
356 _fragment
["type"] = "static-length-string";
357 _fragment
["length"] = type
.length
;
360 void visit(const lst::dynamic_length_string_type
& type
) final
362 _fragment
["type"] = "dynamic-length-string";
363 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
366 json::json _fragment
;
368 } /* namespace ctf2 */
372 lsc::trace_class_visitor::trace_class_visitor(
373 lsc::append_metadata_fragment_function append_metadata_fragment
) :
374 _append_metadata_fragment(std::move(append_metadata_fragment
))
378 void lsc::trace_class_visitor::visit(const lst::trace_class
& trace_class
)
381 auto preamble_fragment
= make_json_fragment("preamble");
383 preamble_fragment
["version"] = 2;
384 preamble_fragment
["uuid"] = trace_class
.uuid
;
385 append_metadata_fragment(preamble_fragment
);
388 auto trace_class_fragment
= make_json_fragment("trace-class");
390 ::ctf2::trace_environment_visitor environment_visitor
;
391 trace_class
.accept(environment_visitor
);
392 trace_class_fragment
["environment"] = environment_visitor
.move_fragment();
394 const auto packet_header
= trace_class
.packet_header();
396 ::ctf2::field_visitor field_visitor
;
398 packet_header
->accept(field_visitor
);
399 trace_class_fragment
["packet-header-field-class"] = field_visitor
.move_fragment();
402 append_metadata_fragment(trace_class_fragment
);
405 void lsc::trace_class_visitor::visit(const lst::clock_class
& clock_class
)
407 auto clock_class_fragment
= make_json_fragment("clock-class");
410 offset
.update({ { "seconds", clock_class
.offset
/ clock_class
.frequency
},
411 { "cycles", clock_class
.offset
% clock_class
.frequency
} });
413 clock_class_fragment
.update({ { "name", clock_class
.name
},
414 { "description", clock_class
.description
},
415 { "frequency", clock_class
.frequency
},
416 { "offset", std::move(offset
) } });
418 if (clock_class
.uuid
) {
419 clock_class_fragment
["uuid"] = *clock_class
.uuid
;
422 append_metadata_fragment(clock_class_fragment
);
425 void lsc::trace_class_visitor::visit(const lst::stream_class
& stream_class
)
427 auto stream_class_fragment
= make_json_fragment("data-stream-class");
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
;
435 const auto packet_context
= stream_class
.packet_context();
436 if (packet_context
) {
437 ::ctf2::field_visitor visitor
;
439 packet_context
->accept(visitor
);
440 stream_class_fragment
["packet-context-field-class"] = visitor
.move_fragment();
443 const auto event_header
= stream_class
.event_header();
445 ::ctf2::field_visitor visitor
;
447 event_header
->accept(visitor
);
448 stream_class_fragment
["event-record-header-field-class"] = visitor
.move_fragment();
451 const auto event_context
= stream_class
.event_context();
453 ::ctf2::field_visitor visitor
;
455 event_context
->accept(visitor
);
456 stream_class_fragment
["event-record-common-context-field-class"] =
457 visitor
.move_fragment();
460 append_metadata_fragment(stream_class_fragment
);
463 void lsc::trace_class_visitor::visit(const lst::event_class
& event_class
)
465 auto event_class_fragment
= make_json_fragment("event-record-class");
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
;
471 if (event_class
.payload
) {
472 ::ctf2::field_visitor visitor
;
474 event_class
.payload
->accept(visitor
);
475 event_class_fragment
["payload-field-class"] = visitor
.move_fragment();
478 append_metadata_fragment(event_class_fragment
);
481 void lsc::trace_class_visitor::append_metadata_fragment(const nlohmann::json
& fragment
) const
483 _append_metadata_fragment(record_separator
+ fragment
.dump(spaces_per_indent
).c_str());