Move autodisc to its own convenience library
authorSimon Marchi <simon.marchi@efficios.com>
Mon, 5 Aug 2019 15:05:08 +0000 (11:05 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Mon, 12 Aug 2019 02:19:51 +0000 (22:19 -0400)
Since we'll want to make TraceCollectionMessageIterator have an
automatic source discovery feature, like the CLI has, move the auto
discovery code to its own library, which doesn't have any dependency on
the CLI.

To avoid dependencies on the CLI, here are the changes done to the auto
discovery code:

- To avoid depending on cli_query, use a simplified version,
  simple_query, included in the auto discovery code.  It doesn't handle
  retrying if the status is TRY_AGAIN, as it is not very likely that a
  babeltrace2.support-info query implementation will need to do that, as
  we want them to be very fast (we can always revisit it in the future).
- Avoid using BT_CLI_LOGE_APPEND_CAUSE, replace with the new
  BT_AUTODISC_LOGE_AND_APPEND.
- Don't use require_loaded_plugins, get_loaded_plugins_count and
  borrow_loaded_plugin, as those are CLI-specific.  Instead, the caller of
  auto_discover_source_components must provide an array of plugins.
- plugin_restrict, used to filter down to a single plugin by name, is
  removed.  Instead, it is the caller's responsibility to pass a single
  plugin to `plugins` if it wants to restrict the search to a single
  plugin.

A side effect of moving the code is that a warning is now printed by
default when no input is found from non-option argument.  I think this
is desirable to have this warning printed, so I have opted to update the
test case to expect it.

Change-Id: Id4009976430126d926eac46ff044365a6b2bc006
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1825
Tested-by: jenkins <jenkins@lttng.org>
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
12 files changed:
configure.ac
src/Makefile.am
src/autodisc/Makefile.am [new file with mode: 0644]
src/autodisc/autodisc.c [new file with mode: 0644]
src/autodisc/autodisc.h [new file with mode: 0644]
src/cli/Makefile.am
src/cli/babeltrace2-cfg-cli-args.c
src/cli/babeltrace2-cfg-src-auto-disc.c [deleted file]
src/cli/babeltrace2-cfg-src-auto-disc.h [deleted file]
src/cli/babeltrace2-plugins.c
src/cli/babeltrace2-plugins.h
tests/cli/convert/test_auto_source_discovery_grouping

index b5ae239cdce2aadc2fa1bb692cd8c2ea25b0dafd..93576c3cf2641b7001ee014c0d645f5ad47d71ba 100644 (file)
@@ -674,6 +674,7 @@ AC_CONFIG_FILES([
        include/Makefile
        Makefile
        src/argpar/Makefile
+       src/autodisc/Makefile
        src/babeltrace2-ctf-writer.pc
        src/babeltrace2.pc
        src/bindings/Makefile
index b08827fe115632211b585bcd08c358acda656745..2e9acc5969b4f3ba13402cf84bf5d9bb4b549942 100644 (file)
@@ -1,6 +1,7 @@
 SUBDIRS = \
        common \
        py-common \
+       autodisc \
        argpar \
        ctfser \
        fd-cache \
diff --git a/src/autodisc/Makefile.am b/src/autodisc/Makefile.am
new file mode 100644 (file)
index 0000000..17457ff
--- /dev/null
@@ -0,0 +1,5 @@
+noinst_LTLIBRARIES = libbabeltrace2-autodisc.la
+
+libbabeltrace2_autodisc_la_SOURCES = \
+       autodisc.c \
+       autodisc.h
diff --git a/src/autodisc/autodisc.c b/src/autodisc/autodisc.c
new file mode 100644 (file)
index 0000000..85b6bfc
--- /dev/null
@@ -0,0 +1,761 @@
+/*
+ * Copyright (c) 2019 EfficiOS Inc. and Linux Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#define BT_LOG_TAG "CLI-CFG-SRC-AUTO-DISC"
+#define BT_LOG_OUTPUT_LEVEL log_level
+#include "logging/log.h"
+
+#include "autodisc.h"
+#include "common/common.h"
+
+#define BT_AUTODISC_LOG_AND_APPEND(_lvl, _fmt, ...)                            \
+       do {                                                            \
+               BT_LOG_WRITE(_lvl, BT_LOG_TAG, _fmt, ##__VA_ARGS__);    \
+               (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN( \
+                       "Source auto-discovery", _fmt, ##__VA_ARGS__);          \
+       } while (0)
+
+#define BT_AUTODISC_LOGE_APPEND_CAUSE(_fmt, ...)                               \
+       BT_AUTODISC_LOG_AND_APPEND(BT_LOG_ERROR, _fmt, ##__VA_ARGS__)
+
+/* Finalize and free a `struct auto_source_discovery_result`. */
+
+static
+void auto_source_discovery_result_destroy(struct auto_source_discovery_result *res)
+{
+       if (res) {
+               g_free(res->group);
+               bt_value_put_ref(res->inputs);
+               bt_value_put_ref(res->original_input_indices);
+               g_free(res);
+       }
+}
+
+/* Allocate and initialize a `struct auto_source_discovery_result`. */
+
+static
+struct auto_source_discovery_result *auto_source_discovery_result_create(
+               const char *plugin_name, const char *source_cc_name,
+               const char *group, bt_logging_level log_level)
+{
+       struct auto_source_discovery_result *res;
+
+       res = g_new0(struct auto_source_discovery_result, 1);
+       if (!res) {
+               BT_AUTODISC_LOGE_APPEND_CAUSE(
+                       "Failed to allocate a auto_source_discovery_result structure.");
+               goto error;
+       }
+
+       res->plugin_name = plugin_name;
+       res->source_cc_name = source_cc_name;
+       res->group = g_strdup(group);
+       if (group && !res->group) {
+               BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to allocate a string.");
+               goto error;
+       }
+
+       res->inputs = bt_value_array_create();
+       if (!res->inputs) {
+               BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to allocate an array value.");
+               goto error;
+       }
+
+       res->original_input_indices = bt_value_array_create();
+       if (!res->original_input_indices) {
+               BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to allocate an array value.");
+               goto error;
+       }
+
+       goto end;
+error:
+       auto_source_discovery_result_destroy(res);
+
+end:
+       return res;
+}
+
+/* Finalize a `struct auto_source_discovery`. */
+
+void auto_source_discovery_fini(struct auto_source_discovery *auto_disc)
+{
+       if (auto_disc->results) {
+               g_ptr_array_free(auto_disc->results, TRUE);
+       }
+}
+
+/* Initialize an already allocated `struct auto_source_discovery`. */
+
+int auto_source_discovery_init(struct auto_source_discovery *auto_disc)
+{
+       int status;
+
+       auto_disc->results = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) auto_source_discovery_result_destroy);
+
+       if (!auto_disc->results) {
+               goto error;
+       }
+
+       status = 0;
+       goto end;
+
+error:
+       auto_source_discovery_fini(auto_disc);
+       status = -1;
+
+end:
+
+       return status;
+}
+
+static
+const bt_value *borrow_array_value_last_element_const(const bt_value *array)
+{
+       uint64_t last_index = bt_value_array_get_size(array) - 1;
+
+       return bt_value_array_borrow_element_by_index_const(array, last_index);
+}
+
+/*
+ * Assign `input` to source component class `source_cc_name` of plugin
+ * `plugin_name`, in the group with key `group`.
+ */
+
+static
+int auto_source_discovery_add(struct auto_source_discovery *auto_disc,
+               const char *plugin_name,
+               const char *source_cc_name,
+               const char *group,
+               const char *input,
+               uint64_t original_input_index,
+               bt_logging_level log_level)
+{
+       int status;
+       bt_value_array_append_element_status append_status;
+       guint len;
+       guint i;
+       struct auto_source_discovery_result *res = NULL;
+       bool append_index;
+
+       len = auto_disc->results->len;
+       i = len;
+
+       if (group) {
+               for (i = 0; i < len; i++) {
+                       res = g_ptr_array_index(auto_disc->results, i);
+
+                       if (strcmp(res->plugin_name, plugin_name) != 0) {
+                               continue;
+                       }
+
+                       if (strcmp(res->source_cc_name, source_cc_name) != 0) {
+                               continue;
+                       }
+
+                       if (g_strcmp0(res->group, group) != 0) {
+                               continue;
+                       }
+
+                       break;
+               }
+       }
+
+       if (i == len) {
+               /* Add a new result entry. */
+               res = auto_source_discovery_result_create(plugin_name,
+                       source_cc_name, group, log_level);
+               if (!res) {
+                       goto error;
+               }
+
+               g_ptr_array_add(auto_disc->results, res);
+       }
+
+       append_status = bt_value_array_append_string_element(res->inputs, input);
+       if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
+               BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to append a string value.");
+               goto error;
+       }
+
+       /*
+        * Append `original_input_index` to `original_input_indices` if not
+        * there already.  We process the `inputs` array in order, so if it is
+        * present, it has to be the last element.
+        */
+       if (bt_value_array_is_empty(res->original_input_indices)) {
+               append_index = true;
+       } else {
+               const bt_value *last_index_value;
+               uint64_t last_index;
+
+               last_index_value =
+                       borrow_array_value_last_element_const(res->original_input_indices);
+               last_index = bt_value_integer_unsigned_get(last_index_value);
+
+               BT_ASSERT(last_index <= original_input_index);
+
+               append_index = (last_index != original_input_index);
+       }
+
+       if (append_index) {
+               append_status = bt_value_array_append_unsigned_integer_element(
+                       res->original_input_indices, original_input_index);
+
+               if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
+                       BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to append an unsigned integer value.");
+                       goto error;
+               }
+       }
+
+       status = 0;
+       goto end;
+
+error:
+       status = -1;
+
+end:
+       return status;
+}
+
+static
+int convert_weight_value(const bt_value *weight_value, double *weight,
+               const char *plugin_name, const char *source_cc_name,
+               const char *input, const char *input_type,
+               bt_logging_level log_level)
+{
+       enum bt_value_type weight_value_type;
+       int status;
+
+       weight_value_type = bt_value_get_type(weight_value);
+
+       if (weight_value_type == BT_VALUE_TYPE_REAL) {
+               *weight = bt_value_real_get(weight_value);
+       } else if (weight_value_type == BT_VALUE_TYPE_SIGNED_INTEGER) {
+               /* Accept signed integer as a convenience for "return 0" or "return 1" in Python. */
+               *weight = bt_value_integer_signed_get(weight_value);
+       } else {
+               BT_LOGW("babeltrace.support-info query: unexpected type for weight: "
+                       "component-class-name=source.%s.%s, input=%s, input-type=%s, "
+                       "expected-entry-type=%s, actual-entry-type=%s",
+                       plugin_name, source_cc_name, input, input_type,
+                       bt_common_value_type_string(BT_VALUE_TYPE_REAL),
+                       bt_common_value_type_string(bt_value_get_type(weight_value)));
+               goto error;
+       }
+
+       if (*weight < 0.0 || *weight > 1.0) {
+               BT_LOGW("babeltrace.support-info query: weight value is out of range [0.0, 1.0]: "
+                       "component-class-name=source.%s.%s, input=%s, input-type=%s, "
+                       "weight=%f",
+                       plugin_name, source_cc_name, input, input_type, *weight);
+               goto error;
+       }
+
+       status = 0;
+       goto end;
+
+error:
+       status = -1;
+
+end:
+       return status;
+}
+
+static
+bt_query_executor_query_status simple_query(const bt_component_class *comp_cls,
+               const char *obj, const bt_value *params,
+               bt_logging_level log_level, const bt_value **result)
+{
+       bt_query_executor_query_status status;
+       bt_query_executor_set_logging_level_status set_logging_level_status;
+       bt_query_executor *query_exec;
+
+       query_exec = bt_query_executor_create(comp_cls, obj, params);
+       if (!query_exec) {
+               BT_AUTODISC_LOGE_APPEND_CAUSE("Cannot create a query executor.");
+               status = BT_QUERY_EXECUTOR_QUERY_STATUS_MEMORY_ERROR;
+               goto end;
+       }
+
+       set_logging_level_status = bt_query_executor_set_logging_level(query_exec, log_level);
+       if (set_logging_level_status != BT_QUERY_EXECUTOR_SET_LOGGING_LEVEL_STATUS_OK) {
+               BT_AUTODISC_LOGE_APPEND_CAUSE(
+                       "Cannot set query executor's logging level: "
+                       "log-level=%s",
+                       bt_common_logging_level_string(log_level));
+               status = (int) set_logging_level_status;
+               goto end;
+       }
+
+       status = bt_query_executor_query(query_exec, result);
+
+end:
+       bt_query_executor_put_ref(query_exec);
+
+       return status;
+}
+
+
+/*
+ * Query all known source components to see if any of them can handle `input`
+ * as the given `type`(arbitrary string, directory or file).
+ *
+ * If `plugin_restrict` is non-NULL, only query source component classes provided
+ * by the plugin with that name.
+ *
+ * If `component_class_restrict` is non-NULL, only query source component classes
+ * with that name.
+ *
+ * Return:
+ *
+ * - > 0 on success, if no source component class has reported that it handles `input`
+ * -   0 on success, if a source component class has reported that it handles `input`
+ * - < 0 on failure (e.g. memory error)
+ */
+static
+int support_info_query_all_sources(const char *input,
+               const char *input_type,
+               uint64_t original_input_index,
+               const bt_plugin **plugins,
+               size_t plugin_count,
+               const char *component_class_restrict,
+               enum bt_logging_level log_level,
+               struct auto_source_discovery *auto_disc)
+{
+       bt_value_map_insert_entry_status insert_status;
+       bt_value *query_params = NULL;
+       int status;
+       size_t i_plugins;
+       const struct bt_value *query_result = NULL;
+       struct {
+               const bt_component_class_source *source;
+               const bt_plugin *plugin;
+               const bt_value *group;
+               double weigth;
+       } winner = { NULL, NULL, NULL, 0 };
+
+       query_params = bt_value_map_create();
+       if (!query_params) {
+               BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to allocate a map value.");
+               goto error;
+       }
+
+       insert_status = bt_value_map_insert_string_entry(query_params, "input", input);
+       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+               BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to insert a map entry.");
+               goto error;
+       }
+
+       insert_status = bt_value_map_insert_string_entry(query_params, "type", input_type);
+       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
+               BT_AUTODISC_LOGE_APPEND_CAUSE("Failed to insert a map entry.");
+               goto error;
+       }
+
+       for (i_plugins = 0; i_plugins < plugin_count; i_plugins++) {
+               const bt_plugin *plugin;
+               const char *plugin_name;
+               uint64_t source_count;
+               uint64_t i_sources;
+
+               plugin = plugins[i_plugins];
+               plugin_name = bt_plugin_get_name(plugin);
+
+               source_count = bt_plugin_get_source_component_class_count(plugin);
+
+               for (i_sources = 0; i_sources < source_count; i_sources++) {
+                       const bt_component_class_source *source_cc;
+                       const bt_component_class *cc;
+                       const char *source_cc_name;
+                       bt_query_executor_query_status query_status;
+
+                       source_cc = bt_plugin_borrow_source_component_class_by_index_const(plugin, i_sources);
+                       cc = bt_component_class_source_as_component_class_const(source_cc);
+                       source_cc_name = bt_component_class_get_name(cc);
+
+                       /*
+                        * If the search is restricted to a specific component class, only consider the
+                        * component classes with that name.
+                        */
+                       if (component_class_restrict && strcmp(component_class_restrict, source_cc_name) != 0) {
+                               continue;
+                       }
+
+                       BT_LOGD("babeltrace.support-info query: before: component-class-name=source.%s.%s, input=%s, "
+                               "type=%s", plugin_name, source_cc_name, input, input_type);
+
+                       BT_VALUE_PUT_REF_AND_RESET(query_result);
+                       query_status = simple_query(cc, "babeltrace.support-info",
+                               query_params, log_level, &query_result);
+
+                       if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_OK) {
+                               double weight;
+                               const bt_value *group_value = NULL;
+                               enum bt_value_type query_result_type;
+
+                               BT_ASSERT(query_result);
+
+                               query_result_type = bt_value_get_type(query_result);
+
+                               if (query_result_type == BT_VALUE_TYPE_REAL || query_result_type == BT_VALUE_TYPE_SIGNED_INTEGER) {
+                                       if (convert_weight_value(query_result, &weight, plugin_name, source_cc_name, input, input_type, log_level) != 0) {
+                                               /* convert_weight_value has already warned. */
+                                               continue;
+                                       }
+                               } else if (query_result_type == BT_VALUE_TYPE_MAP) {
+                                       const bt_value *weight_value;
+
+                                       if (!bt_value_map_has_entry(query_result, "weight")) {
+                                               BT_LOGW("babeltrace.support-info query: result is missing `weight` entry: "
+                                                       "component-class-name=source.%s.%s, input=%s, input-type=%s",
+                                                       bt_plugin_get_name(plugin),
+                                                       bt_component_class_get_name(cc), input,
+                                                       input_type);
+                                               continue;
+                                       }
+
+                                       weight_value = bt_value_map_borrow_entry_value_const(query_result, "weight");
+                                       BT_ASSERT(weight_value);
+
+                                       if (convert_weight_value(weight_value, &weight, plugin_name, source_cc_name, input, input_type, log_level) != 0) {
+                                               /* convert_weight_value has already warned. */
+                                               continue;
+                                       }
+
+                                       if (bt_value_map_has_entry(query_result, "group")) {
+                                               enum bt_value_type group_value_type;
+
+                                               group_value = bt_value_map_borrow_entry_value_const(query_result, "group");
+                                               BT_ASSERT(group_value);
+
+                                               group_value_type = bt_value_get_type(group_value);
+
+                                               if (group_value_type == BT_VALUE_TYPE_NULL) {
+                                                       /* Do as if no value was passed. */
+                                                       group_value = NULL;
+                                               } else if (bt_value_get_type(group_value) != BT_VALUE_TYPE_STRING) {
+                                                       BT_LOGW("babeltrace.support-info query: unexpected type for entry `group`: "
+                                                               "component-class-name=source.%s.%s, input=%s, input-type=%s, "
+                                                               "expected-entry-type=%s,%s, actual-entry-type=%s",
+                                                               bt_plugin_get_name(plugin),
+                                                               bt_component_class_get_name(cc), input,
+                                                               input_type,
+                                                               bt_common_value_type_string(BT_VALUE_TYPE_NULL),
+                                                               bt_common_value_type_string(BT_VALUE_TYPE_STRING),
+                                                               bt_common_value_type_string(bt_value_get_type(group_value)));
+                                                       continue;
+                                               }
+                                       }
+                               } else {
+                                       BT_LOGW("babeltrace.support-info query: unexpected result type: "
+                                               "component-class-name=source.%s.%s, input=%s, input-type=%s, "
+                                               "expected-types=%s,%s,%s, actual-type=%s",
+                                               bt_plugin_get_name(plugin),
+                                               bt_component_class_get_name(cc), input,
+                                               input_type,
+                                               bt_common_value_type_string(BT_VALUE_TYPE_REAL),
+                                               bt_common_value_type_string(BT_VALUE_TYPE_MAP),
+                                               bt_common_value_type_string(BT_VALUE_TYPE_SIGNED_INTEGER),
+                                               bt_common_value_type_string(bt_value_get_type(query_result)));
+                                       continue;
+                               }
+
+                               BT_LOGD("babeltrace.support-info query: success: component-class-name=source.%s.%s, input=%s, "
+                                       "type=%s, weight=%f\n",
+                                       bt_plugin_get_name(plugin), bt_component_class_get_name(cc), input,
+                                       input_type, weight);
+
+                               if (weight > winner.weigth) {
+                                       winner.source = source_cc;
+                                       winner.plugin = plugin;
+
+                                       bt_value_put_ref(winner.group);
+                                       winner.group = group_value;
+                                       bt_value_get_ref(winner.group);
+
+                                       winner.weigth = weight;
+                               }
+                       } else if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_ERROR) {
+                               BT_AUTODISC_LOGE_APPEND_CAUSE("babeltrace.support-info query failed.");
+                               goto error;
+                       } else if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_MEMORY_ERROR) {
+                               BT_AUTODISC_LOGE_APPEND_CAUSE("Memory error.");
+                               goto error;
+                       } else {
+                               BT_LOGD("babeltrace.support-info query: failure: component-class-name=source.%s.%s, input=%s, "
+                                       "type=%s, status=%s\n",
+                                       bt_plugin_get_name(plugin), bt_component_class_get_name(cc), input,
+                                       input_type,
+                                       bt_common_func_status_string(query_status));
+                       }
+               }
+       }
+
+       if (winner.source) {
+               const char *source_name;
+               const char *plugin_name;
+               const char *group;
+
+               source_name = bt_component_class_get_name(
+                       bt_component_class_source_as_component_class_const(winner.source));
+               plugin_name = bt_plugin_get_name(winner.plugin);
+               group = winner.group ? bt_value_string_get(winner.group) : NULL;
+
+               BT_LOGI("Input %s is awarded to component class source.%s.%s with weight %f",
+                       input, plugin_name, source_name, winner.weigth);
+
+               status = auto_source_discovery_add(auto_disc, plugin_name,
+                       source_name, group, input, original_input_index, log_level);
+               if (status != 0) {
+                       goto error;
+               }
+       } else {
+               BT_LOGI("Input %s (%s) was not recognized by any source component class.",
+                       input, input_type);
+               status = 1;
+       }
+
+       goto end;
+
+error:
+       status = -1;
+
+end:
+       bt_value_put_ref(query_result);
+       bt_value_put_ref(query_params);
+       bt_value_put_ref(winner.group);
+
+       return status;
+}
+
+/*
+ * Look for a source component class that recognizes `input` as an arbitrary
+ * string.
+ *
+ * Same return value semantic as `support_info_query_all_sources`.
+ */
+
+static
+int auto_discover_source_for_input_as_string(const char *input,
+               uint64_t original_input_index,
+               const bt_plugin **plugins,
+               size_t plugin_count,
+               const char *component_class_restrict,
+               enum bt_logging_level log_level,
+               struct auto_source_discovery *auto_disc)
+{
+       return support_info_query_all_sources(input, "string",
+               original_input_index, plugins, plugin_count,
+               component_class_restrict, log_level, auto_disc);
+}
+
+static
+int auto_discover_source_for_input_as_dir_or_file_rec(GString *input,
+               uint64_t original_input_index,
+               const bt_plugin **plugins,
+               size_t plugin_count,
+               const char *component_class_restrict,
+               enum bt_logging_level log_level,
+               struct auto_source_discovery *auto_disc)
+{
+       int status;
+       GError *error = NULL;
+
+       if (g_file_test(input->str, G_FILE_TEST_IS_REGULAR)) {
+               /* It's a file. */
+               status = support_info_query_all_sources(input->str,
+                       "file", original_input_index, plugins, plugin_count,
+                       component_class_restrict, log_level, auto_disc);
+       } else if (g_file_test(input->str, G_FILE_TEST_IS_DIR)) {
+               GDir *dir;
+               const gchar *dirent;
+               gsize saved_input_len;
+               int dir_status = 1;
+
+               /* It's a directory. */
+               status = support_info_query_all_sources(input->str,
+                       "directory", original_input_index, plugins,
+                       plugin_count, component_class_restrict, log_level,
+                       auto_disc);
+
+               if (status < 0) {
+                       /* Fatal error. */
+                       goto error;
+               } else if (status == 0) {
+                       /*
+                        * A component class claimed this input as a directory,
+                        * don't recurse.
+                        */
+                       goto end;
+               }
+
+               dir = g_dir_open(input->str, 0, &error);
+               if (!dir) {
+                       const char *fmt = "Failed to open directory %s: %s";
+                       BT_LOGW(fmt, input->str, error->message);
+
+                       if (error->code == G_FILE_ERROR_ACCES) {
+                               /* This is not a fatal error, we just skip it. */
+                               status = 1;
+                               goto end;
+                       } else {
+                               BT_AUTODISC_LOGE_APPEND_CAUSE(fmt, input->str,
+                                       error->message);
+                               goto error;
+                       }
+               }
+
+               saved_input_len = input->len;
+
+               do {
+                       errno = 0;
+                       dirent = g_dir_read_name(dir);
+                       if (dirent) {
+                               g_string_append_c_inline(input, G_DIR_SEPARATOR);
+                               g_string_append(input, dirent);
+
+                               status = auto_discover_source_for_input_as_dir_or_file_rec(
+                                       input, original_input_index, plugins, plugin_count,
+                                       component_class_restrict, log_level, auto_disc);
+
+                               g_string_truncate(input, saved_input_len);
+
+                               if (status < 0) {
+                                       /* Fatal error. */
+                                       goto error;
+                               } else if (status == 0) {
+                                       dir_status = 0;
+                               }
+                       } else if (errno != 0) {
+                               BT_LOGW_ERRNO("Failed to read directory entry", ": dir=%s", input->str);
+                               goto error;
+                       }
+               } while (dirent);
+
+               status = dir_status;
+
+               g_dir_close(dir);
+       } else {
+               BT_LOGD("Skipping %s, not a file or directory", input->str);
+               status = 1;
+       }
+
+       goto end;
+
+error:
+       status = -1;
+
+end:
+
+       if (error) {
+               g_error_free(error);
+       }
+
+       return status;
+}
+
+/*
+ * Look for a source component class that recognizes `input` as a directory or
+ * file.  If `input` is a directory and is not directly recognized, recurse and
+ * apply the same logic to children nodes.
+ *
+ * Same return value semantic as `support_info_query_all_sources`.
+ */
+
+static
+int auto_discover_source_for_input_as_dir_or_file(const char *input,
+               uint64_t original_input_index,
+               const bt_plugin **plugins,
+               size_t plugin_count,
+               const char *component_class_restrict,
+               enum bt_logging_level log_level,
+               struct auto_source_discovery *auto_disc)
+{
+       GString *mutable_input;
+       int status;
+
+       mutable_input = g_string_new(input);
+       if (!mutable_input) {
+               status = -1;
+               goto end;
+       }
+
+       status = auto_discover_source_for_input_as_dir_or_file_rec(
+               mutable_input, original_input_index, plugins, plugin_count,
+               component_class_restrict, log_level, auto_disc);
+
+       g_string_free(mutable_input, TRUE);
+end:
+       return status;
+}
+
+int auto_discover_source_components(
+               const bt_value *inputs,
+               const bt_plugin **plugins,
+               size_t plugin_count,
+               const char *component_class_restrict,
+               enum bt_logging_level log_level,
+               struct auto_source_discovery *auto_disc)
+{
+       uint64_t i_inputs, input_count;
+       int status;
+
+       input_count = bt_value_array_get_size(inputs);
+
+       for (i_inputs = 0; i_inputs < input_count; i_inputs++) {
+               const bt_value *input_value;
+               const char *input;
+
+               input_value = bt_value_array_borrow_element_by_index_const(inputs, i_inputs);
+               input = bt_value_string_get(input_value);
+               status = auto_discover_source_for_input_as_string(input, i_inputs,
+                       plugins, plugin_count, component_class_restrict,
+                       log_level, auto_disc);
+               if (status < 0) {
+                       /* Fatal error. */
+                       goto end;
+               } else if (status == 0) {
+                       /* A component class has claimed this input as an arbitrary string. */
+                       continue;
+               }
+
+               status = auto_discover_source_for_input_as_dir_or_file(input,
+                       i_inputs, plugins, plugin_count,
+                       component_class_restrict, log_level, auto_disc);
+               if (status < 0) {
+                       /* Fatal error. */
+                       goto end;
+               } else if (status == 0) {
+                       /*
+                        * This input (or something under it) was recognized.
+                        */
+                       continue;
+               }
+
+               BT_LOGW("No trace was found based on input `%s`.", input);
+       }
+
+       status = 0;
+end:
+       return status;
+}
diff --git a/src/autodisc/autodisc.h b/src/autodisc/autodisc.h
new file mode 100644 (file)
index 0000000..09b209a
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef AUTODISC_AUTODISC_H
+#define AUTODISC_AUTODISC_H
+
+/*
+ * Copyright (c) 2019 EfficiOS Inc. and Linux Foundation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <glib.h>
+
+#include <babeltrace2/babeltrace.h>
+
+struct auto_source_discovery {
+       /* Array of `struct auto_source_discovery_result *`. */
+       GPtrArray *results;
+};
+
+/* Value type of the `auto_source_discovery::results` array. */
+
+struct auto_source_discovery_result {
+       /*
+        * `plugin_name` and `source_cc_name` are borrowed from the plugin and source
+        * component class (which outlive this structure).
+        */
+       const char *plugin_name;
+       const char *source_cc_name;
+
+       /*
+        * `group` is owned by this structure.
+        *
+        * May be NULL, to mean "no group".
+        */
+       gchar *group;
+
+       /* Array of input strings. */
+       bt_value *inputs;
+
+       /*
+        * Array of integers: indices of the original inputs that contributed
+        * to this result.
+        */
+       bt_value *original_input_indices;
+};
+
+int auto_source_discovery_init(struct auto_source_discovery *auto_disc);
+void auto_source_discovery_fini(struct auto_source_discovery *auto_disc);
+
+/*
+ * Given `inputs` a list of strings, query source component classes to discover
+ * which source components should be instantiated to deal with these inputs.
+ *
+ * Return 0 if execution completed successfully, < 0 otherwise.
+ */
+
+int auto_discover_source_components(
+               const bt_value *inputs,
+               const bt_plugin **plugins,
+               size_t plugin_count,
+               const char *component_class_restrict,
+               enum bt_logging_level log_level,
+               struct auto_source_discovery *auto_disc);
+
+#endif /* AUTODISC_AUTODISC_H */
index 0f0884af379186c65ac076f40085a58cb73efacb..56e2a899e7cf1260a1ef22821432bded3fb0fddb 100644 (file)
@@ -32,8 +32,6 @@ babeltrace2_bin_SOURCES = \
        babeltrace2-cfg-cli-args-default.c \
        babeltrace2-cfg-cli-params-arg.c \
        babeltrace2-cfg-cli-params-arg.h \
