2 * Copyright (C) 2023 Kienan Stewart <kstewart@efficios.com>
4 * SPDX-License-Identifier: LGPL-2.1-only
9 #include "field_stats.hpp"
12 #include <babeltrace2/babeltrace.h>
18 bt_message_iterator
*iterator
;
19 bt_value
*stats_value
;
20 const bt_event_class
*event_class
;
23 bt_component_class_initialize_method_status
24 field_stats_initialize(bt_self_component_sink
*self_component_sink
,
25 bt_self_component_sink_configuration
*,
29 bt_component_class_initialize_method_status status
;
30 struct field_stats
*field_stats
= nullptr;
32 if (bt_self_component_sink_add_input_port(self_component_sink
, "in", nullptr, nullptr) !=
33 BT_SELF_COMPONENT_ADD_PORT_STATUS_OK
) {
34 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
35 bt_self_component_sink_as_self_component(self_component_sink
),
36 "Failed to add input port");
37 status
= BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR
;
41 field_stats
= (struct field_stats
*) malloc(sizeof(*field_stats
));
42 if (field_stats
== nullptr) {
43 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
44 bt_self_component_sink_as_self_component(self_component_sink
),
45 "Failed to allocate memory for private data");
46 status
= BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_MEMORY_ERROR
;
50 field_stats
->iterator
= nullptr;
51 field_stats
->stats_value
= bt_value_map_create();
52 field_stats
->event_class
= nullptr;
53 if (field_stats
->stats_value
== nullptr) {
54 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
55 bt_self_component_sink_as_self_component(self_component_sink
),
56 "Failed to allocate memory for field_stats.stats map");
57 status
= BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_ERROR
;
60 bt_self_component_set_data(bt_self_component_sink_as_self_component(self_component_sink
),
62 status
= BT_COMPONENT_CLASS_INITIALIZE_METHOD_STATUS_OK
;
73 static bt_value_map_foreach_entry_const_func_status
74 stats_value_print_summary(const char *key
, const bt_value
*value
, void *)
76 assert(bt_value_is_map(value
));
78 const bt_value
*min
= bt_value_map_borrow_entry_value_const(value
, "min");
79 const bt_value
*max
= bt_value_map_borrow_entry_value_const(value
, "max");
80 const bt_value
*display_base
= bt_value_map_borrow_entry_value_const(value
, "display_base");
81 enum bt_field_class_integer_preferred_display_base display_base_value
=
82 BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL
;
84 if (display_base
!= nullptr) {
85 display_base_value
= (enum bt_field_class_integer_preferred_display_base
)
86 bt_value_integer_unsigned_get(display_base
);
88 assert(min
!= nullptr);
89 assert(max
!= nullptr);
91 if (bt_value_is_string(min
)) {
92 fmt::print("{} \"{}\" \"{}\"\n",
94 bt_value_string_get(min
),
95 bt_value_string_get(max
));
96 } else if (bt_value_is_unsigned_integer(min
)) {
97 switch (display_base_value
) {
98 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL
:
99 fmt::print("{} 0x{:X} 0x{:X}\n",
101 bt_value_integer_unsigned_get(min
),
102 bt_value_integer_unsigned_get(max
));
105 fmt::print("{} {} {}\n",
107 bt_value_integer_unsigned_get(min
),
108 bt_value_integer_unsigned_get(max
));
111 } else if (bt_value_is_signed_integer(min
)) {
112 switch (display_base_value
) {
113 case BT_FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL
:
114 fmt::print("{} 0x{:X} 0x{:X}\n",
116 (uint64_t) bt_value_integer_signed_get(min
),
117 (uint64_t) bt_value_integer_signed_get(max
));
120 fmt::print("{} {} {}\n",
122 bt_value_integer_signed_get(min
),
123 bt_value_integer_signed_get(max
));
126 } else if (bt_value_is_real(min
)) {
127 fmt::print("{} {:0g} {:0g}\n", key
, bt_value_real_get(min
), bt_value_real_get(max
));
131 return BT_VALUE_MAP_FOREACH_ENTRY_CONST_FUNC_STATUS_OK
;
134 void field_stats_finalize(bt_self_component_sink
*self_component_sink
)
136 struct field_stats
*field_stats
= (struct field_stats
*) bt_self_component_get_data(
137 bt_self_component_sink_as_self_component(self_component_sink
));
138 bt_value_put_ref(field_stats
->stats_value
);
142 bt_component_class_sink_graph_is_configured_method_status
143 field_stats_graph_is_configured(bt_self_component_sink
*self_component_sink
)
145 struct field_stats
*field_stats
= (struct field_stats
*) bt_self_component_get_data(
146 bt_self_component_sink_as_self_component(self_component_sink
));
147 bt_self_component_port_input
*input_port
=
148 bt_self_component_sink_borrow_input_port_by_index(self_component_sink
, 0);
149 if (bt_message_iterator_create_from_sink_component(
150 self_component_sink
, input_port
, &field_stats
->iterator
) !=
151 BT_MESSAGE_ITERATOR_CREATE_FROM_SINK_COMPONENT_STATUS_OK
) {
152 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
153 bt_self_component_sink_as_self_component(self_component_sink
),
154 "input port message iterator creation failed");
155 return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_ERROR
;
158 return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK
;
161 static bt_component_class_sink_consume_method_status
162 member_stats_set_min_max(bt_value
*member_map
,
163 const bt_field_class_structure_member
*member
,
164 const bt_field
*member_field
,
165 const bt_field_class
*member_class
,
166 const bt_field_class_type
*member_class_type
,
167 bt_self_component_sink
*self_component_sink
)
169 bt_value
*min
, *max
, *display_base
= bt_value_null
;
170 const char *name
= bt_field_class_structure_member_get_name(member
);
172 if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER
)) {
173 min
= bt_value_integer_unsigned_create_init(
174 bt_field_integer_unsigned_get_value(member_field
));
175 max
= bt_value_integer_unsigned_create_init(
176 bt_field_integer_unsigned_get_value(member_field
));
177 display_base
= bt_value_integer_unsigned_create_init(
178 bt_field_class_integer_get_preferred_display_base(member_class
));
179 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_SIGNED_INTEGER
)) {
180 min
= bt_value_integer_signed_create_init(
181 bt_field_integer_signed_get_value(member_field
));
182 max
= bt_value_integer_signed_create_init(
183 bt_field_integer_signed_get_value(member_field
));
184 display_base
= bt_value_integer_unsigned_create_init(
185 bt_field_class_integer_get_preferred_display_base(member_class
));
186 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_STRING
)) {
187 min
= bt_value_string_create_init(bt_field_string_get_value(member_field
));
188 max
= bt_value_string_create_init(bt_field_string_get_value(member_field
));
189 } else if (bt_field_class_type_is(*member_class_type
,
190 BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL
)) {
191 min
= bt_value_real_create_init(
192 bt_field_real_double_precision_get_value(member_field
));
193 max
= bt_value_real_create_init(
194 bt_field_real_double_precision_get_value(member_field
));
195 } else if (bt_field_class_type_is(*member_class_type
,
196 BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL
)) {
197 min
= bt_value_real_create_init(
198 bt_field_real_single_precision_get_value(member_field
));
199 max
= bt_value_real_create_init(
200 bt_field_real_single_precision_get_value(member_field
));
201 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_BIT_ARRAY
)) {
202 min
= bt_value_integer_unsigned_create_init(
203 bt_field_bit_array_get_value_as_integer(member_field
));
204 max
= bt_value_integer_unsigned_create_init(
205 bt_field_bit_array_get_value_as_integer(member_field
));
207 const auto field_class_type_name
= fmt::to_string(*member_class_type
);
209 fmt::print("Unsupported field type for '{}': {}\n", name
, field_class_type_name
);
210 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
211 bt_self_component_sink_as_self_component(self_component_sink
),
212 "Unsupported field type '%s' for member '%s'",
213 field_class_type_name
.c_str(),
216 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR
;
219 if (min
!= nullptr) {
220 bt_value_map_insert_entry(member_map
, "min", min
);
221 bt_value_put_ref(min
);
223 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
224 bt_self_component_sink_as_self_component(self_component_sink
),
225 "No minimum value for member '%s'",
227 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR
;
229 if (max
!= nullptr) {
230 bt_value_map_insert_entry(member_map
, "max", max
);
231 bt_value_put_ref(max
);
233 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
234 bt_self_component_sink_as_self_component(self_component_sink
),
235 "No maximum value for member '%s'",
237 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR
;
239 if (display_base
!= bt_value_null
) {
240 bt_value_map_insert_entry(member_map
, "display_base", display_base
);
241 bt_value_put_ref(display_base
);
243 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
;
246 static bt_component_class_sink_consume_method_status
247 member_stats_update_min_max(bt_value
*member_map
,
248 const bt_field_class_structure_member
*member
,
249 const bt_field
*member_field
,
250 const bt_field_class_type
*member_class_type
,
251 bt_self_component_sink
*self_component_sink
)
253 const char *name
= bt_field_class_structure_member_get_name(member
);
254 bt_value
*min
= bt_value_map_borrow_entry_value(member_map
, "min");
255 bt_value
*max
= bt_value_map_borrow_entry_value(member_map
, "max");
257 if (min
== nullptr || max
== nullptr) {
258 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
259 bt_self_component_sink_as_self_component(self_component_sink
),
260 "Missing min or max value for member '%s'",
262 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR
;
265 if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_UNSIGNED_INTEGER
)) {
266 bt_value
*value
= bt_value_integer_unsigned_create_init(
267 bt_field_integer_unsigned_get_value(member_field
));
268 if (bt_value_integer_unsigned_get(value
) < bt_value_integer_unsigned_get(min
)) {
269 bt_value_integer_unsigned_set(min
, bt_value_integer_unsigned_get(value
));
271 if (bt_value_integer_unsigned_get(value
) > bt_value_integer_unsigned_get(max
)) {
272 bt_value_integer_unsigned_set(max
, bt_value_integer_unsigned_get(value
));
274 bt_value_put_ref(value
);
275 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_SIGNED_INTEGER
)) {
276 bt_value
*value
= bt_value_integer_signed_create_init(
277 bt_field_integer_signed_get_value(member_field
));
278 if (bt_value_integer_signed_get(value
) < bt_value_integer_signed_get(min
)) {
279 bt_value_integer_signed_set(min
, bt_value_integer_signed_get(value
));
281 if (bt_value_integer_signed_get(value
) > bt_value_integer_signed_get(max
)) {
282 bt_value_integer_signed_set(max
, bt_value_integer_signed_get(value
));
284 bt_value_put_ref(value
);
285 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_STRING
)) {
287 bt_value_string_create_init(bt_field_string_get_value(member_field
));
288 if (strcmp(bt_value_string_get(value
), bt_value_string_get(min
)) < 0) {
289 bt_value_string_set(min
, bt_value_string_get(value
));
291 if (strcmp(bt_value_string_get(value
), bt_value_string_get(max
)) > 0) {
292 bt_value_string_set(max
, bt_value_string_get(value
));
294 bt_value_put_ref(value
);
295 } else if (bt_field_class_type_is(*member_class_type
,
296 BT_FIELD_CLASS_TYPE_DOUBLE_PRECISION_REAL
)) {
297 bt_value
*value
= bt_value_real_create_init(
298 bt_field_real_double_precision_get_value(member_field
));
299 if (bt_value_real_get(value
) < bt_value_real_get(min
)) {
300 bt_value_real_set(min
, bt_value_real_get(value
));
302 if (bt_value_real_get(value
) > bt_value_real_get(max
)) {
303 bt_value_real_set(max
, bt_value_real_get(value
));
305 bt_value_put_ref(value
);
306 } else if (bt_field_class_type_is(*member_class_type
,
307 BT_FIELD_CLASS_TYPE_SINGLE_PRECISION_REAL
)) {
308 bt_value
*value
= bt_value_real_create_init(
309 (double) bt_field_real_single_precision_get_value(member_field
));
310 if (bt_value_real_get(value
) < bt_value_real_get(min
)) {
311 bt_value_real_set(min
, bt_value_real_get(value
));
313 if (bt_value_real_get(value
) > bt_value_real_get(max
)) {
314 bt_value_real_set(max
, bt_value_real_get(value
));
316 bt_value_put_ref(value
);
317 } else if (bt_field_class_type_is(*member_class_type
, BT_FIELD_CLASS_TYPE_BIT_ARRAY
)) {
318 bt_value
*value
= bt_value_integer_unsigned_create_init(
319 bt_field_bit_array_get_value_as_integer(member_field
));
320 if (bt_value_integer_unsigned_get(value
) < bt_value_integer_unsigned_get(min
)) {
321 bt_value_integer_unsigned_set(min
, bt_value_integer_unsigned_get(value
));
323 if (bt_value_integer_unsigned_get(value
) > bt_value_integer_unsigned_get(max
)) {
324 bt_value_integer_unsigned_set(max
, bt_value_integer_unsigned_get(value
));
326 bt_value_put_ref(value
);
328 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
329 bt_self_component_sink_as_self_component(self_component_sink
),
330 "Unsupported field type '%ld' for member '%s'",
333 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR
;
335 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
;
338 static bt_component_class_sink_consume_method_status
339 update_stats(const bt_message
*message
,
340 field_stats
*field_stats
,
341 bt_self_component_sink
*self_component_sink
)
343 if (bt_message_get_type(message
) != BT_MESSAGE_TYPE_EVENT
) {
344 /* It's not an error to get non-EVENT messages */
345 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
;
348 bt_component_class_sink_consume_method_status status
;
349 const bt_event
*event
= bt_message_event_borrow_event_const(message
);
350 const bt_field
*event_payload
= bt_event_borrow_payload_field_const(event
);
351 const bt_event_class
*event_class
= bt_event_borrow_class_const(event
);
352 const bt_field_class
*event_payload_class
=
353 bt_event_class_borrow_payload_field_class_const(event_class
);
355 if (field_stats
->event_class
!= nullptr) {
356 assert(event_class
== field_stats
->event_class
);
358 field_stats
->event_class
= event_class
;
361 /* Iterate over each field in the event payload */
362 for (uint64_t index
= 0;
363 index
< bt_field_class_structure_get_member_count(event_payload_class
);
365 const bt_field_class_structure_member
*member
=
366 bt_field_class_structure_borrow_member_by_index_const(event_payload_class
,
368 const char *name
= bt_field_class_structure_member_get_name(member
);
369 const bt_field
*member_field
=
370 bt_field_structure_borrow_member_field_by_name_const(event_payload
, name
);
371 const bt_field_class
*member_class
=
372 bt_field_class_structure_member_borrow_field_class_const(member
);
373 const bt_field_class_type member_class_type
= bt_field_class_get_type(member_class
);
375 /* Ignore array and structure field types. */
376 if (bt_field_class_type_is(member_class_type
, BT_FIELD_CLASS_TYPE_ARRAY
) ||
377 bt_field_class_type_is(member_class_type
, BT_FIELD_CLASS_TYPE_STRUCTURE
)) {
381 bt_value
*member_map
=
382 bt_value_map_borrow_entry_value(field_stats
->stats_value
, name
);
383 if (member_map
== nullptr) {
384 if (bt_value_map_insert_empty_map_entry(
385 field_stats
->stats_value
, name
, &member_map
) !=
386 BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK
) {
387 BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_COMPONENT(
388 bt_self_component_sink_as_self_component(
389 self_component_sink
),
390 "Failed to insert new empty map entry for field '%s'",
392 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR
;
395 status
= member_stats_set_min_max(member_map
,
400 self_component_sink
);
401 if (status
!= BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
) {
405 status
= member_stats_update_min_max(member_map
,
409 self_component_sink
);
410 if (status
!= BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
) {
415 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
;
418 bt_component_class_sink_consume_method_status
419 field_stats_consume(bt_self_component_sink
*self_component_sink
)
421 bt_component_class_sink_consume_method_status status
;
422 struct field_stats
*field_stats
= (struct field_stats
*) bt_self_component_get_data(
423 bt_self_component_sink_as_self_component(self_component_sink
));
424 bt_message_array_const messages
;
425 uint64_t message_count
;
426 bt_message_iterator_next_status next_status
;
429 next_status
= bt_message_iterator_next(field_stats
->iterator
, &messages
, &message_count
);
431 if (next_status
!= BT_MESSAGE_ITERATOR_NEXT_STATUS_OK
) {
432 if (next_status
== BT_MESSAGE_ITERATOR_NEXT_STATUS_END
) {
433 bt_value_map_foreach_entry_const(
434 field_stats
->stats_value
, stats_value_print_summary
, nullptr);
435 bt_message_iterator_put_ref(field_stats
->iterator
);
437 status
= static_cast<bt_component_class_sink_consume_method_status
>(next_status
);
441 for (uint64_t index
= 0; index
< message_count
; index
++) {
442 const bt_message
*message
= messages
[index
];
443 status
= update_stats(message
, field_stats
, self_component_sink
);
444 bt_message_put_ref(message
);
445 if (status
!= BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
) {
449 status
= BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK
;