babeltrace(1): add --connect option and connection management
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Fri, 10 Feb 2017 20:54:23 +0000 (15:54 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Sun, 28 May 2017 16:57:38 +0000 (12:57 -0400)
This patch adds the --connect option to connect component instances
by name (and optional port name):

    --connect SRC[SRCPORT]:DST[DSTPORT]

Example:

    babeltrace convert --source a.a --name A1 --source a.a --name A2
                       --sink b.b --name B
                       --connect A1.port:B
                       --connect A2.port:B.other-port

The code in babeltrace-cfg-connect.c validates that:

1. All the endpoint specified in connection arguments exist.

2. All connections are in the correct direction (source to filter,
   source to sink, filter to filter, filter to sink).

3. SRC and DST are not the same above.

4. All component instances are connected (no orphan component).

5. There's no duplicate connection.

There's a remaining validation to be done: ensure that there's no cycle
in the graph created by those connections.

If no --connect options are specified, babeltrace-cfg-connect.c connects
the component instances automatically:

1. It gives a unique name to unnamed component instances. The automatic
   name is TYPE-PLUGIN.COMPCLS.INDEX, where:

   TYPE:
     `source`, `filter`, or `sink`

   PLUGIN:
     Plugin name

   COMPCLS:
     Component class name

   INDEX:
     Automatic index to avoid collisions

2. It creates a multiplexer filter component configuration
   (`utils.mux`).

3. It connects the default ports of all the configured sources to the
   default port of this multiplexer filter.

4. It connects this multiplexer filter to the default ports of all the
   configured sinks.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
converter/Makefile.am
converter/babeltrace-cfg-connect.c [new file with mode: 0644]
converter/babeltrace-cfg-connect.h [new file with mode: 0644]
converter/babeltrace-cfg.c
converter/babeltrace-cfg.h
converter/babeltrace.c

index eb3b8043a95691f13295fe60f23863c0dc9d9587..6cbe2ff92ad42cfb40e3dcd40c9af52732ced563 100644 (file)
@@ -12,7 +12,9 @@ babeltrace_bin_SOURCES = \
        babeltrace-cfg.c \
        babeltrace-cfg.h \
        default-cfg.h \
-       default-cfg.c
+       default-cfg.c \
+       babeltrace-cfg-connect.h \
+       babeltrace-cfg-connect.c
 
 # -Wl,--no-as-needed is needed for recent gold linker who seems to think
 # it knows better and considers libraries with constructors having
diff --git a/converter/babeltrace-cfg-connect.c b/converter/babeltrace-cfg-connect.c
new file mode 100644 (file)
index 0000000..1e8e297
--- /dev/null
@@ -0,0 +1,884 @@
+/*
+ * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <babeltrace/values.h>
+#include "babeltrace-cfg.h"
+#include "babeltrace-cfg-connect.h"
+
+static bool all_named_in_array(GPtrArray *comps)
+{
+       size_t i;
+       bool all_named = true;
+
+       for (i = 0; i < comps->len; i++) {
+               struct bt_config_component *comp = g_ptr_array_index(comps, i);
+
+               if (comp->instance_name->len == 0) {
+                       all_named = false;
+                       goto end;
+               }
+       }
+
+end:
+       return all_named;
+}
+
+static bool all_named(struct bt_config *cfg)
+{
+       return all_named_in_array(cfg->cmd_data.convert.sources) &&
+               all_named_in_array(cfg->cmd_data.convert.filters) &&
+               all_named_in_array(cfg->cmd_data.convert.sinks);
+}
+
+void bt_config_connection_destroy(struct bt_config_connection *connection)
+{
+       if (!connection) {
+               return;
+       }
+
+       if (connection->src_instance_name) {
+               g_string_free(connection->src_instance_name, TRUE);
+       }
+
+       if (connection->dst_instance_name) {
+               g_string_free(connection->dst_instance_name, TRUE);
+       }
+
+       if (connection->src_port_name) {
+               g_string_free(connection->src_port_name, TRUE);
+       }
+
+       if (connection->dst_port_name) {
+               g_string_free(connection->dst_port_name, TRUE);
+       }
+
+       if (connection->arg) {
+               g_string_free(connection->arg, TRUE);
+       }
+
+       g_free(connection);
+}
+
+static struct bt_config_connection *bt_config_connection_create(const char *arg)
+{
+       struct bt_config_connection *cfg_connection;
+
+       cfg_connection = g_new0(struct bt_config_connection, 1);
+       if (!cfg_connection) {
+               goto error;
+       }
+
+       cfg_connection->src_instance_name = g_string_new(NULL);
+       if (!cfg_connection->src_instance_name) {
+               goto error;
+       }
+
+       cfg_connection->dst_instance_name = g_string_new(NULL);
+       if (!cfg_connection->dst_instance_name) {
+               goto error;
+       }
+
+       cfg_connection->src_port_name = g_string_new(NULL);
+       if (!cfg_connection->src_port_name) {
+               goto error;
+       }
+
+       cfg_connection->dst_port_name = g_string_new(NULL);
+       if (!cfg_connection->dst_port_name) {
+               goto error;
+       }
+
+       cfg_connection->arg = g_string_new(arg);
+       if (!cfg_connection->arg) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       g_free(cfg_connection);
+       cfg_connection = NULL;
+
+end:
+       return cfg_connection;
+}
+
+static struct bt_config_connection *bt_config_connection_create_full(
+       const char *src_instance_name, const char *src_port_name,
+       const char *dst_instance_name, const char *dst_port_name,
+       const char *arg)
+{
+       struct bt_config_connection *cfg_connection =
+               bt_config_connection_create(arg);
+
+       if (!cfg_connection) {
+               goto end;
+       }
+
+       g_string_assign(cfg_connection->src_instance_name, src_instance_name);
+       g_string_assign(cfg_connection->dst_instance_name, dst_instance_name);
+       g_string_assign(cfg_connection->src_port_name, src_port_name);
+       g_string_assign(cfg_connection->dst_port_name, dst_port_name);
+
+end:
+       return cfg_connection;
+}
+
+static GScanner *create_connection_arg_scanner(void)
+{
+       GScannerConfig scanner_config = {
+               .cset_skip_characters = " \t\n",
+               .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "_-",
+               .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "_-",
+               .case_sensitive = TRUE,
+               .cpair_comment_single = NULL,
+               .skip_comment_multi = TRUE,
+               .skip_comment_single = TRUE,
+               .scan_comment_multi = FALSE,
+               .scan_identifier = TRUE,
+               .scan_identifier_1char = TRUE,
+               .scan_identifier_NULL = FALSE,
+               .scan_symbols = FALSE,
+               .symbol_2_token = FALSE,
+               .scope_0_fallback = FALSE,
+               .scan_binary = FALSE,
+               .scan_octal = FALSE,
+               .scan_float = FALSE,
+               .scan_hex = FALSE,
+               .scan_hex_dollar = FALSE,
+               .numbers_2_int = FALSE,
+               .int_2_float = FALSE,
+               .store_int64 = FALSE,
+               .scan_string_sq = FALSE,
+               .scan_string_dq = FALSE,
+               .identifier_2_string = FALSE,
+               .char_2_token = TRUE,
+       };
+
+       return g_scanner_new(&scanner_config);
+}
+
+static struct bt_config_connection *cfg_connection_from_arg(const char *arg)
+{
+       struct bt_config_connection *connection = NULL;
+       GScanner *scanner = NULL;
+       enum {
+               EXPECTING_SRC,
+               EXPECTING_SRC_DOT,
+               EXPECTING_SRC_PORT,
+               EXPECTING_COLON,
+               EXPECTING_DST,
+               EXPECTING_DST_DOT,
+               EXPECTING_DST_PORT,
+               DONE,
+       } state = EXPECTING_SRC;
+
+       connection = bt_config_connection_create(arg);
+       if (!connection) {
+               goto error;
+       }
+
+       scanner = create_connection_arg_scanner();
+       if (!scanner) {
+               goto error;
+       }
+
+       g_scanner_input_text(scanner, arg, strlen(arg));
+
+       while (true) {
+               GTokenType token_type = g_scanner_get_next_token(scanner);
+
+               if (token_type == G_TOKEN_EOF) {
+                       goto after_scan;
+               }
+
+               switch (state) {
+               case EXPECTING_SRC:
+                       if (token_type != G_TOKEN_IDENTIFIER) {
+                               goto error;
+                       }
+
+                       g_string_assign(connection->src_instance_name,
+                               scanner->value.v_identifier);
+                       state = EXPECTING_SRC_DOT;
+                       break;
+               case EXPECTING_SRC_DOT:
+                       if (token_type == ':') {
+                               state = EXPECTING_DST;
+                               break;
+                       }
+
+                       if (token_type != '.') {
+                               goto error;
+                       }
+
+                       state = EXPECTING_SRC_PORT;
+                       break;
+               case EXPECTING_SRC_PORT:
+                       if (token_type != G_TOKEN_IDENTIFIER) {
+                               goto error;
+                       }
+
+                       g_string_assign(connection->src_port_name,
+                               scanner->value.v_identifier);
+                       state = EXPECTING_COLON;
+                       break;
+               case EXPECTING_COLON:
+                       if (token_type != ':') {
+                               goto error;
+                       }
+
+                       state = EXPECTING_DST;
+                       break;
+               case EXPECTING_DST:
+                       if (token_type != G_TOKEN_IDENTIFIER) {
+                               goto error;
+                       }
+
+                       g_string_assign(connection->dst_instance_name,
+                               scanner->value.v_identifier);
+                       state = EXPECTING_DST_DOT;
+                       break;
+               case EXPECTING_DST_DOT:
+                       if (token_type != '.') {
+                               goto error;
+                       }
+
+                       state = EXPECTING_DST_PORT;
+                       break;
+               case EXPECTING_DST_PORT:
+                       if (token_type != G_TOKEN_IDENTIFIER) {
+                               goto error;
+                       }
+
+                       g_string_assign(connection->dst_port_name,
+                               scanner->value.v_identifier);
+                       state = DONE;
+                       break;
+               default:
+                       goto error;
+               }
+       }
+
+after_scan:
+       if (state != EXPECTING_DST_DOT && state != DONE) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       bt_config_connection_destroy(connection);
+       connection = NULL;
+
+end:
+       if (scanner) {
+               g_scanner_destroy(scanner);
+       }
+
+       return connection;
+}
+
+static struct bt_config_component *find_component_in_array(GPtrArray *comps,
+               const char *name)
+{
+       size_t i;
+       struct bt_config_component *found_comp = NULL;
+
+       for (i = 0; i < comps->len; i++) {
+               struct bt_config_component *comp = g_ptr_array_index(comps, i);
+
+               if (strcmp(name, comp->instance_name->str) == 0) {
+                       found_comp = bt_get(comp);
+                       goto end;
+               }
+       }
+
+end:
+       return found_comp;
+}
+
+static struct bt_config_component *find_component(struct bt_config *cfg,
+               const char *name, enum bt_component_class_type *type)
+{
+       struct bt_config_component *comp;
+
+       comp = find_component_in_array(cfg->cmd_data.convert.sources, name);
+       if (comp) {
+               *type = BT_COMPONENT_CLASS_TYPE_SOURCE;
+               goto end;
+       }
+
+       comp = find_component_in_array(cfg->cmd_data.convert.filters, name);
+       if (comp) {
+               *type = BT_COMPONENT_CLASS_TYPE_FILTER;
+               goto end;
+       }
+
+       comp = find_component_in_array(cfg->cmd_data.convert.sinks, name);
+       if (comp) {
+               *type = BT_COMPONENT_CLASS_TYPE_SINK;
+               goto end;
+       }
+
+end:
+       return comp;
+}
+
+static int validate_all_endpoints_exist(struct bt_config *cfg, char *error_buf,
+               size_t error_buf_size)
+{
+       size_t i;
+       int ret = 0;
+
+       for (i = 0; i < cfg->cmd_data.convert.connections->len; i++) {
+               struct bt_config_connection *connection =
+                       g_ptr_array_index(cfg->cmd_data.convert.connections, i);
+               struct bt_config_component *comp;
+               enum bt_component_class_type type;
+
+               comp = find_component(cfg, connection->src_instance_name->str,
+                       &type);
+               bt_put(comp);
+               if (!comp) {
+                       comp = find_component(cfg,
+                               connection->dst_instance_name->str, &type);
+                       bt_put(comp);
+                       if (!comp) {
+                               snprintf(error_buf, error_buf_size,
+                                       "Invalid connection: cannot find component `%s`:\n    %s\n",
+                                       connection->dst_instance_name->str,
+                                       connection->arg->str);
+                               ret = -1;
+                               goto end;
+                       }
+               }
+       }
+
+end:
+       return ret;
+}
+
+static int validate_connection_directions(struct bt_config *cfg,
+               char *error_buf, size_t error_buf_size)
+{
+       size_t i;
+       int ret = 0;
+       struct bt_config_component *src_comp = NULL;
+       struct bt_config_component *dst_comp = NULL;
+
+       for (i = 0; i < cfg->cmd_data.convert.connections->len; i++) {
+               struct bt_config_connection *connection =
+                       g_ptr_array_index(cfg->cmd_data.convert.connections, i);
+               enum bt_component_class_type src_type;
+               enum bt_component_class_type dst_type;
+
+               src_comp = find_component(cfg,
+                       connection->src_instance_name->str, &src_type);
+               assert(src_comp);
+               dst_comp = find_component(cfg,
+                       connection->dst_instance_name->str, &dst_type);
+               assert(dst_comp);
+
+               if (src_type == BT_COMPONENT_CLASS_TYPE_SOURCE) {
+                       if (dst_type != BT_COMPONENT_CLASS_TYPE_FILTER &&
+                                       dst_type != BT_COMPONENT_CLASS_TYPE_SINK) {
+                               snprintf(error_buf, error_buf_size,
+                                       "Invalid connection: source component `%s` not connected to filter or sink component:\n    %s\n",
+                                       connection->src_instance_name->str,
+                                       connection->arg->str);
+                               ret = -1;
+                               goto end;
+                       }
+               } else if (src_type == BT_COMPONENT_CLASS_TYPE_FILTER) {
+                       if (dst_type != BT_COMPONENT_CLASS_TYPE_FILTER &&
+                                       dst_type != BT_COMPONENT_CLASS_TYPE_SINK) {
+                               snprintf(error_buf, error_buf_size,
+                                       "Invalid connection: filter component `%s` not connected to filter or sink component:\n    %s\n",
+                                       connection->src_instance_name->str,
+                                       connection->arg->str);
+                               ret = -1;
+                               goto end;
+                       }
+               } else {
+                       snprintf(error_buf, error_buf_size,
+                               "Invalid connection: cannot connect sink component `%s` to component `%s`:\n    %s\n",
+                               connection->src_instance_name->str,
+                               connection->dst_instance_name->str,
+                               connection->arg->str);
+                       ret = -1;
+                       goto end;
+               }
+
+               BT_PUT(src_comp);
+               BT_PUT(dst_comp);
+       }
+
+end:
+       bt_put(src_comp);
+       bt_put(dst_comp);
+       return ret;
+}
+
+static int validate_self_connections(struct bt_config *cfg, char *error_buf,
+               size_t error_buf_size)
+{
+       size_t i;
+       int ret = 0;
+
+       for (i = 0; i < cfg->cmd_data.convert.connections->len; i++) {
+               struct bt_config_connection *connection =
+                       g_ptr_array_index(cfg->cmd_data.convert.connections, i);
+
+               if (strcmp(connection->src_instance_name->str,
+                               connection->dst_instance_name->str) == 0) {
+                       snprintf(error_buf, error_buf_size,
+                               "Invalid connection: component `%s` is connected to itself:\n    %s\n",
+                               connection->src_instance_name->str,
+                               connection->arg->str);
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static int validate_all_components_connected_in_array(GPtrArray *comps,
+               struct bt_value *connected_components,
+               char *error_buf, size_t error_buf_size)
+{
+       int ret = 0;
+       size_t i;
+
+       for (i = 0; i < comps->len; i++) {
+               struct bt_config_component *comp = g_ptr_array_index(comps, i);
+
+               if (!bt_value_map_has_key(connected_components,
+                               comp->instance_name->str)) {
+                       snprintf(error_buf, error_buf_size,
+                               "Component `%s` is not connected\n",
+                               comp->instance_name->str);
+                       ret = -1;
+                       goto end;
+               }
+       }
+
+end:
+       return ret;
+}
+
+static int validate_all_components_connected(struct bt_config *cfg,
+               char *error_buf, size_t error_buf_size)
+{
+       size_t i;
+       int ret = 0;
+       struct bt_value *connected_components = bt_value_map_create();
+
+       if (!connected_components) {
+               ret = -1;
+               goto end;
+       }
+
+       for (i = 0; i < cfg->cmd_data.convert.connections->len; i++) {
+               struct bt_config_connection *connection =
+                       g_ptr_array_index(cfg->cmd_data.convert.connections, i);
+
+               ret = bt_value_map_insert(connected_components,
+                       connection->src_instance_name->str, bt_value_null);
+               if (ret) {
+                       goto end;
+               }
+
+               ret = bt_value_map_insert(connected_components,
+                       connection->dst_instance_name->str, bt_value_null);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+       ret = validate_all_components_connected_in_array(
+               cfg->cmd_data.convert.sources, connected_components,
+               error_buf, error_buf_size);
+       if (ret) {
+               goto end;
+       }
+
+       ret = validate_all_components_connected_in_array(
+               cfg->cmd_data.convert.filters, connected_components,
+               error_buf, error_buf_size);
+       if (ret) {
+               goto end;
+       }
+
+       ret = validate_all_components_connected_in_array(
+               cfg->cmd_data.convert.sinks, connected_components,
+               error_buf, error_buf_size);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       bt_put(connected_components);
+       return ret;
+}
+
+static int validate_no_duplicate_connection(struct bt_config *cfg,
+               char *error_buf, size_t error_buf_size)
+{
+       size_t i;
+       int ret = 0;
+       struct bt_value *flat_connection_names = bt_value_map_create();
+       GString *flat_connection_name = NULL;
+
+       if (!flat_connection_names) {
+               ret = -1;
+               goto end;
+       }
+
+       flat_connection_name = g_string_new(NULL);
+       if (!flat_connection_name) {
+               ret = -1;
+               goto end;
+       }
+
+       for (i = 0; i < cfg->cmd_data.convert.connections->len; i++) {
+               struct bt_config_connection *connection =
+                       g_ptr_array_index(cfg->cmd_data.convert.connections, i);
+
+               g_string_printf(flat_connection_name, "%s.%s:%s.%s",
+                       connection->src_instance_name->str,
+                       connection->src_port_name->str,
+                       connection->dst_instance_name->str,
+                       connection->dst_port_name->str);
+
+               if (bt_value_map_has_key(flat_connection_names,
+                               flat_connection_name->str)) {
+                       snprintf(error_buf, error_buf_size,
+                               "Duplicate connection:\n    %s\n",
+                               connection->arg->str);
+                       ret = -1;
+                       goto end;
+               }
+
+               ret = bt_value_map_insert(flat_connection_names,
+                       flat_connection_name->str, bt_value_null);
+               if (ret) {
+                       goto end;
+               }
+       }
+
+end:
+       bt_put(flat_connection_names);
+
+       if (flat_connection_name) {
+               g_string_free(flat_connection_name, TRUE);
+       }
+
+       return ret;
+}
+
+static int validate_connections(struct bt_config *cfg, char *error_buf,
+               size_t error_buf_size)
+{
+       int ret;
+
+       ret = validate_all_endpoints_exist(cfg, error_buf, error_buf_size);
+       if (ret) {
+               goto end;
+       }
+
+       ret = validate_connection_directions(cfg, error_buf, error_buf_size);
+       if (ret) {
+               goto end;
+       }
+
+       ret = validate_self_connections(cfg, error_buf, error_buf_size);
+       if (ret) {
+               goto end;
+       }
+
+       ret = validate_all_components_connected(cfg, error_buf, error_buf_size);
+       if (ret) {
+               goto end;
+       }
+
+       ret = validate_no_duplicate_connection(cfg, error_buf, error_buf_size);
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static bool component_name_exists_in_array(GPtrArray *comps, const char *name)
+{
+       size_t i;
+       bool exists = false;
+
+       for (i = 0; i < comps->len; i++) {
+               struct bt_config_component *comp = g_ptr_array_index(comps, i);
+
+               if (comp->instance_name->len > 0) {
+                       if (strcmp(comp->instance_name->str, name) == 0) {
+                               exists = true;
+                               break;
+                       }
+               }
+       }
+
+       return exists;
+}
+
+static bool component_name_exists_at_all(struct bt_config *cfg,
+               const char *name)
+{
+       return component_name_exists_in_array(cfg->cmd_data.convert.sources,
+               name) || component_name_exists_in_array(
+                       cfg->cmd_data.convert.filters, name) ||
+               component_name_exists_in_array(
+                       cfg->cmd_data.convert.sinks, name);
+}
+
+static int auto_name_component(struct bt_config *cfg, const char *prefix,
+               struct bt_config_component *comp)
+{
+       int ret = 0;
+       unsigned int i = 0;
+       GString *new_name;
+
+       assert(comp->instance_name->len == 0);
+       new_name = g_string_new(NULL);
+       if (!new_name) {
+               ret = -1;
+               goto end;
+       }
+
+       do {
+               g_string_printf(new_name, "%s-%s.%s-%d", prefix,
+                       comp->plugin_name->str, comp->component_name->str, i);
+               i++;
+       } while (component_name_exists_at_all(cfg, new_name->str));
+
+       g_string_assign(comp->instance_name, new_name->str);
+
+end:
+       if (new_name) {
+               g_string_free(new_name, TRUE);
+       }
+
+       return ret;
+}
+
+static int auto_name_components_in_array(struct bt_config *cfg,
+               GPtrArray *comps, const char *prefix)
+{
+       int ret = 0;
+       size_t i;
+
+       for (i = 0; i < comps->len; i++) {
+               struct bt_config_component *comp = g_ptr_array_index(comps, i);
+
+               if (comp->instance_name->len == 0) {
+                       ret = auto_name_component(cfg, prefix, comp);
+                       if (ret) {
+                               goto end;
+                       }
+               }
+       }
+
+end:
+       return ret;
+}
+
+static int auto_name_components(struct bt_config *cfg)
+{
+       int ret;
+
+       ret = auto_name_components_in_array(cfg, cfg->cmd_data.convert.sources,
+               "source");
+       if (ret) {
+               goto end;
+       }
+
+       ret = auto_name_components_in_array(cfg, cfg->cmd_data.convert.filters,
+               "filter");
+       if (ret) {
+               goto end;
+       }
+
+       ret = auto_name_components_in_array(cfg, cfg->cmd_data.convert.sinks,
+               "sink");
+       if (ret) {
+               goto end;
+       }
+
+end:
+       return ret;
+}
+
+static int auto_connect(struct bt_config *cfg)
+{
+       int ret = 0;
+       struct bt_config_component *muxer_cfg_comp = NULL;
+       size_t i;
+       const char *last_filter_comp_name;
+
+       /* Make sure all components have a unique instance name */
+       ret = auto_name_components(cfg);
+       if (ret) {
+               goto error;
+       }
+
+       /* Add an implicit muxer filter */
+       muxer_cfg_comp = bt_config_component_from_arg("utils.muxer");
+       if (!muxer_cfg_comp) {
+               goto error;
+       }
+
+       auto_name_component(cfg, "filter", muxer_cfg_comp);
+       g_ptr_array_add(cfg->cmd_data.convert.filters, bt_get(muxer_cfg_comp));
+
+       /* Connect all sources to this mux */
+       for (i = 0; i < cfg->cmd_data.convert.sources->len; i++) {
+               struct bt_config_component *comp =
+                       g_ptr_array_index(cfg->cmd_data.convert.sources, i);
+               struct bt_config_connection *cfg_connection =
+                       bt_config_connection_create_full(
+                               comp->instance_name->str, "",
+                               muxer_cfg_comp->instance_name->str, "",
+                               "(auto)");
+
+               if (!cfg_connection) {
+                       goto error;
+               }
+
+               g_ptr_array_add(cfg->cmd_data.convert.connections,
+                       cfg_connection);
+       }
+
+       /* Connect this mux to the filter components, in order */
+       last_filter_comp_name = muxer_cfg_comp->instance_name->str;
+
+       for (i = 0; i < cfg->cmd_data.convert.filters->len - 1; i++) {
+               struct bt_config_component *comp =
+                       g_ptr_array_index(cfg->cmd_data.convert.filters, i);
+               struct bt_config_connection *cfg_connection;
+
+               cfg_connection = bt_config_connection_create_full(
+                               last_filter_comp_name, "",
+                               comp->instance_name->str, "",
+                               "(auto)");
+
+               if (!cfg_connection) {
+                       goto error;
+               }
+
+               g_ptr_array_add(cfg->cmd_data.convert.connections,
+                       cfg_connection);
+               last_filter_comp_name = comp->instance_name->str;
+       }
+
+       /* Connect the last filter component to all sink components */
+       for (i = 0; i < cfg->cmd_data.convert.sinks->len; i++) {
+               struct bt_config_component *comp =
+                       g_ptr_array_index(cfg->cmd_data.convert.sinks, i);
+               struct bt_config_connection *cfg_connection =
+                       bt_config_connection_create_full(
+                               last_filter_comp_name, "",
+                               comp->instance_name->str, "",
+                               "(auto)");
+
+               if (!cfg_connection) {
+                       goto error;
+               }
+
+               g_ptr_array_add(cfg->cmd_data.convert.connections,
+                       cfg_connection);
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       bt_put(muxer_cfg_comp);
+       return ret;
+}
+
+int bt_config_create_connections(struct bt_config *cfg,
+               struct bt_value *connection_args,
+               char *error_buf, size_t error_buf_size)
+{
+       int ret;
+       size_t i;
+
+       if (bt_value_array_is_empty(connection_args)) {
+               /* No explicit connections: do automatic connection */
+               ret = auto_connect(cfg);
+               if (ret) {
+                       goto error;
+               }
+       }
+
+       if (!all_named(cfg)) {
+               snprintf(error_buf, error_buf_size, "At least one connection (--connect) specified, but not all component\ninstances are named (use --name)\n");
+               goto error;
+       }
+
+       for (i = 0; i < bt_value_array_size(connection_args); i++) {
+               struct bt_value *arg_value =
+                       bt_value_array_get(connection_args, i);
+               const char *arg;
+               struct bt_config_connection *cfg_connection;
+
+               ret = bt_value_string_get(arg_value, &arg);
+               BT_PUT(arg_value);
+               assert(ret == 0);
+               cfg_connection = cfg_connection_from_arg(arg);
+               if (!cfg_connection) {
+                       snprintf(error_buf, error_buf_size, "Cannot parse --connect option's argument:\n    %s\n",
+                               arg);
+                       goto error;
+               }
+
+               g_ptr_array_add(cfg->cmd_data.convert.connections,
+                       cfg_connection);
+       }
+
+
+       ret = validate_connections(cfg, error_buf, error_buf_size);
+       if (ret) {
+               goto error;
+       }
+
+       goto end;
+
+error:
+       ret = -1;
+
+end:
+       return ret;
+}
diff --git a/converter/babeltrace-cfg-connect.h b/converter/babeltrace-cfg-connect.h
new file mode 100644 (file)
index 0000000..98f9d04
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef BABELTRACE_CONNECT_H
+#define BABELTRACE_CONNECT_H
+
+/*
+ * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <stdlib.h>
+#include <stdint.h>
+#include <babeltrace/values.h>
+#include <glib.h>
+#include "babeltrace-cfg.h"
+
+struct bt_config_connection {
+       GString *src_instance_name;
+       GString *dst_instance_name;
+       GString *src_port_name;
+       GString *dst_port_name;
+       GString *arg;
+};
+
+int bt_config_create_connections(struct bt_config *cfg,
+               struct bt_value *connection_args,
+               char *error_buf, size_t error_buf_size);
+
+void bt_config_connection_destroy(struct bt_config_connection *connection);
+
+#endif /* BABELTRACE_CONNECT_H */
index 6d8ff8d9846e23853853cf8af577369139dbde70..807dcdc9c82acd5feb3130efd452ac3c1edfbfc6 100644 (file)
@@ -37,6 +37,7 @@
 #include <sys/types.h>
 #include <pwd.h>
 #include "babeltrace-cfg.h"
+#include "babeltrace-cfg-connect.h"
 
 #define DEFAULT_SOURCE_COMPONENT_NAME  "ctf.fs"
 #define DEFAULT_SINK_COMPONENT_NAME    "text.text"
@@ -781,7 +782,6 @@ end:
  * Creates a component configuration from a command-line source/sink
  * option's argument.
  */
-static
 struct bt_config_component *bt_config_component_from_arg(const char *arg)
 {
        struct bt_config_component *bt_config_component = NULL;
@@ -830,10 +830,19 @@ void bt_config_destroy(struct bt_object *obj)
                        g_ptr_array_free(cfg->cmd_data.convert.sources, TRUE);
                }
 
+               if (cfg->cmd_data.convert.filters) {
+                       g_ptr_array_free(cfg->cmd_data.convert.filters, TRUE);
+               }
+
                if (cfg->cmd_data.convert.sinks) {
                        g_ptr_array_free(cfg->cmd_data.convert.sinks, TRUE);
                }
 
+               if (cfg->cmd_data.convert.connections) {
+                       g_ptr_array_free(cfg->cmd_data.convert.connections,
+                               TRUE);
+               }
+
                BT_PUT(cfg->cmd_data.convert.plugin_paths);
                break;
        case BT_CONFIG_COMMAND_LIST_PLUGINS:
@@ -1998,6 +2007,7 @@ enum {
        OPT_CLOCK_OFFSET,
        OPT_CLOCK_OFFSET_NS,
        OPT_CLOCK_SECONDS,
+       OPT_CONNECT,
        OPT_DEBUG,
        OPT_DEBUG_INFO_DIR,
        OPT_DEBUG_INFO_FULL_PATH,
@@ -2212,6 +2222,13 @@ static struct bt_config *bt_config_convert_create(
                goto error;
        }
 
+       cfg->cmd_data.convert.filters = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) bt_put);
+       if (!cfg->cmd_data.convert.filters) {
+               print_err_oom();
+               goto error;
+       }
+
        cfg->cmd_data.convert.sinks = g_ptr_array_new_with_free_func(
                (GDestroyNotify) bt_put);
        if (!cfg->cmd_data.convert.sinks) {
@@ -2219,6 +2236,13 @@ static struct bt_config *bt_config_convert_create(
                goto error;
        }
 
+       cfg->cmd_data.convert.connections = g_ptr_array_new_with_free_func(
+               (GDestroyNotify) bt_config_connection_destroy);
+       if (!cfg->cmd_data.convert.connections) {
+               print_err_oom();
+               goto error;
+       }
+
        if (initial_plugin_paths) {
                cfg->cmd_data.convert.plugin_paths =
                        bt_get(initial_plugin_paths);
@@ -2768,6 +2792,8 @@ void print_convert_usage(FILE *fp)
        fprintf(fp, "      --begin=BEGIN                 Set the `begin` parameter of the latest\n");
        fprintf(fp, "                                    source component instance to BEGIN\n");
        fprintf(fp, "                                    (see the suggested format of BEGIN below)\n");
+       fprintf(fp, "  -c, --connect=CONNECTION          Connect two component instances (see the\n");
+       fprintf(fp, "                                    expected format of CONNECTION below)\n");
        fprintf(fp, "  -d, --debug                       Enable debug mode\n");
        fprintf(fp, "      --end=END                     Set the `end` parameter of the latest\n");
        fprintf(fp, "                                    source component instance to END\n");
@@ -2807,6 +2833,26 @@ void print_convert_usage(FILE *fp)
        fprintf(fp, "\n");
        fprintf(fp, "    [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n");
        fprintf(fp, "\n\n");
+       fprintf(fp, "Expected format of CONNECTION\n");
+       fprintf(fp, "-----------------------------\n");
+       fprintf(fp, "\n");
+       fprintf(fp, "    SRC[.SRCPORT]:DST[.DSTPORT]\n");
+       fprintf(fp, "\n");
+       fprintf(fp, "SRC and DST are the names of the source and destination component\n");
+       fprintf(fp, "instances to connect together. You can set the name of a component\n");
+       fprintf(fp, "instance with the --name option.\n");
+       fprintf(fp, "\n");
+       fprintf(fp, "SRCPORT and DSTPORT are the optional source and destination ports to use\n");
+       fprintf(fp, "for the connection. When the port is not specified, the default port is\n");
+       fprintf(fp, "used.\n");
+       fprintf(fp, "\n");
+       fprintf(fp, "You can connect a source component to a filter or sink component. You\n");
+       fprintf(fp, "can connect a filter component to a sink component.\n");
+       fprintf(fp, "\n");
+       fprintf(fp, "Example:\n");
+       fprintf(fp, "\n");
+       fprintf(fp, "    my-filter.top10:json-out\n");
+       fprintf(fp, "\n\n");
        fprintf(fp, "Expected format of PARAMS\n");
        fprintf(fp, "-------------------------\n");
        fprintf(fp, "\n");
@@ -2849,6 +2895,7 @@ static struct poptOption convert_long_options[] = {
        { "clock-offset", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET, NULL, NULL },
        { "clock-offset-ns", '\0', POPT_ARG_STRING, NULL, OPT_CLOCK_OFFSET_NS, NULL, NULL },
        { "clock-seconds", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_SECONDS, NULL, NULL },
+       { "connect", 'c', POPT_ARG_STRING, NULL, OPT_CONNECT, NULL, NULL },
        { "debug", 'd', POPT_ARG_NONE, NULL, OPT_DEBUG, NULL, NULL },
        { "debug-info-dir", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_DIR, NULL, NULL },
        { "debug-info-full-path", 0, POPT_ARG_NONE, NULL, OPT_DEBUG_INFO_FULL_PATH, NULL, NULL },
@@ -2905,6 +2952,8 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[],
        int opt, ret = 0;
        struct bt_config *cfg = NULL;
        struct bt_value *instance_names = NULL;
+       struct bt_value *connection_args = NULL;
+       char error_buf[256] = { 0 };
 
        *retcode = 0;
        memset(&ctf_legacy_opts, 0, sizeof(ctf_legacy_opts));
@@ -2960,6 +3009,12 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[],
                goto error;
        }
 
+       connection_args = bt_value_array_create();
+       if (!connection_args) {
+               print_err_oom();
+               goto error;
+       }
+
        ret = append_env_var_plugin_paths(cfg->cmd_data.convert.plugin_paths);
        if (ret) {
                printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n");
@@ -3388,6 +3443,13 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[],
                        }
                        break;
                }
+               case OPT_CONNECT:
+                       if (bt_value_array_append_string(connection_args,
+                                       arg)) {
+                               print_err_oom();
+                               goto error;
+                       }
+                       break;
                case OPT_HELP:
                        print_convert_usage(stdout);
                        *retcode = -1;
@@ -3510,6 +3572,13 @@ struct bt_config *bt_config_convert_from_args(int argc, const char *argv[],
                cur_cfg_comp = NULL;
        }
 
+       ret = bt_config_create_connections(cfg, connection_args,
+               error_buf, 256);
+       if (ret) {
+               printf_err("Cannot creation connections:\n%s", error_buf);
+               goto error;
+       }
+
        goto end;
 
 error:
@@ -3541,6 +3610,7 @@ end:
        BT_PUT(text_legacy_opts.fields);
        BT_PUT(legacy_input_paths);
        BT_PUT(instance_names);
+       BT_PUT(connection_args);
        return cfg;
 }
 
