2 * Copyright (c) 2019 EfficiOS Inc. and Linux Foundation
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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
23 #define BT_LOG_TAG "CLI-CFG-SRC-AUTO-DISC"
26 #include "babeltrace2-cfg-src-auto-disc.h"
27 #include "babeltrace2-plugins.h"
28 #include "common/common.h"
30 /* Finalize and free a `struct auto_source_discovery_result`. */
33 void auto_source_discovery_result_destroy(struct auto_source_discovery_result
*res
)
37 bt_value_put_ref(res
->inputs
);
42 /* Allocate and initialize a `struct auto_source_discovery_result`. */
45 struct auto_source_discovery_result
*auto_source_discovery_result_create(
46 const char *plugin_name
, const char *source_cc_name
,
49 struct auto_source_discovery_result
*res
;
51 res
= g_new0(struct auto_source_discovery_result
, 1);
53 BT_CLI_LOGE_APPEND_CAUSE(
54 "Failed to allocate a auto_source_discovery_result structure.");
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.");
66 res
->inputs
= bt_value_array_create();
68 BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate an array value.");
74 auto_source_discovery_result_destroy(res
);
80 /* Finalize a `struct auto_source_discovery`. */
82 void auto_source_discovery_fini(struct auto_source_discovery
*auto_disc
)
84 if (auto_disc
->results
) {
85 g_ptr_array_free(auto_disc
->results
, TRUE
);
89 /* Initialize an already allocated `struct auto_source_discovery`. */
91 int auto_source_discovery_init(struct auto_source_discovery
*auto_disc
)
95 auto_disc
->results
= g_ptr_array_new_with_free_func(
96 (GDestroyNotify
) auto_source_discovery_result_destroy
);
98 if (!auto_disc
->results
) {
106 auto_source_discovery_fini(auto_disc
);
115 * Assign `input` to source component class `source_cc_name` of plugin
116 * `plugin_name`, in the group with key `group`.
120 int auto_source_discovery_add(struct auto_source_discovery
*auto_disc
,
121 const char *plugin_name
,
122 const char *source_cc_name
,
127 bt_value_array_append_element_status append_status
;
130 struct auto_source_discovery_result
*res
= NULL
;
132 len
= auto_disc
->results
->len
;
136 for (i
= 0; i
< len
; i
++) {
137 res
= g_ptr_array_index(auto_disc
->results
, i
);
139 if (strcmp(res
->plugin_name
, plugin_name
) != 0) {
143 if (strcmp(res
->source_cc_name
, source_cc_name
) != 0) {
147 if (g_strcmp0(res
->group
, group
) != 0) {
156 /* Add a new result entry. */
157 res
= auto_source_discovery_result_create(plugin_name
,
158 source_cc_name
, group
);
163 g_ptr_array_add(auto_disc
->results
, res
);
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.");
184 int 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
)
188 enum bt_value_type weight_value_type
;
191 weight_value_type
= bt_value_get_type(weight_value
);
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
);
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
)));
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, "
212 plugin_name
, source_cc_name
, input
, input_type
, *weight
);
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).
230 * If `plugin_restrict` is non-NULL, only query source component classes provided
231 * by the plugin with that name.
233 * If `component_class_restrict` is non-NULL, only query source component classes
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)
243 int 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
)
251 bt_value_map_insert_entry_status insert_status
;
252 bt_value
*query_params
= NULL
;
255 const struct bt_value
*query_result
= NULL
;
257 const bt_component_class_source
*source
;
258 const bt_plugin
*plugin
;
259 const bt_value
*group
;
261 } winner
= { NULL
, NULL
, NULL
, 0 };
263 query_params
= bt_value_map_create();
265 BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate a map value.");
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.");
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.");
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
;
287 plugin
= borrow_loaded_plugin(i_plugins
);
288 plugin_name
= bt_plugin_get_name(plugin
);
291 * If the search is restricted to a specific plugin, only consider
292 * the plugin with that name.
294 if (plugin_restrict
&& strcmp(plugin_restrict
, plugin_name
) != 0) {
298 source_count
= bt_plugin_get_source_component_class_count(plugin
);
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
;
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
);
311 * If the search is restricted to a specific component class, only consider the
312 * component classes with that name.
314 if (component_class_restrict
&& strcmp(component_class_restrict
, source_cc_name
) != 0) {
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
);
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
);
325 if (query_status
== BT_QUERY_EXECUTOR_QUERY_STATUS_OK
) {
327 const bt_value
*group_value
= NULL
;
328 enum bt_value_type query_result_type
;
330 BT_ASSERT(query_result
);
332 query_result_type
= bt_value_get_type(query_result
);
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. */
339 } else if (query_result_type
== BT_VALUE_TYPE_MAP
) {
340 const bt_value
*weight_value
;
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
,
351 weight_value
= bt_value_map_borrow_entry_value_const(query_result
, "weight");
352 BT_ASSERT(weight_value
);
354 if (convert_weight_value(weight_value
, &weight
, plugin_name
, source_cc_name
, input
, input_type
) != 0) {
355 /* convert_weight_value has already warned. */
359 if (bt_value_map_has_entry(query_result
, "group")) {
360 enum bt_value_type group_value_type
;
362 group_value
= bt_value_map_borrow_entry_value_const(query_result
, "group");
363 BT_ASSERT(group_value
);
365 group_value_type
= bt_value_get_type(group_value
);
367 if (group_value_type
== BT_VALUE_TYPE_NULL
) {
368 /* Do as if no value was passed. */
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
,
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
)));
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
,
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
)));
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
,
402 if (weight
> winner
.weigth
) {
403 winner
.source
= source_cc
;
404 winner
.plugin
= plugin
;
406 bt_value_put_ref(winner
.group
);
407 winner
.group
= group_value
;
408 bt_value_get_ref(winner
.group
);
410 winner
.weigth
= weight
;
412 } else if (query_status
== BT_QUERY_EXECUTOR_QUERY_STATUS_ERROR
) {
413 BT_CLI_LOGE_APPEND_CAUSE("support-info query failed.");
415 } else if (query_status
== BT_QUERY_EXECUTOR_QUERY_STATUS_MEMORY_ERROR
) {
416 BT_CLI_LOGE_APPEND_CAUSE("Memory error.");
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
,
423 bt_common_func_status_string(query_status
));
429 const char *source_name
;
430 const char *plugin_name
;
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
;
438 BT_LOGI("Input %s is awarded to component class source.%s.%s with weight %f",
439 input
, plugin_name
, source_name
, winner
.weigth
);
441 status
= auto_source_discovery_add(auto_disc
, plugin_name
, source_name
, group
, input
);
446 BT_LOGI("Input %s (%s) was not recognized by any source component class.",
457 bt_value_put_ref(query_result
);
458 bt_value_put_ref(query_params
);
459 bt_value_put_ref(winner
.group
);
465 * Look for a source component class that recognizes `input` as an arbitrary
468 * Same return value semantic as `support_info_query_all_sources`.
472 int 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
)
479 return support_info_query_all_sources(input
, "string",
480 query_executor
, plugin_count
, plugin_restrict
,
481 component_class_restrict
, log_level
, auto_disc
);
485 int 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
)
493 GError
*error
= NULL
;
495 if (g_file_test(input
->str
, G_FILE_TEST_IS_REGULAR
)) {
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
)) {
503 gsize saved_input_len
;
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
,
515 } else if (status
== 0) {
517 * A component class claimed this input as a directory,
523 dir
= g_dir_open(input
->str
, 0, &error
);
525 const char *fmt
= "Failed to open directory %s: %s";
526 BT_LOGW(fmt
, input
->str
, error
->message
);
528 if (error
->code
== G_FILE_ERROR_ACCES
) {
529 /* This is not a fatal error, we just skip it. */
533 BT_CLI_LOGE_APPEND_CAUSE(fmt
, input
->str
,
539 saved_input_len
= input
->len
;
543 dirent
= g_dir_read_name(dir
);
545 g_string_append_c_inline(input
, G_DIR_SEPARATOR
);
546 g_string_append(input
, dirent
);
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
);
553 g_string_truncate(input
, saved_input_len
);
558 } else if (status
== 0) {
561 } else if (errno
!= 0) {
562 BT_LOGW_ERRNO("Failed to read directory entry", ": dir=%s", input
->str
);
571 BT_LOGD("Skipping %s, not a file or directory", input
->str
);
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.
594 * Same return value semantic as `support_info_query_all_sources`.
598 int 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
)
605 GString
*mutable_input
;
608 mutable_input
= g_string_new(input
);
609 if (!mutable_input
) {
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
);
618 g_string_free(mutable_input
, TRUE
);
623 int 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
)
631 uint64_t i_inputs
, input_count
;
634 bt_query_executor
*query_executor
= NULL
;
636 input_count
= bt_value_array_get_size(inputs
);
638 status
= require_loaded_plugins(plugin_paths
);
643 plugin_count
= get_loaded_plugins_count();
645 query_executor
= bt_query_executor_create();
646 if (!query_executor
) {
647 BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate a query executor.");
651 for (i_inputs
= 0; i_inputs
< input_count
; i_inputs
++) {
652 const bt_value
*input_value
;
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
);
663 } else if (status
== 0) {
664 /* A component class has claimed this input as an arbitrary string. */
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
);
674 } else if (status
== 0) {
676 * This input (or something under it) was recognized.
681 BT_LOGW("No trace was found based on input `%s`.", input
);
686 bt_query_executor_put_ref(query_executor
);