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>
23 class bad_alloc_with_msg
: public std::bad_alloc
{
25 explicit bad_alloc_with_msg(const std::string
& msg
) : _msg(msg
)
29 virtual const char *what() const noexcept override
40 field_stats() : stats_value
{ lttng::bt2::make_value_ref(bt_value_map_create()) }
43 throw bad_alloc_with_msg(
44 "Failed to allocate memory for field_stats.stats map");
52 lttng::bt2::message_iterator_ref upstream_iterator
;
53 lttng::bt2::event_class_const_ref event_class
;
54 const lttng::bt2::value_ref stats_value
;
58 bt_value_map_foreach_entry_const_func_status
59 stats_value_print_summary(const char *key
, const bt_value
*value
, void *)
61 LTTNG_ASSERT(bt_value_is_map(value
));
63 const auto *min
= bt_value_map_borrow_entry_value_const(value
, "min");
64 LTTNG_ASSERT(min
!= nullptr);
65 const auto *max
= bt_value_map_borrow_entry_value_const(value
, "max");
66 LTTNG_ASSERT(max
!= nullptr);
68 const auto *display_base
= bt_value_map_borrow_entry_value_const(value
, "display_base");
69 auto display_base_value
= BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL
;
71 if (display_base
!= nullptr) {
72 display_base_value
= (enum bt_field_class_integer_preferred_display_base
)
73 bt_value_integer_unsigned_get(display_base
);
76 LTTNG_ASSERT(bt_value_get_type(min
) == bt_value_get_type(max
));
78 switch (bt_value_get_type(min
)) {
79 case BT_VALUE_TYPE_STRING
:
80 fmt::print("{} \"{}\" \"{}\"\n",
82 bt_value_string_get(min
),
83 bt_value_string_get(max
));
85 case BT_VALUE_TYPE_UNSIGNED_INTEGER
:
86 switch (display_base_value
) {
87 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL
:
88 std::cout
<< lttng::format("{} 0x{:X} 0x{:X}\n",
90 bt_value_integer_unsigned_get(min
),
91 bt_value_integer_unsigned_get(max
));
94 std::cout
<< lttng::format("{} {} {}\n",
96 bt_value_integer_unsigned_get(min
),
97 bt_value_integer_unsigned_get(max
));
102 case BT_VALUE_TYPE_SIGNED_INTEGER
:
103 switch (display_base_value
) {
104 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL
:
105 std::cout
<< lttng::format("{} 0x{:X} 0x{:X}\n",
107 std::uint64_t(bt_value_integer_signed_get(min
)),
108 std::uint64_t(bt_value_integer_signed_get(max
)));
111 std::cout
<< lttng::format("{} {} {}\n",
113 bt_value_integer_signed_get(min
),
114 bt_value_integer_signed_get(max
));
119 case BT_VALUE_TYPE_REAL
:
120 std::cout
<< lttng::format(
121 "{} {:0g} {:0g}\n", key
, bt_value_real_get(min
), bt_value_real_get(max
));
127 return BT_VALUE_MAP_FOREACH_ENTRY_CONST_FUNC_STATUS_OK
;
130 void member_stats_set_min_max(bt_value
*member_map
,
131 const bt_field_class_structure_member
*member
,
132 const bt_field
*member_field
,
133 const bt_field_class
*member_class
,
134 const bt_field_class_type
*member_class_type
)
136 lttng::bt2::value_ref min
, max
, display_base
;
137 const char *name
= bt_field_class_structure_member_get_name(member
);
139 if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER
)) {
140 min
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
141 bt_field_integer_unsigned_get_value(member_field
)));
142 max
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
143 bt_field_integer_unsigned_get_value(member_field
)));
144 display_base
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
145 bt_field_class_integer_get_preferred_display_base(member_class
)));
146 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_SIGNED_INTEGER
)) {
147 min
= lttng::bt2::make_value_ref(bt_value_integer_signed_create_init(
148 bt_field_integer_signed_get_value(member_field
)));
149 max
= lttng::bt2::make_value_ref(bt_value_integer_signed_create_init(
150 bt_field_integer_signed_get_value(member_field
)));
151 display_base
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
152 bt_field_class_integer_get_preferred_display_base(member_class
)));
153 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_STRING
)) {
154 min
= lttng::bt2::make_value_ref(
155 bt_value_string_create_init(bt_field_string_get_value(member_field
)));
156 max
= lttng::bt2::make_value_ref(
157 bt_value_string_create_init(bt_field_string_get_value(member_field
)));
158 } else if (bt_field_class_type_is(*member_class_type
,
159 BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL
)) {
160 min
= lttng::bt2::make_value_ref(bt_value_real_create_init(
161 bt_field_real_double_precision_get_value(member_field
)));
162 max
= lttng::bt2::make_value_ref(bt_value_real_create_init(
163 bt_field_real_double_precision_get_value(member_field
)));
164 } else if (bt_field_class_type_is(*member_class_type
,
165 BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL
)) {
166 min
= lttng::bt2::make_value_ref(bt_value_real_create_init(
167 bt_field_real_single_precision_get_value(member_field
)));
168 max
= lttng::bt2::make_value_ref(bt_value_real_create_init(
169 bt_field_real_single_precision_get_value(member_field
)));
170 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_BIT_ARRAY
)) {
171 min
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
172 bt_field_bit_array_get_value_as_integer(member_field
)));
173 max
= lttng::bt2::make_value_ref(bt_value_integer_unsigned_create_init(
174 bt_field_bit_array_get_value_as_integer(member_field
)));
176 throw std::runtime_error(lttng::format(
177 "Unsupported field type '{}' for member '{}'", *member_class_type
, name
));
181 bt_value_map_insert_entry(member_map
, "min", min
.get());
183 throw std::runtime_error(lttng::format("No minimum value for member '{}'", name
));
187 bt_value_map_insert_entry(member_map
, "max", max
.get());
189 throw std::runtime_error(lttng::format("No maximum value for member '{}'", name
));
193 bt_value_map_insert_entry(member_map
, "display_base", display_base
.get());
197 void member_stats_update_min_max(bt_value
*member_map
,
198 const bt_field_class_structure_member
*member
,
199 const bt_field
*member_field
,
200 const bt_field_class_type
*member_class_type
)
202 const char *name
= bt_field_class_structure_member_get_name(member
);
203 bt_value
*min
= bt_value_map_borrow_entry_value(member_map
, "min");
204 bt_value
*max
= bt_value_map_borrow_entry_value(member_map
, "max");
206 if (min
== nullptr || max
== nullptr) {
207 throw std::runtime_error(
208 lttng::format("Missing min or max value for member '{}'", name
));
211 if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER
)) {
212 const auto value
= bt_field_integer_unsigned_get_value(member_field
);
214 if (value
< bt_value_integer_unsigned_get(min
)) {
215 bt_value_integer_unsigned_set(min
, value
);
218 if (value
> bt_value_integer_unsigned_get(max
)) {
219 bt_value_integer_unsigned_set(max
, value
);
221 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_SIGNED_INTEGER
)) {
222 const auto value
= bt_field_integer_signed_get_value(member_field
);
224 if (value
< bt_value_integer_signed_get(min
)) {
225 bt_value_integer_signed_set(min
, value
);
228 if (value
> bt_value_integer_signed_get(max
)) {
229 bt_value_integer_signed_set(max
, value
);
231 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_STRING
)) {
232 const auto value
= bt_field_string_get_value(member_field
);
234 if (strcmp(value
, bt_value_string_get(min
)) < 0) {
235 bt_value_string_set(min
, value
);
238 if (strcmp(value
, bt_value_string_get(max
)) > 0) {
239 bt_value_string_set(max
, value
);
241 } else if (bt_field_class_type_is(*member_class_type
,
242 BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL
)) {
243 const auto value
= bt_field_real_double_precision_get_value(member_field
);
245 if (value
< bt_value_real_get(min
)) {
246 bt_value_real_set(min
, value
);
249 if (value
> bt_value_real_get(max
)) {
250 bt_value_real_set(max
, value
);
252 } else if (bt_field_class_type_is(*member_class_type
,
253 BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL
)) {
254 const auto value
= double(bt_field_real_single_precision_get_value(member_field
));
256 if (value
< bt_value_real_get(min
)) {
257 bt_value_real_set(min
, value
);
260 if (value
> bt_value_real_get(max
)) {
261 bt_value_real_set(max
, value
);
263 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_BIT_ARRAY
)) {
264 const auto value
= bt_field_bit_array_get_value_as_integer(member_field
);
266 if (value
< bt_value_integer_unsigned_get(min
)) {
267 bt_value_integer_unsigned_set(min
, value
);
270 if (value
> bt_value_integer_unsigned_get(max
)) {
271 bt_value_integer_unsigned_set(max
, value
);
274 throw std::runtime_error(lttng::format(
275 "Unsupported field type '%{}' for member '{}'", *member_class_type
, name
));
279 bt_component_class_sink_consume_method_status
280 update_stats(const bt_message
*message
,
281 field_stats
& field_stats
,
282 bt_self_component_sink
*self_component_sink
)
284 if (bt_message_get_type(message
) != BT_MESSAGE_TYPE_EVENT
) {
285 /* It's not an error to get non-EVENT messages. */
286 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
;
289 const auto *event
= bt_message_event_borrow_event_const(message
);
290 const auto *event_payload
= bt_event_borrow_payload_field_const(event
);
291 const auto *event_class
= bt_event_borrow_class_const(event
);
292 const auto *event_payload_class
=
293 bt_event_class_borrow_payload_field_class_const(event_class
);
295 if (field_stats
.event_class
!= nullptr) {
296 LTTNG_ASSERT(event_class
== field_stats
.event_class
.get());
298 bt_event_class_get_ref(event_class
);
299 field_stats
.event_class
.reset(event_class
);
302 /* Iterate over each field in the event payload */
303 for (std::uint64_t index
= 0;
304 index
< bt_field_class_structure_get_member_count(event_payload_class
);
306 const bt_field_class_structure_member
*member
=
307 bt_field_class_structure_borrow_member_by_index_const(event_payload_class
,
309 const auto *name
= bt_field_class_structure_member_get_name(member
);
310 const auto *member_field
=
311 bt_field_structure_borrow_member_field_by_name_const(event_payload
, name
);
312 const auto *member_class
=
313 bt_field_class_structure_member_borrow_field_class_const(member
);
314 const auto member_class_type
= bt_field_class_get_type(member_class
);
316 if (bt_field_class_type_is(member_class_type
, BT_FIELD_CLASS_TYPE_ARRAY
) ||
317 bt_field_class_type_is(member_class_type
, BT_FIELD_CLASS_TYPE_STRUCTURE
)) {
318 /* Ignore array and structure field types. */
323 auto *member_map
= bt_value_map_borrow_entry_value(
324 field_stats
.stats_value
.get(), name
);
325 if (member_map
== nullptr) {
326 /* Initial creation of the value. */
327 if (bt_value_map_insert_empty_map_entry(
328 field_stats
.stats_value
.get(), name
, &member_map
) !=
329 BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
330 throw std::runtime_error(lttng::format(
331 "Failed to insert new empty map entry for field '{}'",
335 member_stats_set_min_max(member_map
,
341 /* Update the value with min/max values. */
342 member_stats_update_min_max(
343 member_map
, member
, member_field
, &member_class_type
);
345 } catch (const std::exception
& ex
) {
346 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
347 bt_self_component_sink_as_self_component(self_component_sink
),
350 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR
;
354 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
;
358 bt_component_class_initialize_method_status
359 field_stats_initialize(bt_self_component_sink
*self_component_sink
,
360 bt_self_component_sink_configuration
*,
364 if (bt_self_component_sink_add_input_port(self_component_sink
, "in", nullptr, nullptr) !=
365 BT_SELF_COMPONENT_ADD_PORT_STATUS_OK
) {
366 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
367 bt_self_component_sink_as_self_component(self_component_sink
),
368 "Failed to add input port");
369 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR
;
372 std::unique_ptr
<struct field_stats
> field_stats
;
374 field_stats
= lttng::make_unique
<struct field_stats
>();
375 } catch (const bad_alloc_with_msg
& ex
) {
376 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
377 bt_self_component_sink_as_self_component(self_component_sink
),
380 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR
;
381 } catch (const std::bad_alloc
&) {
382 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
383 bt_self_component_sink_as_self_component(self_component_sink
),
384 "Failed to allocate memory for private data");
385 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR
;
388 /* Transfer ownership to the component. */
389 bt_self_component_set_data(bt_self_component_sink_as_self_component(self_component_sink
),
390 field_stats
.release());
391 return BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK
;
394 void field_stats_finalize(bt_self_component_sink
*self_component_sink
)
396 auto *field_stats
= static_cast<struct field_stats
*>(bt_self_component_get_data(
397 bt_self_component_sink_as_self_component(self_component_sink
)));
402 bt_component_class_sink_graph_is_configured_method_status
403 field_stats_graph_is_configured(bt_self_component_sink
*self_component_sink
)
405 auto& field_stats
= *static_cast<struct field_stats
*>(bt_self_component_get_data(
406 bt_self_component_sink_as_self_component(self_component_sink
)));
408 bt_self_component_sink_borrow_input_port_by_index(self_component_sink
, 0);
410 bt_message_iterator
*raw_iterator
;
411 if (bt_message_iterator_create_from_sink_component(
412 self_component_sink
, input_port
, &raw_iterator
) !=
413 BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_OK
) {
414 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
415 bt_self_component_sink_as_self_component(self_component_sink
),
416 "input port message iterator creation failed");
417 return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_ERROR
;
420 field_stats
.upstream_iterator
.reset(raw_iterator
);
421 return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK
;
424 bt_component_class_sink_consume_method_status
425 field_stats_consume(bt_self_component_sink
*self_component_sink
)
427 auto& field_stats
= *static_cast<struct field_stats
*>(bt_self_component_get_data(
428 bt_self_component_sink_as_self_component(self_component_sink
)));
430 std::uint64_t message_count
;
431 bt_message_array_const messages
;
432 const auto next_status
= bt_message_iterator_next(
433 field_stats
.upstream_iterator
.get(), &messages
, &message_count
);
435 if (next_status
!= BT_MESSAGE_ITERATOR_NEXT_STATUS_OK
) {
436 if (next_status
== BT_MESSAGE_ITERATOR_NEXT_STATUS_END
) {
437 /* End reached, print the summary. */
438 bt_value_map_foreach_entry_const(
439 field_stats
.stats_value
.get(), stats_value_print_summary
, nullptr);
442 return static_cast<bt_component_class_sink_consume_method_status
>(next_status
);
445 for (std::uint64_t index
= 0; index
< message_count
; index
++) {
446 const auto message
= lttng::bt2::message_const_ref(messages
[index
]);
448 const auto status
= update_stats(message
.get(), field_stats
, self_component_sink
);
449 if (status
!= BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
) {
454 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
;