index a452d59f424d75f09e5544de83c27f32556904e3..23af3e6801b9bb811fba25be2f9bb0ed60fdba62 100644 (file)
@@ -62,9 +62,15 @@ struct bt_config {
                        /* Array of pointers to struct bt_config_component */
                        GPtrArray *sources;
 
+                       /* Array of pointers to struct bt_config_component */
+                       GPtrArray *filters;
+
                        /* Array of pointers to struct bt_config_component */
                        GPtrArray *sinks;
 
+                       /* Array of pointers to struct bt_config_connection */
+                       GPtrArray *connections;
+
                        bool force_correlate;
                        bool omit_system_plugin_path;
                        bool omit_home_plugin_path;
@@ -101,6 +107,8 @@ struct bt_config *bt_config_from_args(int argc, const char *argv[],
                bool omit_home_plugin_path,
                struct bt_value *initial_plugin_paths);
 
+struct bt_config_component *bt_config_component_from_arg(const char *arg);
+
 enum bt_value_status bt_config_append_plugin_paths(
                struct bt_value *plugin_paths, const char *arg);
 
index 04185214eff798cc8224cc08eb0f399991e6a758..af7a5ba6157eabb53f4f300d79cff983ab8bba6e 100644 (file)
@@ -44,6 +44,7 @@
 #include <stdio.h>
 #include <glib.h>
 #include "babeltrace-cfg.h"
+#include "babeltrace-cfg-connect.h"
 #include "default-cfg.h"
 
 GPtrArray *loaded_plugins;
@@ -265,13 +266,36 @@ void print_plugin_paths(struct bt_value *plugin_paths)
 static
 void print_cfg_convert(struct bt_config *cfg)
 {
+       size_t i;
+
        printf("  Force correlate: %s\n",
                cfg->cmd_data.convert.force_correlate ? "yes" : "no");
        print_plugin_paths(cfg->cmd_data.convert.plugin_paths);
        printf("  Source component instances:\n");
        print_bt_config_components(cfg->cmd_data.convert.sources);
+
+       if (cfg->cmd_data.convert.filters->len > 0) {
+               printf("  Filter component instances:\n");
+               print_bt_config_components(cfg->cmd_data.convert.filters);
+       }
+
        printf("  Sink component instances:\n");
        print_bt_config_components(cfg->cmd_data.convert.sinks);
+       printf("  Connections:\n");
+
+       for (i = 0; i < cfg->cmd_data.convert.connections->len; i++) {
+               struct bt_config_connection *cfg_connection =
+                       g_ptr_array_index(cfg->cmd_data.convert.connections,
+                               i);
+
+               printf("    %s%s%s -> %s%s%s\n",
+                       cfg_connection->src_instance_name->str,
+                       cfg_connection->src_port_name->len > 0 ? "." : "",
+                       cfg_connection->src_port_name->str,
+                       cfg_connection->dst_instance_name->str,
+                       cfg_connection->dst_port_name->len > 0 ? "." : "",
+                       cfg_connection->dst_port_name->str);
+       }
 }
 
 static
@@ -810,12 +834,14 @@ static int cmd_convert(struct bt_config *cfg)
        /* TODO handle more than 1 source and 1 sink. */
        if (cfg->cmd_data.convert.sources->len != 1 ||
                        cfg->cmd_data.convert.sinks->len != 1) {
+               fprintf(stderr, "Only one source and one sink component class are supported. Aborting...\n");
                ret = -1;
                goto end;
        }
 
        ret = load_all_plugins(cfg->cmd_data.convert.plugin_paths);
        if (ret) {
+               fprintf(stderr, "Could not load plugins from configured plugin paths. Aborting...\n");
                goto end;
        }
 
This page took 0.037757 seconds and 4 git commands to generate.