2 * Copyright (C) 2023 Kienan Stewart <kstewart@efficios.com>
4 * SPDX-License-Identifier: LGPL-2.1-only
9 #include "../utils.hpp"
10 #include "field_stats.hpp"
12 #include <common/make-unique-wrapper.hpp>
13 #include <common/make-unique.hpp>
16 #include <babeltrace2/babeltrace.h>
24 class bad_alloc_with_msg
: public std::bad_alloc
{
26 explicit bad_alloc_with_msg(std::string msg
) : _msg(std::move(msg
))
30 const char *what() const noexcept override
41 field_stats() : stats_value
{ lttng::bt2::make_value_ref(bt_value_map_create()) }
44 throw bad_alloc_with_msg(
45 "Failed to allocate memory for field_stats.stats map");
49 ~field_stats() = default;
51 lttng::bt2::message_iterator_ref upstream_iterator
;
52 lttng::bt2::event_class_const_ref event_class
;
53 const lttng::bt2::value_ref stats_value
;
57 bt_value_map_foreach_entry_const_func_status
58 stats_value_print_summary(const char *key
, const bt_value
*value
, void *)
60 LTTNG_ASSERT(bt_value_is_map(value
));
62 const auto *min
= bt_value_map_borrow_entry_value_const(value
, "min");
63 LTTNG_ASSERT(min
!= nullptr);
64 const auto *max
= bt_value_map_borrow_entry_value_const(value
, "max");
65 LTTNG_ASSERT(max
!= nullptr);
67 const auto *display_base
= bt_value_map_borrow_entry_value_const(value
, "display_base");
68 auto display_base_value
= BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL
;
70 if (display_base
!= nullptr) {
71 display_base_value
= (enum bt_field_class_integer_preferred_display_base
)
72 bt_value_integer_unsigned_get(display_base
);
75 LTTNG_ASSERT(bt_value_get_type(min
) == bt_value_get_type(max
));
77 switch (bt_value_get_type(min
)) {
78 case BT_VALUE_TYPE_STRING
:
79 fmt::print("{} \"{}\" \"{}\"\n",
81 bt_value_string_get(min
),
82 bt_value_string_get(max
));
84 case BT_VALUE_TYPE_UNSIGNED_INTEGER
:
85 switch (display_base_value
) {
86 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL
:
87 std::cout
<< lttng::format("{} 0x{:X} 0x{:X}\n",
89 bt_value_integer_unsigned_get(min
),
90 bt_value_integer_unsigned_get(max
));
93 std::cout
<< lttng::format("{} {} {}\n",
95 bt_value_integer_unsigned_get(min
),
96 bt_value_integer_unsigned_get(max
));
101 case BT_VALUE_TYPE_SIGNED_INTEGER
:
102 switch (display_base_value
) {
103 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL
:
104 std::cout
<< lttng::format("{} 0x{:X} 0x{:X}\n",
106 std::uint64_t(bt_value_integer_signed_get(min
)),
107 std::uint64_t(bt_value_integer_signed_get(max
)));
110 std::cout
<< lttng::format("{} {} {}\n",
112 bt_value_integer_signed_get(min
),
113 bt_value_integer_signed_get(max
));
118 case BT_VALUE_TYPE_REAL
:
119 std::cout
<< lttng::format(
120 "{} {:0g} {:0g}\n", key
, bt_value_real_get(min
), bt_value_real_get(max
));
126 return BT_VALUE_MAP_FOREACH_ENTRY_CONST_FUNC_STATUS_OK
;
129 void member_stats_set_min_max(bt_value
*member_map
,
130 const bt_field_class_structure_member
*member
,
131 const bt_field
*member_field
,
132 const bt_field_class
*member_class
,
133 const bt_field_class_type
*member_class_type
)
135 lttng::bt2::value_ref min
, max
, display_base
;
136 const char *name
= bt_field_class_structure_member_get_name(member
);
138 if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER
)) {
139 min
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
140 bt_field_integer_unsigned_get_value(member_field
)));
141 max
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
142 bt_field_integer_unsigned_get_value(member_field
)));
143 display_base
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
144 bt_field_class_integer_get_preferred_display_base(member_class
)));
145 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_SIGNED_INTEGER
)) {
146 min
= lttng::bt2::make_value_ref(bt_value_integer_signed_create_init(
147 bt_field_integer_signed_get_value(member_field
)));
148 max
= lttng::bt2::make_value_ref(bt_value_integer_signed_create_init(
149 bt_field_integer_signed_get_value(member_field
)));
150 display_base
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
151 bt_field_class_integer_get_preferred_display_base(member_class
)));
152 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_STRING
)) {
153 min
= lttng::bt2::make_value_ref(
154 bt_value_string_create_init(bt_field_string_get_value(member_field
)));
155 max
= lttng::bt2::make_value_ref(
156 bt_value_string_create_init(bt_field_string_get_value(member_field
)));
157 } else if (bt_field_class_type_is(*member_class_type
,
158 BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL
)) {
159 min
= lttng::bt2::make_value_ref(bt_value_real_create_init(
160 bt_field_real_double_precision_get_value(member_field
)));
161 max
= lttng::bt2::make_value_ref(bt_value_real_create_init(
162 bt_field_real_double_precision_get_value(member_field
)));
163 } else if (bt_field_class_type_is(*member_class_type
,
164 BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL
)) {
165 min
= lttng::bt2::make_value_ref(bt_value_real_create_init(
166 bt_field_real_single_precision_get_value(member_field
)));
167 max
= lttng::bt2::make_value_ref(bt_value_real_create_init(
168 bt_field_real_single_precision_get_value(member_field
)));
169 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_BIT_ARRAY
)) {
170 min
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
171 bt_field_bit_array_get_value_as_integer(member_field
)));
172 max
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
173 bt_field_bit_array_get_value_as_integer(member_field
)));
175 throw std::runtime_error(lttng::format(
176 "Unsupported field type '{}' for member '{}'", *member_class_type
, name
));
180 bt_value_map_insert_entry(member_map
, "min", min
.get());
182 throw std::runtime_error(lttng::format("No minimum value for member '{}'", name
));
186 bt_value_map_insert_entry(member_map
, "max", max
.get());
188 throw std::runtime_error(lttng::format("No maximum value for member '{}'", name
));
192 bt_value_map_insert_entry(member_map
, "display_base", display_base
.get());
196 void member_stats_update_min_max(bt_value
*member_map
,
197 const bt_field_class_structure_member
*member
,
198 const bt_field
*member_field
,
199 const bt_field_class_type
*member_class_type
)
201 const char *name
= bt_field_class_structure_member_get_name(member
);
202 bt_value
*min
= bt_value_map_borrow_entry_value(member_map
, "min");
203 bt_value
*max
= bt_value_map_borrow_entry_value(member_map
, "max");
205 if (min
== nullptr || max
== nullptr) {
206 throw std::runtime_error(
207 lttng::format("Missing min or max value for member '{}'", name
));
210 if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER
)) {
211 const auto value
= bt_field_integer_unsigned_get_value(member_field
);
213 if (value
< bt_value_integer_unsigned_get(min
)) {
214 bt_value_integer_unsigned_set(min
, value
);
217 if (value
> bt_value_integer_unsigned_get(max
)) {
218 bt_value_integer_unsigned_set(max
, value
);
220 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_SIGNED_INTEGER
)) {
221 const auto value
= bt_field_integer_signed_get_value(member_field
);
223 if (value
< bt_value_integer_signed_get(min
)) {
224 bt_value_integer_signed_set(min
, value
);
227 if (value
> bt_value_integer_signed_get(max
)) {
228 bt_value_integer_signed_set(max
, value
);
230 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_STRING
)) {
231 const auto value
= bt_field_string_get_value(member_field
);
233 if (strcmp(value
, bt_value_string_get(min
)) < 0) {
234 bt_value_string_set(min
, value
);
237 if (strcmp(value
, bt_value_string_get(max
)) > 0) {
238 bt_value_string_set(max
, value
);
240 } else if (bt_field_class_type_is(*member_class_type
,
241 BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL
)) {
242 const auto value
= bt_field_real_double_precision_get_value(member_field
);
244 if (value
< bt_value_real_get(min
)) {
245 bt_value_real_set(min
, value
);
248 if (value
> bt_value_real_get(max
)) {
249 bt_value_real_set(max
, value
);
251 } else if (bt_field_class_type_is(*member_class_type
,
252 BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL
)) {
253 const auto value
= double(bt_field_real_single_precision_get_value(member_field
));
255 if (value
< bt_value_real_get(min
)) {
256 bt_value_real_set(min
, value
);
259 if (value
> bt_value_real_get(max
)) {
260 bt_value_real_set(max
, value
);
262 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_BIT_ARRAY
)) {
263 const auto value
= bt_field_bit_array_get_value_as_integer(member_field
);
265 if (value
< bt_value_integer_unsigned_get(min
)) {
266 bt_value_integer_unsigned_set(min
, value
);
269 if (value
> bt_value_integer_unsigned_get(max
)) {
270 bt_value_integer_unsigned_set(max
, value
);
273 throw std::runtime_error(lttng::format(
274 "Unsupported field type '%{}' for member '{}'", *member_class_type
, name
));
278 bt_component_class_sink_consume_method_status
279 update_stats(const bt_message
*message
,
280 field_stats
& field_stats
,
281 bt_self_component_sink
*self_component_sink
)
283 if (bt_message_get_type(message
) != BT_MESSAGE_TYPE_EVENT
) {
284 /* It's not an error to get non-EVENT messages. */
285 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
;
288 const auto *event
= bt_message_event_borrow_event_const(message
);
289 const auto *event_payload
= bt_event_borrow_payload_field_const(event
);
290 const auto *event_class
= bt_event_borrow_class_const(event
);
291 const auto *event_payload_class
=
292 bt_event_class_borrow_payload_field_class_const(event_class
);
294 if (field_stats
.event_class
!= nullptr) {
295 LTTNG_ASSERT(event_class
== field_stats
.event_class
.get());
297 bt_event_class_get_ref(event_class
);
298 field_stats
.event_class
.reset(event_class
);
301 /* Iterate over each field in the event payload */
302 for (std::uint64_t index
= 0;
303 index
< bt_field_class_structure_get_member_count(event_payload_class
);
305 const bt_field_class_structure_member
*member
=
306 bt_field_class_structure_borrow_member_by_index_const(event_payload_class
,
308 const auto *name
= bt_field_class_structure_member_get_name(member
);
309 const auto *member_field
=
310 bt_field_structure_borrow_member_field_by_name_const(event_payload
, name
);
311 const auto *member_class
=
312 bt_field_class_structure_member_borrow_field_class_const(member
);
313 const auto member_class_type
= bt_field_class_get_type(member_class
);
315 if (bt_field_class_type_is(member_class_type
, BT_FIELD_CLASS_TYPE_ARRAY
) ||
316 bt_field_class_type_is(member_class_type
, BT_FIELD_CLASS_TYPE_STRUCTURE
)) {
317 /* Ignore array and structure field types. */
322 auto *member_map
= bt_value_map_borrow_entry_value(
323 field_stats
.stats_value
.get(), name
);
324 if (member_map
== nullptr) {
325 /* Initial creation of the value. */
326 if (bt_value_map_insert_empty_map_entry(
327 field_stats
.stats_value
.get(), name
, &member_map
) !=
328 BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
329 throw std::runtime_error(lttng::format(
330 "Failed to insert new empty map entry for field '{}'",
334 member_stats_set_min_max(member_map
,
340 /* Update the value with min/max values. */
341 member_stats_update_min_max(
342 member_map
, member
, member_field
, &member_class_type
);
344 } catch (const std::exception
& ex
) {
345 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
346 bt_self_component_sink_as_self_component(self_component_sink
),
349 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR
;
353 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
;
357 bt_component_class_initialize_method_status
358 field_stats_initialize(bt_self_component_sink
*self_component_sink
,
359 bt_self_component_sink_configuration
*,
363 if (bt_self_component_sink_add_input_port(self_component_sink
, "in", nullptr, nullptr) !=
364 BT_SELF_COMPONENT_ADD_PORT_STATUS_OK
) {
365 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
366 bt_self_component_sink_as_self_component(self_component_sink
),
367 "Failed to add input port");
368 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR
;
371 std::unique_ptr
<struct field_stats
> field_stats
;
373 field_stats
= lttng::make_unique
<struct field_stats
>();
374 } catch (const bad_alloc_with_msg
& ex
) {
375 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
376 bt_self_component_sink_as_self_component(self_component_sink
),
379 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR
;
380 } catch (const std::bad_alloc
&) {
381 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
382 bt_self_component_sink_as_self_component(self_component_sink
),
383 "Failed to allocate memory for private data");
384 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR
;
387 /* Transfer ownership to the component. */
388 bt_self_component_set_data(bt_self_component_sink_as_self_component(self_component_sink
),
389 field_stats
.release());
390 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK
;
393 void field_stats_finalize(bt_self_component_sink
*self_component_sink
)
395 auto *field_stats
= static_cast<struct field_stats
*>(bt_self_component_get_data(
396 bt_self_component_sink_as_self_component(self_component_sink
)));
401 bt_component_class_sink_graph_is_configured_method_status
402 field_stats_graph_is_configured(bt_self_component_sink
*self_component_sink
)
404 auto& field_stats
= *static_cast<struct field_stats
*>(bt_self_component_get_data(
405 bt_self_component_sink_as_self_component(self_component_sink
)));
407 bt_self_component_sink_borrow_input_port_by_index(self_component_sink
, 0);
409 bt_message_iterator
*raw_iterator
;
410 if (bt_message_iterator_create_from_sink_component(
411 self_component_sink
, input_port
, &raw_iterator
) !=
412 BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_OK
) {
413 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
414 bt_self_component_sink_as_self_component(self_component_sink
),
415 "input port message iterator creation failed");
416 return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_ERROR
;
419 field_stats
.upstream_iterator
.reset(raw_iterator
);
420 return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK
;
423 bt_component_class_sink_consume_method_status
424 field_stats_consume(bt_self_component_sink
*self_component_sink
)
426 auto& field_stats
= *static_cast<struct field_stats
*>(bt_self_component_get_data(
427 bt_self_component_sink_as_self_component(self_component_sink
)));
429 std::uint64_t message_count
;
430 bt_message_array_const messages
;
431 const auto next_status
= bt_message_iterator_next(
432 field_stats
.upstream_iterator
.get(), &messages
, &message_count
);
434 if (next_status
!= BT_MESSAGE_ITERATOR_NEXT_STATUS_OK
) {
435 if (next_status
== BT_MESSAGE_ITERATOR_NEXT_STATUS_END
) {
436 /* End reached, print the summary. */
437 bt_value_map_foreach_entry_const(
438 field_stats
.stats_value
.get(), stats_value_print_summary
, nullptr);
441 return static_cast<bt_component_class_sink_consume_method_status
>(next_status
);
444 for (std::uint64_t index
= 0; index
< message_count
; index
++) {
445 const auto message
= lttng::bt2::message_const_ref(messages
[index
]);
447 const auto status
= update_stats(message
.get(), field_stats
, self_component_sink
);
448 if (status
!= BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
) {
453 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
;