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 "ctf2-trace-class-visitor.hpp"
10 #include "clock-class.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(), location
.elements_
.end(),
59 std::back_inserter(location_array
));
60 return location_array
;
63 const char *get_role_name(lst::integer_type::role 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";
91 const char *get_role_name(lst::static_length_blob_type::role role
)
94 case lst::static_length_blob_type::role::METADATA_STREAM_UUID
:
95 return "metadata-stream-uuid";
102 class trace_environment_visitor
: public lst::trace_class_environment_visitor
{
104 trace_environment_visitor()
108 virtual void visit(const lst::environment_field
<int64_t>& field
) override
113 virtual void visit(const lst::environment_field
<const char *>& field
) override
118 /* Only call once. */
119 json::json
transfer_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
{
141 /* Only call once. */
142 json::json
transfer_fragment()
144 return std::move(_fragment
);
148 virtual void visit(const lst::field
& field
) override final
150 field_visitor field_type_visitor
;
151 field
.get_type().accept(field_type_visitor
);
153 _fragment
["name"] = field
.name
;
154 _fragment
["field-class"] = field_type_visitor
.transfer_fragment();
157 virtual void visit(const lst::integer_type
& type
) override final
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_
?
166 _fragment
["alignment"] = type
.alignment
;
167 _fragment
["preferred-display-base"] = (unsigned int) type
.base_
;
169 if (type
.roles_
.size() > 0) {
170 json::json role_array
= json::json::array();
172 for (const auto role
: type
.roles_
) {
173 role_array
.push_back(get_role_name(role
));
176 _fragment
["roles"] = std::move(role_array
);
180 virtual void visit(const lst::floating_point_type
& type
) override final
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_
?
187 _fragment
["alignment"] = type
.alignment
;
190 template <class EnumerationType
>
191 void visit_enumeration(const EnumerationType
& type
)
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_
?
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
) {
206 LTTNG_THROW_ERROR(fmt::format(
207 "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
, mapping
.range
.end
}};
231 _fragment
["mappings"] = std::move(mappings_value
);
234 virtual void visit(const lst::signed_enumeration_type
& type
) override final
236 visit_enumeration(type
);
239 virtual void visit(const lst::unsigned_enumeration_type
& type
) override final
241 visit_enumeration(type
);
244 virtual void visit(const lst::static_length_array_type
& type
) override final
246 _fragment
["type"] = "static-length-array";
248 ::ctf2::field_visitor element_visitor
;
249 type
.element_type
->accept(element_visitor
);
250 _fragment
["element-field-class"] = element_visitor
.transfer_fragment();
252 if (type
.alignment
!= 0) {
253 _fragment
["minimum-alignment"] = type
.alignment
;
256 _fragment
["length"] = type
.length
;
259 virtual void visit(const lst::dynamic_length_array_type
& type
) override final
261 _fragment
["type"] = "dynamic-length-array";
263 ::ctf2::field_visitor element_visitor
;
264 type
.element_type
->accept(element_visitor
);
265 _fragment
["element-field-class"] = element_visitor
.transfer_fragment();
267 if (type
.alignment
!= 0) {
268 _fragment
["minimum-alignment"] = type
.alignment
;
271 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
274 virtual void visit(const lst::static_length_blob_type
& type
) override final
276 _fragment
["type"] = "static-length-blob";
277 _fragment
["length"] = type
.length_bytes
;
279 if (type
.roles_
.size() > 0) {
280 auto role_array
= json::json::array();
282 for (const auto role
: type
.roles_
) {
283 role_array
.push_back(get_role_name(role
));
286 _fragment
["roles"] = std::move(role_array
);
290 virtual void visit(const lst::dynamic_length_blob_type
& type
) override final
292 _fragment
["type"] = "dynamic-length-blob";
293 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
296 virtual void visit(const lst::null_terminated_string_type
& type
297 __attribute__((unused
))) override final
299 _fragment
["type"] = "null-terminated-string";
302 virtual void visit(const lst::structure_type
& type
) override 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
.transfer_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
, 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
));
340 _fragment
["options"] = std::move(options_value
);
343 virtual void visit(const lst::variant_type
<int64_t>& type
) override final
348 virtual void visit(const lst::variant_type
<uint64_t>& type
) override final
353 virtual void visit(const lst::static_length_string_type
& type
) override final
355 _fragment
["type"] = "static-length-string";
356 _fragment
["length"] = type
.length
;
359 virtual void visit(const lst::dynamic_length_string_type
& type
) override final
361 _fragment
["type"] = "dynamic-length-string";
362 _fragment
["length-field-location"] = to_json(type
.length_field_location
);
365 json::json _fragment
;
367 } /* namespace ctf2 */
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
)
377 void lsc::trace_class_visitor::visit(const lst::trace_class
& trace_class
)
380 auto preamble_fragment
= make_json_fragment("preamble");
382 preamble_fragment
["version"] = 2;
383 preamble_fragment
["uuid"] = trace_class
.uuid
;
384 append_metadata_fragment(preamble_fragment
);
387 auto trace_class_fragment
= make_json_fragment("trace-class");
389 ::ctf2::trace_environment_visitor environment_visitor
;
390 trace_class
.accept(environment_visitor
);
391 trace_class_fragment
["environment"] = environment_visitor
.transfer_fragment();
393 const auto packet_header
= trace_class
.get_packet_header();
395 ::ctf2::field_visitor field_visitor
;
397 packet_header
->accept(field_visitor
);
398 trace_class_fragment
["packet-header-field-class"] = field_visitor
.transfer_fragment();
401 append_metadata_fragment(trace_class_fragment
);
404 void lsc::trace_class_visitor::visit(const lst::clock_class
& clock_class
)
406 auto clock_class_fragment
= make_json_fragment("clock-class");
409 offset
.update({{"seconds", clock_class
.offset
/ clock_class
.frequency
},
410 {"cycles", clock_class
.offset
% clock_class
.frequency
}});
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
)}});
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
.get_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
.transfer_fragment();
443 const auto event_header
= stream_class
.get_event_header();
445 ::ctf2::field_visitor visitor
;
447 event_header
->accept(visitor
);
448 stream_class_fragment
["event-record-header-field-class"] =
449 visitor
.transfer_fragment();
452 const auto event_context
= stream_class
.get_event_context();
454 ::ctf2::field_visitor visitor
;
456 event_context
->accept(visitor
);
457 stream_class_fragment
["event-record-common-context-field-class"] =
458 visitor
.transfer_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
.transfer_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());