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>
19 namespace lsc
= lttng::sessiond::ctf2
;
20 namespace lst
= lttng::sessiond::trace
;
22 namespace json
= nlohmann
;
25 const unsigned int spaces_per_indent
= 2;
26 const std::string record_separator
= "\x1e";
28 json::json
make_json_fragment(const char *type
)
30 return { { "type", type
} };
33 json::json
to_json(const lst::field_location
& location
)
35 json::json location_array
;
37 switch (location
.root_
) {
38 case lst::field_location::root::PACKET_HEADER
:
39 location_array
.push_back("packet-header");
41 case lst::field_location::root::PACKET_CONTEXT
:
42 location_array
.push_back("packet-context");
44 case lst::field_location::root::EVENT_RECORD_HEADER
:
45 location_array
.push_back("event-record-header");
47 case lst::field_location::root::EVENT_RECORD_COMMON_CONTEXT
:
48 location_array
.push_back("event-record-common-context");
50 case lst::field_location::root::EVENT_RECORD_SPECIFIC_CONTEXT
:
51 location_array
.push_back("event-record-specific-context");
53 case lst::field_location::root::EVENT_RECORD_PAYLOAD
:
54 location_array
.push_back("event-record-payload");
58 std::copy(location
.elements_
.begin(),
59 location
.elements_
.end(),
60 std::back_inserter(location_array
));
61 return location_array
;
64 const char *get_role_name(lst::integer_type::role 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";
92 const char *get_role_name(lst::static_length_blob_type::role role
)
95 case lst::static_length_blob_type::role::METADATA_STREAM_UUID
:
96 return "metadata-stream-uuid";
103 class trace_environment_visitor
: public lst::trace_class_environment_visitor
{
105 trace_environment_visitor()
109 virtual void visit(const lst::environment_field
<int64_t>& field
) override
114 virtual 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
{
142 /* Only call once. */
143 json::json
move_fragment()
145 return std::move(_fragment
);
149 virtual void visit(const lst::field
& field
) override final
151 field_visitor field_type_visitor
;
152 field
.get_type().accept(field_type_visitor
);
154 _fragment
["name"] = field
.name
;
155 _fragment
["field-class"] = field_type_visitor
.move_fragment();
158 virtual void visit(const lst::integer_type
& type
) override final
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_
?
167 _fragment
["alignment"] = type
.alignment
;
168 _fragment
["preferred-display-base"] = (unsigned int) type
.base_
;
170 if (type
.roles_
.size() > 0) {
171 json::json role_array
= json::json::array();
173 for (const auto role
: type
.roles_
) {
174 role_array
.push_back(get_role_name(role
));
177 _fragment
["roles"] = std::move(role_array
);
181 virtual void visit(const lst::floating_point_type
& type
) override final
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_
?
188 _fragment
["alignment"] = type
.alignment
;
191 template <class EnumerationType
>
192 void visit_enumeration(const EnumerationType
& type
)
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_
?
203 _fragment
["alignment"] = type
.alignment
;
204 _fragment
["preferred-display-base"] = (unsigned int) type
.base_
;
206 if (type
.roles_
.size() > 0) {
207 if (std::is_signed
<typename
EnumerationType::mapping::range_t::
208 range_integer_t
>::value
) {
210 fmt::format("Failed to serialize {}: unexpected role",
214 auto role_array
= json::json::array();
216 for (const auto role
: type
.roles_
) {
217 role_array
.push_back(get_role_name(role
));
220 _fragment
["roles"] = std::move(role_array
);
223 if (type
.mappings_
->size() < 1) {
224 LTTNG_THROW_ERROR(fmt::format(
225 "Failed to serialize {}: enumeration must have at least one mapping",
229 json::json mappings_value
;
230 for (const auto& mapping
: *type
.mappings_
) {
231 mappings_value
[mapping
.name
] = { { mapping
.range
.begin
,
232 mapping
.range
.end
} };
235 _fragment
["mappings"] = std::move(mappings_value
);
238 virtual void visit(const lst::signed_enumeration_type
& type
) override final
240 visit_enumeration(type
);
243 virtual void visit(const lst::unsigned_enumeration_type
& type
) override final
245 visit_enumeration(type
);
248 virtual void visit(const lst::static_length_array_type
& type
) override final
250 _fragment
["type"] = "static-length-array";
252 ::ctf2::field_visitor element_visitor
;
253 type
.element_type
->accept(element_visitor
);
254 _fragment
["element-field-class"] = element_visitor
.move_fragment();
256 if (type
.alignment
!= 0) {
257 _fragment
["minimum-alignment"] = type
.alignment
;
260 _fragment
["length"] = type
.length
;
263 virtual void visit(const lst::dynamic_length_array_type
& type
) override final
265 _fragment
["type"] = "dynamic-length-array";
267 ::ctf2::field_visitor element_visitor
;
268 type
.element_type
->accept(element_visitor
);
269 _fragment
["element-field-class"] = element_visitor
.move_fragment();
271 if (type
.alignment
!= 0) {
272 _fragment
["minimum-alignment"] = type
.alignment
;
275 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
278 virtual void visit(const lst::static_length_blob_type
& type
) override final
280 _fragment
["type"] = "static-length-blob";
281 _fragment
["length"] = type
.length_bytes
;
283 if (type
.roles_
.size() > 0) {
284 auto role_array
= json::json::array();
286 for (const auto role
: type
.roles_
) {
287 role_array
.push_back(get_role_name(role
));
290 _fragment
["roles"] = std::move(role_array
);
294 virtual void visit(const lst::dynamic_length_blob_type
& type
) override final
296 _fragment
["type"] = "dynamic-length-blob";
297 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
300 virtual void visit(const lst::null_terminated_string_type
& type
301 __attribute__((unused
))) override final
303 _fragment
["type"] = "null-terminated-string";
306 virtual void visit(const lst::structure_type
& type
) override final
308 _fragment
["type"] = "structure";
310 if (type
.alignment
!= 0) {
311 _fragment
["minimum-alignment"] = type
.alignment
;
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
;
319 field
->accept(member_visitor
);
320 member_classes_value
.emplace_back(member_visitor
.move_fragment());
323 _fragment
["member-classes"] = std::move(member_classes_value
);
326 template <class MappingIntegerType
>
327 void visit_variant(const lst::variant_type
<MappingIntegerType
>& type
)
329 _fragment
["type"] = "variant";
330 _fragment
["selector-field-location"] = to_json(type
.selector_field_location
);
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
;
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
));
345 _fragment
["options"] = std::move(options_value
);
348 virtual void visit(const lst::variant_type
<int64_t>& type
) override final
353 virtual void visit(const lst::variant_type
<uint64_t>& type
) override final
358 virtual void visit(const lst::static_length_string_type
& type
) override final
360 _fragment
["type"] = "static-length-string";
361 _fragment
["length"] = type
.length
;
364 virtual void visit(const lst::dynamic_length_string_type
& type
) override final
366 _fragment
["type"] = "dynamic-length-string";
367 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
370 json::json _fragment
;
372 } /* namespace ctf2 */
376 lsc::trace_class_visitor::trace_class_visitor(
377 lsc::append_metadata_fragment_function append_metadata_fragment
) :
378 _append_metadata_fragment(append_metadata_fragment
)
382 void lsc::trace_class_visitor::visit(const lst::trace_class
& trace_class
)
385 auto preamble_fragment
= make_json_fragment("preamble");
387 preamble_fragment
["version"] = 2;
388 preamble_fragment
["uuid"] = trace_class
.uuid
;
389 append_metadata_fragment(preamble_fragment
);
392 auto trace_class_fragment
= make_json_fragment("trace-class");
394 ::ctf2::trace_environment_visitor environment_visitor
;
395 trace_class
.accept(environment_visitor
);
396 trace_class_fragment
["environment"] = environment_visitor
.move_fragment();
398 const auto packet_header
= trace_class
.packet_header();
400 ::ctf2::field_visitor field_visitor
;
402 packet_header
->accept(field_visitor
);
403 trace_class_fragment
["packet-header-field-class"] = field_visitor
.move_fragment();
406 append_metadata_fragment(trace_class_fragment
);
409 void lsc::trace_class_visitor::visit(const lst::clock_class
& clock_class
)
411 auto clock_class_fragment
= make_json_fragment("clock-class");
414 offset
.update({ { "seconds", clock_class
.offset
/ clock_class
.frequency
},
415 { "cycles", clock_class
.offset
% clock_class
.frequency
} });
417 clock_class_fragment
.update({ { "name", clock_class
.name
},
418 { "description", clock_class
.description
},
419 { "frequency", clock_class
.frequency
},
420 { "offset", std::move(offset
) } });
422 if (clock_class
.uuid
) {
423 clock_class_fragment
["uuid"] = *clock_class
.uuid
;
426 append_metadata_fragment(clock_class_fragment
);
429 void lsc::trace_class_visitor::visit(const lst::stream_class
& stream_class
)
431 auto stream_class_fragment
= make_json_fragment("data-stream-class");
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
;
439 const auto packet_context
= stream_class
.packet_context();
440 if (packet_context
) {
441 ::ctf2::field_visitor visitor
;
443 packet_context
->accept(visitor
);
444 stream_class_fragment
["packet-context-field-class"] = visitor
.move_fragment();
447 const auto event_header
= stream_class
.event_header();
449 ::ctf2::field_visitor visitor
;
451 event_header
->accept(visitor
);
452 stream_class_fragment
["event-record-header-field-class"] = visitor
.move_fragment();
455 const auto event_context
= stream_class
.event_context();
457 ::ctf2::field_visitor visitor
;
459 event_context
->accept(visitor
);
460 stream_class_fragment
["event-record-common-context-field-class"] =
461 visitor
.move_fragment();
464 append_metadata_fragment(stream_class_fragment
);
467 void lsc::trace_class_visitor::visit(const lst::event_class
& event_class
)
469 auto event_class_fragment
= make_json_fragment("event-record-class");
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
;
475 if (event_class
.payload
) {
476 ::ctf2::field_visitor visitor
;
478 event_class
.payload
->accept(visitor
);
479 event_class_fragment
["payload-field-class"] = visitor
.move_fragment();
482 append_metadata_fragment(event_class_fragment
);
485 void lsc::trace_class_visitor::append_metadata_fragment(const nlohmann::json
& fragment
) const
487 _append_metadata_fragment(record_separator
+ fragment
.dump(spaces_per_indent
).c_str());