-       babeltrace2-cfg-src-auto-disc.c \
-       babeltrace2-cfg-src-auto-disc.h \
        babeltrace2-plugins.c \
        babeltrace2-plugins.h \
        babeltrace2-query.c \
@@ -55,6 +53,7 @@ babeltrace2_bin_LDFLAGS = $(LD_NO_AS_NEEDED)
 # directly).
 babeltrace2_bin_LDADD = \
        $(top_builddir)/src/argpar/libbabeltrace2-argpar.la \
+       $(top_builddir)/src/autodisc/libbabeltrace2-autodisc.la \
        $(top_builddir)/src/lib/libbabeltrace2.la \
        $(top_builddir)/src/compat/libcompat.la \
        $(top_builddir)/src/common/libbabeltrace2-common.la \
index e50a3d236f0f5639a3eb7251239d1c8a33b42557..81dd05bbb1ae856db79e8e2243a77524833c904f 100644 (file)
@@ -42,7 +42,8 @@
 #include "babeltrace2-cfg-cli-args-connect.h"
 #include "babeltrace2-cfg-cli-params-arg.h"
 #include "babeltrace2-plugins.h"
-#include "babeltrace2-cfg-src-auto-disc.h"
+#include "babeltrace2-query.h"
+#include "autodisc/autodisc.h"
 #include "common/version.h"
 
 static const int cli_default_log_level = BT_LOG_WARNING;
