lib: standardize listener ID types with new `bt_listener_id` type
[babeltrace.git] / src / cli / babeltrace2-cfg-src-auto-disc.c
CommitLineData
73760435
SM
1/*
2 * Copyright (c) 2019 EfficiOS Inc. and Linux Foundation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23#define BT_LOG_TAG "CLI-CFG-SRC-AUTO-DISC"
24#include "logging.h"
25
26#include "babeltrace2-cfg-src-auto-disc.h"
27#include "babeltrace2-plugins.h"
28#include "common/common.h"
29
30/* Finalize and free a `struct auto_source_discovery_result`. */
31
32static
33void auto_source_discovery_result_destroy(struct auto_source_discovery_result *res)
34{
35 if (res) {
36 g_free(res->group);
37 bt_value_put_ref(res->inputs);
38 g_free(res);
39 }
40}
41
42/* Allocate and initialize a `struct auto_source_discovery_result`. */
43
44static
45struct auto_source_discovery_result *auto_source_discovery_result_create(
46 const char *plugin_name, const char *source_cc_name,
47 const char *group)
48{
49 struct auto_source_discovery_result *res;
50
51 res = g_new0(struct auto_source_discovery_result, 1);
52 if (!res) {
53 BT_CLI_LOGE_APPEND_CAUSE(
54 "Failed to allocate a auto_source_discovery_result structure.");
55 goto error;
56 }
57
58 res->plugin_name = plugin_name;
59 res->source_cc_name = source_cc_name;
60 res->group = g_strdup(group);
61 if (group && !res->group) {
62 BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate a string.");
63 goto error;
64 }
65
66 res->inputs = bt_value_array_create();
67 if (!res->inputs) {
68 BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate an array value.");
69 goto error;
70 }
71
72 goto end;
73error:
74 auto_source_discovery_result_destroy(res);
75
76end:
77 return res;
78}
79
80/* Finalize a `struct auto_source_discovery`. */
81
82void auto_source_discovery_fini(struct auto_source_discovery *auto_disc)
83{
84 if (auto_disc->results) {
85 g_ptr_array_free(auto_disc->results, TRUE);
86 }
87}
88
89/* Initialize an already allocated `struct auto_source_discovery`. */
90
91int auto_source_discovery_init(struct auto_source_discovery *auto_disc)
92{
93 int status;
94
95 auto_disc->results = g_ptr_array_new_with_free_func(
96 (GDestroyNotify) auto_source_discovery_result_destroy);
97
98 if (!auto_disc->results) {
99 goto error;
100 }
101
102 status = 0;
103 goto end;
104
105error:
106 auto_source_discovery_fini(auto_disc);
107 status = -1;
108
109end:
110
111 return status;
112}
113
114/*
115 * Assign `input` to source component class `source_cc_name` of plugin
116 * `plugin_name`, in the group with key `group`.
117 */
118
119static
120int auto_source_discovery_add(struct auto_source_discovery *auto_disc,
121 const char *plugin_name,
122 const char *source_cc_name,
123 const char *group,
124 const char *input)
125{
126 int status;
127 bt_value_array_append_element_status append_status;
128 guint len;
129 guint i;
130 struct auto_source_discovery_result *res = NULL;
131
132 len = auto_disc->results->len;
133 i = len;
134
135 if (group) {
136 for (i = 0; i < len; i++) {
137 res = g_ptr_array_index(auto_disc->results, i);
138
139 if (strcmp(res->plugin_name, plugin_name) != 0) {
140 continue;
141 }
142
143 if (strcmp(res->source_cc_name, source_cc_name) != 0) {
144 continue;
145 }
146
147 if (g_strcmp0(res->group, group) != 0) {
148 continue;
149 }
150
151 break;
152 }
153 }
154
155 if (i == len) {
156 /* Add a new result entry. */
157 res = auto_source_discovery_result_create(plugin_name,
158 source_cc_name, group);
159 if (!res) {
160 goto error;
161 }
162
163 g_ptr_array_add(auto_disc->results, res);
164 }
165
166 append_status = bt_value_array_append_string_element(res->inputs, input);
167 if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
168 BT_CLI_LOGE_APPEND_CAUSE("Failed to append a string value.");
169 goto error;
170 }
171
172
173 status = 0;
174 goto end;
175
176error:
177 status = -1;
178
179end:
180 return status;
181}
182
183static
184int convert_weight_value(const bt_value *weight_value, double *weight,
185 const char *plugin_name, const char *source_cc_name,
186 const char *input, const char *input_type)
187{
188 enum bt_value_type weight_value_type;
189 int status;
190
191 weight_value_type = bt_value_get_type(weight_value);
192
193 if (weight_value_type == BT_VALUE_TYPE_REAL) {
194 *weight = bt_value_real_get(weight_value);
195 } else if (weight_value_type == BT_VALUE_TYPE_SIGNED_INTEGER) {
196 /* Accept signed integer as a convenience for "return 0" or "return 1" in Python. */
197 *weight = bt_value_integer_signed_get(weight_value);
198 } else {
199 BT_LOGW("support-info query: unexpected type for weight: "
200 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
201 "expected-entry-type=%s, actual-entry-type=%s",
202 plugin_name, source_cc_name, input, input_type,
203 bt_common_value_type_string(BT_VALUE_TYPE_REAL),
204 bt_common_value_type_string(bt_value_get_type(weight_value)));
205 goto error;
206 }
207
208 if (*weight < 0.0 || *weight > 1.0) {
209 BT_LOGW("support-info query: weight value is out of range [0.0, 1.0]: "
210 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
211 "weight=%f",
212 plugin_name, source_cc_name, input, input_type, *weight);
213 goto error;
214 }
215
216 status = 0;
217 goto end;
218
219error:
220 status = -1;
221
222end:
223 return status;
224}
225
226/*
227 * Query all known source components to see if any of them can handle `input`
228 * as the given `type`(arbitrary string, directory or file).
229 *
230 * If `plugin_restrict` is non-NULL, only query source component classes provided
231 * by the plugin with that name.
232 *
233 * If `component_class_restrict` is non-NULL, only query source component classes
234 * with that name.
235 *
236 * Return:
237 *
238 * - > 0 on success, if no source component class has reported that it handles `input`
239 * - 0 on success, if a source component class has reported that it handles `input`
240 * - < 0 on failure (e.g. memory error)
241 */
242static
243int support_info_query_all_sources(const char *input,
244 const char *input_type,
245 bt_query_executor *query_executor, size_t plugin_count,
246 const char *plugin_restrict,
247 const char *component_class_restrict,
248 enum bt_logging_level log_level,
249 struct auto_source_discovery *auto_disc)
250{
251 bt_value_map_insert_entry_status insert_status;
252 bt_value *query_params = NULL;
253 int status;
254 size_t i_plugins;
255 const struct bt_value *query_result = NULL;
256 struct {
257 const bt_component_class_source *source;
258 const bt_plugin *plugin;
259 const bt_value *group;
260 double weigth;
261 } winner = { NULL, NULL, NULL, 0 };
262
263 query_params = bt_value_map_create();
264 if (!query_params) {
265 BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate a map value.");
266 goto error;
267 }
268
269 insert_status = bt_value_map_insert_string_entry(query_params, "input", input);
270 if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
271 BT_CLI_LOGE_APPEND_CAUSE("Failed to insert a map entry.");
272 goto error;
273 }
274
275 insert_status = bt_value_map_insert_string_entry(query_params, "type", input_type);
276 if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
277 BT_CLI_LOGE_APPEND_CAUSE("Failed to insert a map entry.");
278 goto error;
279 }
280
281 for (i_plugins = 0; i_plugins < plugin_count; i_plugins++) {
282 const bt_plugin *plugin;
283 const char *plugin_name;
284 uint64_t source_count;
285 uint64_t i_sources;
286
287 plugin = borrow_loaded_plugin(i_plugins);
288 plugin_name = bt_plugin_get_name(plugin);
289
290 /*
291 * If the search is restricted to a specific plugin, only consider
292 * the plugin with that name.
293 */
294 if (plugin_restrict && strcmp(plugin_restrict, plugin_name) != 0) {
295 continue;
296 }
297
298 source_count = bt_plugin_get_source_component_class_count(plugin);
299
300 for (i_sources = 0; i_sources < source_count; i_sources++) {
301 const bt_component_class_source *source_cc;
302 const bt_component_class *cc;
303 const char *source_cc_name;
304 bt_query_executor_query_status query_status;
305
306 source_cc = bt_plugin_borrow_source_component_class_by_index_const(plugin, i_sources);
307 cc = bt_component_class_source_as_component_class_const(source_cc);
308 source_cc_name = bt_component_class_get_name(cc);
309
310 /*
311 * If the search is restricted to a specific component class, only consider the
312 * component classes with that name.
313 */
314 if (component_class_restrict && strcmp(component_class_restrict, source_cc_name) != 0) {
315 continue;
316 }
317
318 BT_LOGD("support-info query: before: component-class-name=source.%s.%s, input=%s, "
319 "type=%s", plugin_name, source_cc_name, input, input_type);
320
321 BT_VALUE_PUT_REF_AND_RESET(query_result);
322 query_status = bt_query_executor_query(query_executor, cc, "support-info",
323 query_params, log_level, &query_result);
324
325 if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_OK) {
326 double weight;
327 const bt_value *group_value = NULL;
328 enum bt_value_type query_result_type;
329
330 BT_ASSERT(query_result);
331
332 query_result_type = bt_value_get_type(query_result);
333
334 if (query_result_type == BT_VALUE_TYPE_REAL || query_result_type == BT_VALUE_TYPE_SIGNED_INTEGER) {
335 if (convert_weight_value(query_result, &weight, plugin_name, source_cc_name, input, input_type) != 0) {
336 /* convert_weight_value has already warned. */
337 continue;
338 }
339 } else if (query_result_type == BT_VALUE_TYPE_MAP) {
340 const bt_value *weight_value;
341
342 if (!bt_value_map_has_entry(query_result, "weight")) {
343 BT_LOGW("support-info query: result is missing `weight` entry: "
344 "component-class-name=source.%s.%s, input=%s, input-type=%s",
345 bt_plugin_get_name(plugin),
346 bt_component_class_get_name(cc), input,
347 input_type);
348 continue;
349 }
350
351 weight_value = bt_value_map_borrow_entry_value_const(query_result, "weight");
352 BT_ASSERT(weight_value);
353
354 if (convert_weight_value(weight_value, &weight, plugin_name, source_cc_name, input, input_type) != 0) {
355 /* convert_weight_value has already warned. */
356 continue;
357 }
358
359 if (bt_value_map_has_entry(query_result, "group")) {
360 enum bt_value_type group_value_type;
361
362 group_value = bt_value_map_borrow_entry_value_const(query_result, "group");
363 BT_ASSERT(group_value);
364
365 group_value_type = bt_value_get_type(group_value);
366
367 if (group_value_type == BT_VALUE_TYPE_NULL) {
368 /* Do as if no value was passed. */
369 group_value = NULL;
370 } else if (bt_value_get_type(group_value) != BT_VALUE_TYPE_STRING) {
371 BT_LOGW("support-info query: unexpected type for entry `group`: "
372 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
373 "expected-entry-type=%s,%s, actual-entry-type=%s",
374 bt_plugin_get_name(plugin),
375 bt_component_class_get_name(cc), input,
376 input_type,
377 bt_common_value_type_string(BT_VALUE_TYPE_NULL),
378 bt_common_value_type_string(BT_VALUE_TYPE_STRING),
379 bt_common_value_type_string(bt_value_get_type(group_value)));
380 continue;
381 }
382 }
383 } else {
384 BT_LOGW("support-info query: unexpected result type: "
385 "component-class-name=source.%s.%s, input=%s, input-type=%s, "
386 "expected-types=%s,%s,%s, actual-type=%s",
387 bt_plugin_get_name(plugin),
388 bt_component_class_get_name(cc), input,
389 input_type,
390 bt_common_value_type_string(BT_VALUE_TYPE_REAL),
391 bt_common_value_type_string(BT_VALUE_TYPE_MAP),
392 bt_common_value_type_string(BT_VALUE_TYPE_SIGNED_INTEGER),
393 bt_common_value_type_string(bt_value_get_type(query_result)));
394 continue;
395 }
396
397 BT_LOGD("support-info query: success: component-class-name=source.%s.%s, input=%s, "
398 "type=%s, weight=%f\n",
399 bt_plugin_get_name(plugin), bt_component_class_get_name(cc), input,
400 input_type, weight);
401
402 if (weight > winner.weigth) {
403 winner.source = source_cc;
404 winner.plugin = plugin;
405
406 bt_value_put_ref(winner.group);
407 winner.group = group_value;
408 bt_value_get_ref(winner.group);
409
410 winner.weigth = weight;
411 }
412 } else if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_ERROR) {
413 BT_CLI_LOGE_APPEND_CAUSE("support-info query failed.");
414 goto error;
415 } else if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_MEMORY_ERROR) {
416 BT_CLI_LOGE_APPEND_CAUSE("Memory error.");
417 goto error;
418 } else {
419 BT_LOGD("support-info query: failure: component-class-name=source.%s.%s, input=%s, "
420 "type=%s, status=%s\n",
421 bt_plugin_get_name(plugin), bt_component_class_get_name(cc), input,
422 input_type,
423 bt_common_func_status_string(query_status));
424 }
425 }
426 }
427
428 if (winner.source) {
429 const char *source_name;
430 const char *plugin_name;
431 const char *group;
432
433 source_name = bt_component_class_get_name(
434 bt_component_class_source_as_component_class_const(winner.source));
435 plugin_name = bt_plugin_get_name(winner.plugin);
436 group = winner.group ? bt_value_string_get(winner.group) : NULL;
437
438 BT_LOGI("Input %s is awarded to component class source.%s.%s with weight %f",
439 input, plugin_name, source_name, winner.weigth);
440
441 status = auto_source_discovery_add(auto_disc, plugin_name, source_name, group, input);
442 if (status != 0) {
443 goto error;
444 }
445 } else {
446 BT_LOGI("Input %s (%s) was not recognized by any source component class.",
447 input, input_type);
448 status = 1;
449 }
450
451 goto end;
452
453error:
454 status = -1;
455
456end:
457 bt_value_put_ref(query_result);
458 bt_value_put_ref(query_params);
459 bt_value_put_ref(winner.group);
460
461 return status;
462}
463
464/*
465 * Look for a source component class that recognizes `input` as an arbitrary
466 * string.
467 *
468 * Same return value semantic as `support_info_query_all_sources`.
469 */
470
471static
472int auto_discover_source_for_input_as_string(const char *input,
473 bt_query_executor *query_executor, size_t plugin_count,
474 const char *plugin_restrict,
475 const char *component_class_restrict,
476 enum bt_logging_level log_level,
477 struct auto_source_discovery *auto_disc)
478{
479 return support_info_query_all_sources(input, "string",
480 query_executor, plugin_count, plugin_restrict,
481 component_class_restrict, log_level, auto_disc);
482}
483
484static
485int auto_discover_source_for_input_as_dir_or_file_rec(GString *input,
486 bt_query_executor *query_executor, size_t plugin_count,
487 const char *plugin_restrict,
488 const char *component_class_restrict,
489 enum bt_logging_level log_level,
490 struct auto_source_discovery *auto_disc)
491{
492 int status;
493 GError *error = NULL;
494
495 if (g_file_test(input->str, G_FILE_TEST_IS_REGULAR)) {
496 /* It's a file. */
497 status = support_info_query_all_sources(input->str,
498 "file", query_executor, plugin_count,
499 plugin_restrict, component_class_restrict, log_level, auto_disc);
500 } else if (g_file_test(input->str, G_FILE_TEST_IS_DIR)) {
501 GDir *dir;
502 const gchar *dirent;
503 gsize saved_input_len;
504 int dir_status = 1;
505
506 /* It's a directory. */
507 status = support_info_query_all_sources(input->str,
508 "directory", query_executor, plugin_count,
509 plugin_restrict, component_class_restrict, log_level,
510 auto_disc);
511
512 if (status < 0) {
513 /* Fatal error. */
514 goto error;
515 } else if (status == 0) {
516 /*
517 * A component class claimed this input as a directory,
518 * don't recurse.
519 */
520 goto end;
521 }
522
523 dir = g_dir_open(input->str, 0, &error);
524 if (!dir) {
525 const char *fmt = "Failed to open directory %s: %s";
526 BT_LOGW(fmt, input->str, error->message);
527
d847ef86 528 if (error->code == G_FILE_ERROR_ACCES) {
73760435
SM
529 /* This is not a fatal error, we just skip it. */
530 status = 1;
531 goto end;
532 } else {
533 BT_CLI_LOGE_APPEND_CAUSE(fmt, input->str,
534 error->message);
535 goto error;
536 }
537 }
538
539 saved_input_len = input->len;
540
541 do {
542 errno = 0;
543 dirent = g_dir_read_name(dir);
544 if (dirent) {
545 g_string_append_c_inline(input, G_DIR_SEPARATOR);
546 g_string_append(input, dirent);
547
548 status = auto_discover_source_for_input_as_dir_or_file_rec(
549 input, query_executor, plugin_count,
550 plugin_restrict, component_class_restrict,
551 log_level, auto_disc);
552
553 g_string_truncate(input, saved_input_len);
554
555 if (status < 0) {
556 /* Fatal error. */
557 goto error;
558 } else if (status == 0) {
559 dir_status = 0;
560 }
561 } else if (errno != 0) {
562 BT_LOGW_ERRNO("Failed to read directory entry", ": dir=%s", input->str);
563 goto error;
564 }
5084732e 565 } while (dirent);
73760435
SM
566
567 status = dir_status;
568
569 g_dir_close(dir);
570 } else {
571 BT_LOGD("Skipping %s, not a file or directory", input->str);
572 status = 1;
573 }
574
575 goto end;
576
577error:
578 status = -1;
579
580end:
581
582 if (error) {
583 g_error_free(error);
584 }
585
586 return status;
587}
588
589/*
590 * Look for a source component class that recognizes `input` as a directory or
591 * file. If `input` is a directory and is not directly recognized, recurse and
592 * apply the same logic to children nodes.
593 *
594 * Same return value semantic as `support_info_query_all_sources`.
595 */
596
597static
598int auto_discover_source_for_input_as_dir_or_file(const char *input,
599 bt_query_executor *query_executor, size_t plugin_count,
600 const char *plugin_restrict,
601 const char *component_class_restrict,
602 enum bt_logging_level log_level,
603 struct auto_source_discovery *auto_disc)
604{
605 GString *mutable_input;
606 int status;
607
608 mutable_input = g_string_new(input);
609 if (!mutable_input) {
610 status = -1;
611 goto end;
612 }
613
614 status = auto_discover_source_for_input_as_dir_or_file_rec(
615 mutable_input, query_executor, plugin_count, plugin_restrict,
616 component_class_restrict, log_level, auto_disc);
617
618 g_string_free(mutable_input, TRUE);
619end:
620 return status;
621}
622
623int auto_discover_source_components(
624 const bt_value *plugin_paths,
625 const bt_value *inputs,
626 const char *plugin_restrict,
627 const char *component_class_restrict,
628 enum bt_logging_level log_level,
629 struct auto_source_discovery *auto_disc)
630{
631 uint64_t i_inputs, input_count;
632 int status;
633 size_t plugin_count;
634 bt_query_executor *query_executor = NULL;
635
636 input_count = bt_value_array_get_size(inputs);
637
638 status = require_loaded_plugins(plugin_paths);
639 if (status != 0) {
640 goto end;
641 }
642
643 plugin_count = get_loaded_plugins_count();
644
645 query_executor = bt_query_executor_create();
646 if (!query_executor) {
647 BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate a query executor.");
648 goto end;
649 }
650
651 for (i_inputs = 0; i_inputs < input_count; i_inputs++) {
652 const bt_value *input_value;
653 const char *input;
654
655 input_value = bt_value_array_borrow_element_by_index_const(inputs, i_inputs);
656 input = bt_value_string_get(input_value);
657 status = auto_discover_source_for_input_as_string(input, query_executor,
658 plugin_count, plugin_restrict, component_class_restrict,
659 log_level, auto_disc);
660 if (status < 0) {
661 /* Fatal error. */
662 goto end;
663 } else if (status == 0) {
664 /* A component class has claimed this input as an arbitrary string. */
665 continue;
666 }
667
668 status = auto_discover_source_for_input_as_dir_or_file(input,
669 query_executor, plugin_count, plugin_restrict,
670 component_class_restrict, log_level, auto_disc);
671 if (status < 0) {
672 /* Fatal error. */
673 goto end;
674 } else if (status == 0) {
675 /*
676 * This input (or something under it) was recognized.
677 */
678 continue;
679 }
680
681 BT_LOGW("No trace was found based on input `%s`.", input);
682 }
683
684 status = 0;
685end:
686 bt_query_executor_put_ref(query_executor);
687 return status;
688}
This page took 0.066257 seconds and 4 git commands to generate.