clang-tidy: apply suggested fixes
[lttng-tools.git] / tests / utils / bt2_plugins / field_stats / field_stats.cpp
1 /**
2 * Copyright (C) 2023 Kienan Stewart <kstewart@efficios.com>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 *
6 */
7
8 #include "../fmt.hpp"
9 #include "../utils.hpp"
10 #include "field_stats.hpp"
11
12 #include <common/make-unique-wrapper.hpp>
13 #include <common/make-unique.hpp>
14
15 #include <assert.h>
16 #include <babeltrace2/babeltrace.h>
17 #include <cstdint>
18 #include <cstring>
19 #include <iostream>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <utility>
23
24 class bad_alloc_with_msg : public std::bad_alloc {
25 public:
26 explicit bad_alloc_with_msg(std::string msg) : _msg(std::move(msg))
27 {
28 }
29
30 const char *what() const noexcept override
31 {
32 return _msg.c_str();
33 }
34
35 private:
36 std::string _msg;
37 };
38
39 struct field_stats {
40 public:
41 field_stats() : stats_value{ lttng::bt2::make_value_ref(bt_value_map_create()) }
42 {
43 if (!stats_value) {
44 throw bad_alloc_with_msg(
45 "Failed to allocate memory for field_stats.stats map");
46 }
47 }
48
49 ~field_stats() = default;
50
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;
54 };
55
56 namespace {
57 bt_value_map_foreach_entry_const_func_status
58 stats_value_print_summary(const char *key, const bt_value *value, void *)
59 {
60 LTTNG_ASSERT(bt_value_is_map(value));
61
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);
66
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;
69
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);
73 }
74
75 LTTNG_ASSERT(bt_value_get_type(min) == bt_value_get_type(max));
76
77 switch (bt_value_get_type(min)) {
78 case BT_VALUE_TYPE_STRING:
79 fmt::print("{} \"{}\" \"{}\"\n",
80 key,
81 bt_value_string_get(min),
82 bt_value_string_get(max));
83 break;
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",
88 key,
89 bt_value_integer_unsigned_get(min),
90 bt_value_integer_unsigned_get(max));
91 break;
92 default:
93 std::cout << lttng::format("{} {} {}\n",
94 key,
95 bt_value_integer_unsigned_get(min),
96 bt_value_integer_unsigned_get(max));
97 break;
98 }
99
100 break;
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",
105 key,
106 std::uint64_t(bt_value_integer_signed_get(min)),
107 std::uint64_t(bt_value_integer_signed_get(max)));
108 break;
109 default:
110 std::cout << lttng::format("{} {} {}\n",
111 key,
112 bt_value_integer_signed_get(min),
113 bt_value_integer_signed_get(max));
114 break;
115 }
116
117 break;
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));
121 break;
122 default:
123 abort();
124 }
125
126 return BT_VALUE_MAP_FOREACH_ENTRY_CONST_FUNC_STATUS_OK;
127 }
128
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)
134 {
135 lttng::bt2::value_ref min, max, display_base;
136 const char *name = bt_field_class_structure_member_get_name(member);
137
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)));
174 } else {
175 throw std::runtime_error(lttng::format(
176 "Unsupported field type '{}' for member '{}'", *member_class_type, name));
177 }
178
179 if (min) {
180 bt_value_map_insert_entry(member_map, "min", min.get());
181 } else {
182 throw std::runtime_error(lttng::format("No minimum value for member '{}'", name));
183 }
184
185 if (max) {
186 bt_value_map_insert_entry(member_map, "max", max.get());
187 } else {
188 throw std::runtime_error(lttng::format("No maximum value for member '{}'", name));
189 }
190
191 if (display_base) {
192 bt_value_map_insert_entry(member_map, "display_base", display_base.get());
193 }
194 }
195
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)
200 {
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");
204
205 if (min == nullptr || max == nullptr) {
206 throw std::runtime_error(
207 lttng::format("Missing min or max value for member '{}'", name));
208 }
209
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);
212
213 if (value < bt_value_integer_unsigned_get(min)) {
214 bt_value_integer_unsigned_set(min, value);
215 }
216
217 if (value > bt_value_integer_unsigned_get(max)) {
218 bt_value_integer_unsigned_set(max, value);
219 }
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);
222
223 if (value < bt_value_integer_signed_get(min)) {
224 bt_value_integer_signed_set(min, value);
225 }
226
227 if (value > bt_value_integer_signed_get(max)) {
228 bt_value_integer_signed_set(max, value);
229 }
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);
232
233 if (strcmp(value, bt_value_string_get(min)) < 0) {
234 bt_value_string_set(min, value);
235 }
236
237 if (strcmp(value, bt_value_string_get(max)) > 0) {
238 bt_value_string_set(max, value);
239 }
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);
243
244 if (value < bt_value_real_get(min)) {
245 bt_value_real_set(min, value);
246 }
247
248 if (value > bt_value_real_get(max)) {
249 bt_value_real_set(max, value);
250 }
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));
254
255 if (value < bt_value_real_get(min)) {
256 bt_value_real_set(min, value);
257 }
258
259 if (value > bt_value_real_get(max)) {
260 bt_value_real_set(max, value);
261 }
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);
264
265 if (value < bt_value_integer_unsigned_get(min)) {
266 bt_value_integer_unsigned_set(min, value);
267 }
268
269 if (value > bt_value_integer_unsigned_get(max)) {
270 bt_value_integer_unsigned_set(max, value);
271 }
272 } else {
273 throw std::runtime_error(lttng::format(
274 "Unsupported field type '%{}' for member '{}'", *member_class_type, name));
275 }
276 }
277
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)
282 {
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;
286 }
287
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);
293
294 if (field_stats.event_class != nullptr) {
295 LTTNG_ASSERT(event_class == field_stats.event_class.get());
296 } else {
297 bt_event_class_get_ref(event_class);
298 field_stats.event_class.reset(event_class);
299 }
300
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);
304 index++) {
305 const bt_field_class_structure_member *member =
306 bt_field_class_structure_borrow_member_by_index_const(event_payload_class,
307 index);
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);
314
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. */
318 continue;
319 }
320
321 try {
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 '{}'",
331 name));
332 }
333
334 member_stats_set_min_max(member_map,
335 member,
336 member_field,
337 member_class,
338 &member_class_type);
339 } else {
340 /* Update the value with min/max values. */
341 member_stats_update_min_max(
342 member_map, member, member_field, &member_class_type);
343 }
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),
347 "%s",
348 ex.what());
349 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_ERROR;
350 }
351 }
352
353 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
354 }
355 } /* namespace */
356
357 bt_component_class_initialize_method_status
358 field_stats_initialize(bt_self_component_sink *self_component_sink,
359 bt_self_component_sink_configuration *,
360 const bt_value *,
361 void *)
362 {
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;
369 }
370
371 std::unique_ptr<struct field_stats> field_stats;
372 try {
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),
377 "%s",
378 ex.what());
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;
385 }
386
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;
391 }
392
393 void field_stats_finalize(bt_self_component_sink *self_component_sink)
394 {
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)));
397
398 delete field_stats;
399 }
400
401 bt_component_class_sink_graph_is_configured_method_status
402 field_stats_graph_is_configured(bt_self_component_sink *self_component_sink)
403 {
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)));
406 auto *input_port =
407 bt_self_component_sink_borrow_input_port_by_index(self_component_sink, 0);
408
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;
417 }
418
419 field_stats.upstream_iterator.reset(raw_iterator);
420 return BT_COMPONENT_CLASS_SINK_GRAPH_IS_CONFIGURED_METHOD_STATUS_OK;
421 }
422
423 bt_component_class_sink_consume_method_status
424 field_stats_consume(bt_self_component_sink *self_component_sink)
425 {
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)));
428
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);
433
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);
439 }
440
441 return static_cast<bt_component_class_sink_consume_method_status>(next_status);
442 }
443
444 for (std::uint64_t index = 0; index < message_count; index++) {
445 const auto message = lttng::bt2::message_const_ref(messages[index]);
446
447 const auto status = update_stats(message.get(), field_stats, self_component_sink);
448 if (status != BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK) {
449 return status;
450 }
451 }
452
453 return BT_COMPONENT_CLASS_SINK_CONSUME_METHOD_STATUS_OK;
454 }
This page took 0.039868 seconds and 4 git commands to generate.