@@ -4267,9 +4268,25 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[],
                        }
                } else {
                        int status;
+                       size_t plugin_count;
+                       const bt_plugin **plugins;
+                       const bt_plugin *plugin;
 
-                       status = auto_discover_source_components(plugin_paths, non_opts,
-                               auto_source_discovery_restrict_plugin_name,
+                       status = require_loaded_plugins(plugin_paths);
+                       if (status != 0) {
+                               goto error;
+                       }
+
+                       if (auto_source_discovery_restrict_plugin_name) {
+                               plugin_count = 1;
+                               plugin = find_loaded_plugin(auto_source_discovery_restrict_plugin_name);
+                               plugins = &plugin;
+                       } else {
+                               plugin_count = get_loaded_plugins_count();
+                               plugins = borrow_loaded_plugins();
+                       }
+
+                       status = auto_discover_source_components(non_opts, plugins, plugin_count,
                                auto_source_discovery_restrict_component_class_name,
                                *default_log_level >= 0 ? *default_log_level : cli_default_log_level,
                                &auto_disc);
diff --git a/src/cli/babeltrace2-cfg-src-auto-disc.c b/src/cli/babeltrace2-cfg-src-auto-disc.c
deleted file mode 100644 (file)
index 2f559a8..0000000
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * Copyright (c) 2019 EfficiOS Inc. and Linux Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#define BT_LOG_TAG "CLI-CFG-SRC-AUTO-DISC"
-#include "logging.h"
-
-#include "babeltrace2-cfg-src-auto-disc.h"
-#include "babeltrace2-plugins.h"
-#include "babeltrace2-query.h"
-#include "common/common.h"
-
-/* Finalize and free a `struct auto_source_discovery_result`. */
-
-static
-void auto_source_discovery_result_destroy(struct auto_source_discovery_result *res)
-{
-       if (res) {
-               g_free(res->group);
-               bt_value_put_ref(res->inputs);
-               bt_value_put_ref(res->original_input_indices);
-               g_free(res);
-       }
-}
-
-/* Allocate and initialize a `struct auto_source_discovery_result`. */
-
-static
-struct auto_source_discovery_result *auto_source_discovery_result_create(
-               const char *plugin_name, const char *source_cc_name,
-               const char *group)
-{
-       struct auto_source_discovery_result *res;
-
-       res = g_new0(struct auto_source_discovery_result, 1);
-       if (!res) {
-               BT_CLI_LOGE_APPEND_CAUSE(
-                       "Failed to allocate a auto_source_discovery_result structure.");
-               goto error;
-       }
-
-       res->plugin_name = plugin_name;
-       res->source_cc_name = source_cc_name;
-       res->group = g_strdup(group);
-       if (group && !res->group) {
-               BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate a string.");
-               goto error;
-       }
-
-       res->inputs = bt_value_array_create();
-       if (!res->inputs) {
-               BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate an array value.");
-               goto error;
-       }
-
-       res->original_input_indices = bt_value_array_create();
-       if (!res->original_input_indices) {
-               BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate an array value.");
-               goto error;
-       }
-
-       goto end;
-error:
-       auto_source_discovery_result_destroy(res);
-
-end:
-       return res;
-}
-
-/* Finalize a `struct auto_source_discovery`. */
-
-void auto_source_discovery_fini(struct auto_source_discovery *auto_disc)
-{
-       if (auto_disc->results) {
-               g_ptr_array_free(auto_disc->results, TRUE);
-       }
-}
-
-/* Initialize an already allocated `struct auto_source_discovery`. */
-
-int auto_source_discovery_init(struct auto_source_discovery *auto_disc)
-{
-       int status;
-
-       auto_disc->results = g_ptr_array_new_with_free_func(
-               (GDestroyNotify) auto_source_discovery_result_destroy);
-
-       if (!auto_disc->results) {
-               goto error;
-       }
-
-       status = 0;
-       goto end;
-
-error:
-       auto_source_discovery_fini(auto_disc);
-       status = -1;
-
-end:
-
-       return status;
-}
-
-static
-const bt_value *borrow_array_value_last_element_const(const bt_value *array)
-{
-       uint64_t last_index = bt_value_array_get_size(array) - 1;
-
-       return bt_value_array_borrow_element_by_index_const(array, last_index);
-}
-
-/*
- * Assign `input` to source component class `source_cc_name` of plugin
- * `plugin_name`, in the group with key `group`.
- */
-
-static
-int auto_source_discovery_add(struct auto_source_discovery *auto_disc,
-               const char *plugin_name,
-               const char *source_cc_name,
-               const char *group,
-               const char *input,
-               uint64_t original_input_index)
-{
-       int status;
-       bt_value_array_append_element_status append_status;
-       guint len;
-       guint i;
-       struct auto_source_discovery_result *res = NULL;
-       bool append_index;
-
-       len = auto_disc->results->len;
-       i = len;
-
-       if (group) {
-               for (i = 0; i < len; i++) {
-                       res = g_ptr_array_index(auto_disc->results, i);
-
-                       if (strcmp(res->plugin_name, plugin_name) != 0) {
-                               continue;
-                       }
-
-                       if (strcmp(res->source_cc_name, source_cc_name) != 0) {
-                               continue;
-                       }
-
-                       if (g_strcmp0(res->group, group) != 0) {
-                               continue;
-                       }
-
-                       break;
-               }
-       }
-
-       if (i == len) {
-               /* Add a new result entry. */
-               res = auto_source_discovery_result_create(plugin_name,
-                       source_cc_name, group);
-               if (!res) {
-                       goto error;
-               }
-
-               g_ptr_array_add(auto_disc->results, res);
-       }
-
-       append_status = bt_value_array_append_string_element(res->inputs, input);
-       if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
-               BT_CLI_LOGE_APPEND_CAUSE("Failed to append a string value.");
-               goto error;
-       }
-
-       /*
-        * Append `original_input_index` to `original_input_indices` if not
-        * there already.  We process the `inputs` array in order, so if it is
-        * present, it has to be the last element.
-        */
-       if (bt_value_array_is_empty(res->original_input_indices)) {
-               append_index = true;
-       } else {
-               const bt_value *last_index_value;
-               uint64_t last_index;
-
-               last_index_value =
-                       borrow_array_value_last_element_const(res->original_input_indices);
-               last_index = bt_value_integer_unsigned_get(last_index_value);
-
-               BT_ASSERT(last_index <= original_input_index);
-
-               append_index = (last_index != original_input_index);
-       }
-
-       if (append_index) {
-               append_status = bt_value_array_append_unsigned_integer_element(
-                       res->original_input_indices, original_input_index);
-
-               if (append_status != BT_VALUE_ARRAY_APPEND_ELEMENT_STATUS_OK) {
-                       BT_CLI_LOGE_APPEND_CAUSE("Failed to append an unsigned integer value.");
-                       goto error;
-               }
-       }
-
-       status = 0;
-       goto end;
-
-error:
-       status = -1;
-
-end:
-       return status;
-}
-
-static
-int convert_weight_value(const bt_value *weight_value, double *weight,
-               const char *plugin_name, const char *source_cc_name,
-               const char *input, const char *input_type)
-{
-       enum bt_value_type weight_value_type;
-       int status;
-
-       weight_value_type = bt_value_get_type(weight_value);
-
-       if (weight_value_type == BT_VALUE_TYPE_REAL) {
-               *weight = bt_value_real_get(weight_value);
-       } else if (weight_value_type == BT_VALUE_TYPE_SIGNED_INTEGER) {
-               /* Accept signed integer as a convenience for "return 0" or "return 1" in Python. */
-               *weight = bt_value_integer_signed_get(weight_value);
-       } else {
-               BT_LOGW("babeltrace.support-info query: unexpected type for weight: "
-                       "component-class-name=source.%s.%s, input=%s, input-type=%s, "
-                       "expected-entry-type=%s, actual-entry-type=%s",
-                       plugin_name, source_cc_name, input, input_type,
-                       bt_common_value_type_string(BT_VALUE_TYPE_REAL),
-                       bt_common_value_type_string(bt_value_get_type(weight_value)));
-               goto error;
-       }
-
-       if (*weight < 0.0 || *weight > 1.0) {
-               BT_LOGW("babeltrace.support-info query: weight value is out of range [0.0, 1.0]: "
-                       "component-class-name=source.%s.%s, input=%s, input-type=%s, "
-                       "weight=%f",
-                       plugin_name, source_cc_name, input, input_type, *weight);
-               goto error;
-       }
-
-       status = 0;
-       goto end;
-
-error:
-       status = -1;
-
-end:
-       return status;
-}
-
-/*
- * Query all known source components to see if any of them can handle `input`
- * as the given `type`(arbitrary string, directory or file).
- *
- * If `plugin_restrict` is non-NULL, only query source component classes provided
- * by the plugin with that name.
- *
- * If `component_class_restrict` is non-NULL, only query source component classes
- * with that name.
- *
- * Return:
- *
- * - > 0 on success, if no source component class has reported that it handles `input`
- * -   0 on success, if a source component class has reported that it handles `input`
- * - < 0 on failure (e.g. memory error)
- */
-static
-int support_info_query_all_sources(const char *input,
-               const char *input_type,
-               uint64_t original_input_index,
-               size_t plugin_count,
-               const char *plugin_restrict,
-               const char *component_class_restrict,
-               enum bt_logging_level log_level,
-               struct auto_source_discovery *auto_disc)
-{
-       bt_value_map_insert_entry_status insert_status;
-       bt_value *query_params = NULL;
-       int status;
-       size_t i_plugins;
-       const struct bt_value *query_result = NULL;
-       struct {
-               const bt_component_class_source *source;
-               const bt_plugin *plugin;
-               const bt_value *group;
-               double weigth;
-       } winner = { NULL, NULL, NULL, 0 };
-
-       query_params = bt_value_map_create();
-       if (!query_params) {
-               BT_CLI_LOGE_APPEND_CAUSE("Failed to allocate a map value.");
-               goto error;
-       }
-
-       insert_status = bt_value_map_insert_string_entry(query_params, "input", input);
-       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-               BT_CLI_LOGE_APPEND_CAUSE("Failed to insert a map entry.");
-               goto error;
-       }
-
-       insert_status = bt_value_map_insert_string_entry(query_params, "type", input_type);
-       if (insert_status != BT_VALUE_MAP_INSERT_ENTRY_STATUS_OK) {
-               BT_CLI_LOGE_APPEND_CAUSE("Failed to insert a map entry.");
-               goto error;
-       }
-
-       for (i_plugins = 0; i_plugins < plugin_count; i_plugins++) {
-               const bt_plugin *plugin;
-               const char *plugin_name;
-               uint64_t source_count;
-               uint64_t i_sources;
-
-               plugin = borrow_loaded_plugin(i_plugins);
-               plugin_name = bt_plugin_get_name(plugin);
-
-               /*
-                * If the search is restricted to a specific plugin, only consider
-                * the plugin with that name.
-                */
-               if (plugin_restrict && strcmp(plugin_restrict, plugin_name) != 0) {
-                       continue;
-               }
-
-               source_count = bt_plugin_get_source_component_class_count(plugin);
-
-               for (i_sources = 0; i_sources < source_count; i_sources++) {
-                       const bt_component_class_source *source_cc;
-                       const bt_component_class *cc;
-                       const char *source_cc_name;
-                       bt_query_executor_query_status query_status;
-
-                       source_cc = bt_plugin_borrow_source_component_class_by_index_const(plugin, i_sources);
-                       cc = bt_component_class_source_as_component_class_const(source_cc);
-                       source_cc_name = bt_component_class_get_name(cc);
-
-                       /*
-                        * If the search is restricted to a specific component class, only consider the
-                        * component classes with that name.
-                        */
-                       if (component_class_restrict && strcmp(component_class_restrict, source_cc_name) != 0) {
-                               continue;
-                       }
-
-                       BT_LOGD("babeltrace.support-info query: before: component-class-name=source.%s.%s, input=%s, "
-                               "type=%s", plugin_name, source_cc_name, input, input_type);
-
-                       BT_VALUE_PUT_REF_AND_RESET(query_result);
-                       query_status = cli_query(cc, "babeltrace.support-info",
-                               query_params, log_level, NULL, &query_result,
-                               NULL);
-
-                       if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_OK) {
-                               double weight;
-                               const bt_value *group_value = NULL;
-                               enum bt_value_type query_result_type;
-
-                               BT_ASSERT(query_result);
-
-                               query_result_type = bt_value_get_type(query_result);
-
-                               if (query_result_type == BT_VALUE_TYPE_REAL || query_result_type == BT_VALUE_TYPE_SIGNED_INTEGER) {
-                                       if (convert_weight_value(query_result, &weight, plugin_name, source_cc_name, input, input_type) != 0) {
-                                               /* convert_weight_value has already warned. */
-                                               continue;
-                                       }
-                               } else if (query_result_type == BT_VALUE_TYPE_MAP) {
-                                       const bt_value *weight_value;
-
-                                       if (!bt_value_map_has_entry(query_result, "weight")) {
-                                               BT_LOGW("babeltrace.support-info query: result is missing `weight` entry: "
-                                                       "component-class-name=source.%s.%s, input=%s, input-type=%s",
-                                                       bt_plugin_get_name(plugin),
-                                                       bt_component_class_get_name(cc), input,
-                                                       input_type);
-                                               continue;
-                                       }
-
-                                       weight_value = bt_value_map_borrow_entry_value_const(query_result, "weight");
-                                       BT_ASSERT(weight_value);
-
-                                       if (convert_weight_value(weight_value, &weight, plugin_name, source_cc_name, input, input_type) != 0) {
-                                               /* convert_weight_value has already warned. */
-                                               continue;
-                                       }
-
-                                       if (bt_value_map_has_entry(query_result, "group")) {
-                                               enum bt_value_type group_value_type;
-
-                                               group_value = bt_value_map_borrow_entry_value_const(query_result, "group");
-                                               BT_ASSERT(group_value);
-
-                                               group_value_type = bt_value_get_type(group_value);
-
-                                               if (group_value_type == BT_VALUE_TYPE_NULL) {
-                                                       /* Do as if no value was passed. */
-                                                       group_value = NULL;
-                                               } else if (bt_value_get_type(group_value) != BT_VALUE_TYPE_STRING) {
-                                                       BT_LOGW("babeltrace.support-info query: unexpected type for entry `group`: "
-                                                               "component-class-name=source.%s.%s, input=%s, input-type=%s, "
-                                                               "expected-entry-type=%s,%s, actual-entry-type=%s",
-                                                               bt_plugin_get_name(plugin),
-                                                               bt_component_class_get_name(cc), input,
-                                                               input_type,
-                                                               bt_common_value_type_string(BT_VALUE_TYPE_NULL),
-                                                               bt_common_value_type_string(BT_VALUE_TYPE_STRING),
-                                                               bt_common_value_type_string(bt_value_get_type(group_value)));
-                                                       continue;
-                                               }
-                                       }
-                               } else {
-                                       BT_LOGW("babeltrace.support-info query: unexpected result type: "
-                                               "component-class-name=source.%s.%s, input=%s, input-type=%s, "
-                                               "expected-types=%s,%s,%s, actual-type=%s",
-                                               bt_plugin_get_name(plugin),
-                                               bt_component_class_get_name(cc), input,
-                                               input_type,
-                                               bt_common_value_type_string(BT_VALUE_TYPE_REAL),
-                                               bt_common_value_type_string(BT_VALUE_TYPE_MAP),
-                                               bt_common_value_type_string(BT_VALUE_TYPE_SIGNED_INTEGER),
-                                               bt_common_value_type_string(bt_value_get_type(query_result)));
-                                       continue;
-                               }
-
-                               BT_LOGD("babeltrace.support-info query: success: component-class-name=source.%s.%s, input=%s, "
-                                       "type=%s, weight=%f\n",
-                                       bt_plugin_get_name(plugin), bt_component_class_get_name(cc), input,
-                                       input_type, weight);
-
-                               if (weight > winner.weigth) {
-                                       winner.source = source_cc;
-                                       winner.plugin = plugin;
-
-                                       bt_value_put_ref(winner.group);
-                                       winner.group = group_value;
-                                       bt_value_get_ref(winner.group);
-
-                                       winner.weigth = weight;
-                               }
-                       } else if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_ERROR) {
-                               BT_CLI_LOGE_APPEND_CAUSE("babeltrace.support-info query failed.");
-                               goto error;
-                       } else if (query_status == BT_QUERY_EXECUTOR_QUERY_STATUS_MEMORY_ERROR) {
-                               BT_CLI_LOGE_APPEND_CAUSE("Memory error.");
-                               goto error;
-                       } else {
-                               BT_LOGD("babeltrace.support-info query: failure: component-class-name=source.%s.%s, input=%s, "
-                                       "type=%s, status=%s\n",
-                                       bt_plugin_get_name(plugin), bt_component_class_get_name(cc), input,
-                                       input_type,
-                                       bt_common_func_status_string(query_status));
-                       }
-               }
-       }
-
-       if (winner.source) {
-               const char *source_name;
-               const char *plugin_name;
-               const char *group;
-
-               source_name = bt_component_class_get_name(
-                       bt_component_class_source_as_component_class_const(winner.source));
-               plugin_name = bt_plugin_get_name(winner.plugin);
-               group = winner.group ? bt_value_string_get(winner.group) : NULL;
-
-               BT_LOGI("Input %s is awarded to component class source.%s.%s with weight %f",
-                       input, plugin_name, source_name, winner.weigth);
-
-               status = auto_source_discovery_add(auto_disc, plugin_name,
-                       source_name, group, input, original_input_index);
-               if (status != 0) {
-                       goto error;
-               }
-       } else {
-               BT_LOGI("Input %s (%s) was not recognized by any source component class.",
-                       input, input_type);
-               status = 1;
-       }
-
-       goto end;
-
-error:
-       status = -1;
-
-end:
-       bt_value_put_ref(query_result);
-       bt_value_put_ref(query_params);
-       bt_value_put_ref(winner.group);
-
-       return status;
-}
-
-/*
- * Look for a source component class that recognizes `input` as an arbitrary
- * string.
- *
- * Same return value semantic as `support_info_query_all_sources`.
- */
-
-static
-int auto_discover_source_for_input_as_string(const char *input,
-               uint64_t original_input_index,
-               size_t plugin_count, const char *plugin_restrict,
-               const char *component_class_restrict,
-               enum bt_logging_level log_level,
-               struct auto_source_discovery *auto_disc)
-{
-       return support_info_query_all_sources(input, "string",
-               original_input_index, plugin_count, plugin_restrict,
-               component_class_restrict, log_level, auto_disc);
-}
-
-static
-int auto_discover_source_for_input_as_dir_or_file_rec(GString *input,
-               uint64_t original_input_index,
-               size_t plugin_count, const char *plugin_restrict,
-               const char *component_class_restrict,
-               enum bt_logging_level log_level,
-               struct auto_source_discovery *auto_disc)
-{
-       int status;
-       GError *error = NULL;
-
-       if (g_file_test(input->str, G_FILE_TEST_IS_REGULAR)) {
-               /* It's a file. */
-               status = support_info_query_all_sources(input->str,
-                       "file", original_input_index, plugin_count,
-                       plugin_restrict, component_class_restrict, log_level, auto_disc);
-       } else if (g_file_test(input->str, G_FILE_TEST_IS_DIR)) {
-               GDir *dir;
-               const gchar *dirent;
-               gsize saved_input_len;
-               int dir_status = 1;
-
-               /* It's a directory. */
-               status = support_info_query_all_sources(input->str,
-                       "directory", original_input_index, plugin_count,
-                       plugin_restrict, component_class_restrict, log_level,
-                       auto_disc);
-
-               if (status < 0) {
-                       /* Fatal error. */
-                       goto error;
-               } else if (status == 0) {
-                       /*
-                        * A component class claimed this input as a directory,
-                        * don't recurse.
-                        */
-                       goto end;
-               }
-
-               dir = g_dir_open(input->str, 0, &error);
-               if (!dir) {
-                       const char *fmt = "Failed to open directory %s: %s";
-                       BT_LOGW(fmt, input->str, error->message);
-
-                       if (error->code == G_FILE_ERROR_ACCES) {
-                               /* This is not a fatal error, we just skip it. */
-                               status = 1;
-                               goto end;
-                       } else {
-                               BT_CLI_LOGE_APPEND_CAUSE(fmt, input->str,
-                                       error->message);
-                               goto error;
-                       }
-               }
-
-               saved_input_len = input->len;
-
-               do {
-                       errno = 0;
-                       dirent = g_dir_read_name(dir);
-                       if (dirent) {
-                               g_string_append_c_inline(input, G_DIR_SEPARATOR);
-                               g_string_append(input, dirent);
-
-                               status = auto_discover_source_for_input_as_dir_or_file_rec(
-                                       input, original_input_index, plugin_count,
-                                       plugin_restrict, component_class_restrict,
-                                       log_level, auto_disc);
-
-                               g_string_truncate(input, saved_input_len);
-
-                               if (status < 0) {
-                                       /* Fatal error. */
-                                       goto error;
-                               } else if (status == 0) {
-                                       dir_status = 0;
-                               }
-                       } else if (errno != 0) {
-                               BT_LOGW_ERRNO("Failed to read directory entry", ": dir=%s", input->str);
-                               goto error;
-                       }
-               } while (dirent);
-
-               status = dir_status;
-
-               g_dir_close(dir);
-       } else {
-               BT_LOGD("Skipping %s, not a file or directory", input->str);
-               status = 1;
-       }
-
-       goto end;
-
-error:
-       status = -1;
-
-end:
-
-       if (error) {
-               g_error_free(error);
-       }
-
-       return status;
-}
-
-/*
- * Look for a source component class that recognizes `input` as a directory or
- * file.  If `input` is a directory and is not directly recognized, recurse and
- * apply the same logic to children nodes.
- *
- * Same return value semantic as `support_info_query_all_sources`.
- */
-
-static
-int auto_discover_source_for_input_as_dir_or_file(const char *input,
-               uint64_t original_input_index,
-               size_t plugin_count, const char *plugin_restrict,
-               const char *component_class_restrict,
-               enum bt_logging_level log_level,
-               struct auto_source_discovery *auto_disc)
-{
-       GString *mutable_input;
-       int status;
-
-       mutable_input = g_string_new(input);
-       if (!mutable_input) {
-               status = -1;
-               goto end;
-       }
-
-       status = auto_discover_source_for_input_as_dir_or_file_rec(
-               mutable_input, original_input_index, plugin_count, plugin_restrict,
-               component_class_restrict, log_level, auto_disc);
-
-       g_string_free(mutable_input, TRUE);
-end:
-       return status;
-}
-
-int auto_discover_source_components(
-               const bt_value *plugin_paths,
-               const bt_value *inputs,
-               const char *plugin_restrict,
-               const char *component_class_restrict,
-               enum bt_logging_level log_level,
-               struct auto_source_discovery *auto_disc)
-{
-       uint64_t i_inputs, input_count;
-       int status;
-       size_t plugin_count;
-
-       input_count = bt_value_array_get_size(inputs);
-
-       status = require_loaded_plugins(plugin_paths);
-       if (status != 0) {
-               goto end;
-       }
-
-       plugin_count = get_loaded_plugins_count();
-
-       for (i_inputs = 0; i_inputs < input_count; i_inputs++) {
-               const bt_value *input_value;
-               const char *input;
-
-               input_value = bt_value_array_borrow_element_by_index_const(inputs, i_inputs);
-               input = bt_value_string_get(input_value);
-               status = auto_discover_source_for_input_as_string(input, i_inputs,
-                       plugin_count, plugin_restrict, component_class_restrict,
-                       log_level, auto_disc);
-               if (status < 0) {
-                       /* Fatal error. */
-                       goto end;
-               } else if (status == 0) {
-                       /* A component class has claimed this input as an arbitrary string. */
-                       continue;
-               }
-
-               status = auto_discover_source_for_input_as_dir_or_file(input,
-                       i_inputs, plugin_count, plugin_restrict,
-                       component_class_restrict, log_level, auto_disc);
-               if (status < 0) {
-                       /* Fatal error. */
-                       goto end;
-               } else if (status == 0) {
-                       /*
-                        * This input (or something under it) was recognized.
-                        */
-                       continue;
-               }
-
-               BT_LOGW("No trace was found based on input `%s`.", input);
-       }
-
-       status = 0;
-end:
-       return status;
-}
diff --git a/src/cli/babeltrace2-cfg-src-auto-disc.h b/src/cli/babeltrace2-cfg-src-auto-disc.h
deleted file mode 100644 (file)
index d6192cd..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#ifndef CLI_BABELTRACE_CFG_SRC_AUTO_DISC_H
-#define CLI_BABELTRACE_CFG_SRC_AUTO_DISC_H
-
-/*
- * Copyright (c) 2019 EfficiOS Inc. and Linux Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include <glib.h>
-
-#include <babeltrace2/babeltrace.h>
-
-struct auto_source_discovery {
-       /* Array of `struct auto_source_discovery_result *`. */
-       GPtrArray *results;
-};
-
-/* Value type of the `auto_source_discovery::results` array. */
-
-struct auto_source_discovery_result {
-       /*
-        * `plugin_name` and `source_cc_name` are borrowed from the plugin and source
-        * component class (which outlive this structure).
-        */
-       const char *plugin_name;
-       const char *source_cc_name;
-
-       /*
-        * `group` is owned by this structure.
-        *
-        * May be NULL, to mean "no group".
-        */
-       gchar *group;
-
-       /* Array of input strings. */
-       bt_value *inputs;
-
-       /*
-        * Array of integers: indices of the original inputs that contributed
-        * to this result.
-        */
-       bt_value *original_input_indices;
-};
-
-int auto_source_discovery_init(struct auto_source_discovery *auto_disc);
-void auto_source_discovery_fini(struct auto_source_discovery *auto_disc);
-
-/*
- * Given `inputs` a list of strings, query source component classes to discover
- * which source components should be instantiated to deal with these inputs.
- *
- * Return 0 if execution completed successfully, < 0 otherwise.
- */
-
-int auto_discover_source_components(
-               const bt_value *plugin_paths,
-               const bt_value *inputs,
-               const char *plugin_filter,
-               const char *component_class_filter,
-               enum bt_logging_level log_level,
-               struct auto_source_discovery *auto_disc);
-
-#endif /* CLI_BABELTRACE_CFG_SRC_AUTO_DISC_H */
index 73123408a542a88045f22c340e489166e9f6a722..6be4e154832e41bb36fefc4ba8b99d066cc476f8 100644 (file)
@@ -77,6 +77,11 @@ size_t get_loaded_plugins_count(void)
        return loaded_plugins->len;
 }
 
+const bt_plugin **borrow_loaded_plugins(void)
+{
+       return (const bt_plugin **) loaded_plugins->pdata;
+}
+
 const bt_plugin *borrow_loaded_plugin(size_t index)
 {
        BT_ASSERT(index < loaded_plugins->len);
index 83a67930b885459277e94ccf3ad3361b7f3f5467..60354d370b1117db19a841d28ef97b509810729d 100644 (file)
@@ -35,6 +35,7 @@ BT_HIDDEN int require_loaded_plugins(const bt_value *plugin_paths);
 
 BT_HIDDEN const bt_plugin *find_loaded_plugin(const char *name);
 BT_HIDDEN size_t get_loaded_plugins_count(void);
+BT_HIDDEN const bt_plugin **borrow_loaded_plugins(void);
 BT_HIDDEN const bt_plugin *borrow_loaded_plugin(size_t index);
 
 #endif /* CLI_BABELTRACE_PLUGINS_H */
index d4e9e4c47d645eab97d1adb5cbed1548b113b8a1..85f324d843a0b274930c01396598351ca67ae801 100755 (executable)
@@ -26,7 +26,7 @@ fi
 # shellcheck source=../../utils/utils.sh
 SH_TAP=1 source "$UTILSSH"
 
-NUM_TESTS=1
+NUM_TESTS=3
 
 plan_tests $NUM_TESTS
 
@@ -45,8 +45,21 @@ else
 fi
 
 stdout_expected_file="${data_dir}/stdout.expect"
+stdout_actual_file=$(mktemp -t stdout-actual.XXXXXX)
+stderr_actual_file=$(mktemp -t actual-stderr.XXXXXX)
 
-bt_diff_cli "$stdout_expected_file" "/dev/null" \
+bt_cli "$stdout_actual_file" "$stderr_actual_file" \
        convert --plugin-path "${plugin_dir}" "ABCDE" "${trace_dir}" some_other_non_opt \
        -c sink.text.details --params='with-metadata=false'
-ok "$?" "sources are auto-discovered"
+ok "$?" "CLI runs successfully"
+
+# Check components and their inputs.
+bt_diff "$stdout_expected_file" "$stdout_actual_file"
+ok "$?" "expected components are instantiated with expected inputs"
+
+# Check that expected warning is printed.
+grep -q 'No trace was found based on input `some_other_non_opt`' "$stderr_actual_file"
+ok "$?" "warning is printed"
+
+rm -f "$stdout_actual_file"
+rm -f "$stderr_actual_file"
This page took 0.045446 seconds and 4 git commands to generate.