From: Michael Jeanson Date: Tue, 4 Jun 2019 18:22:42 +0000 (-0400) Subject: cli: Rename to babeltrace2 X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=commitdiff_plain;h=ec2c5e50e20bcc3770a1efae8123866b31224622 cli: Rename to babeltrace2 We decided to make babeltrace2 co-installable with babeltrace1 since it offers backward compatibility but is not a full drop in replacement. This first change renames the command line executables. Change-Id: I52db05629b6349a5221f6b684cb15b289db8c6b3 Signed-off-by: Michael Jeanson Reviewed-on: https://review.lttng.org/c/babeltrace/+/1402 Tested-by: jenkins Reviewed-by: Philippe Proulx --- diff --git a/.gitignore b/.gitignore index f828aa19..ac30a452 100644 --- a/.gitignore +++ b/.gitignore @@ -62,10 +62,10 @@ plugins/ctf/common/metadata/lexer.c plugins/ctf/common/metadata/parser.c plugins/ctf/common/metadata/parser.h plugins/ctf/common/metadata/parser.output -/cli/babeltrace -/cli/babeltrace.bin -/cli/babeltrace-log -/cli/babeltrace-log.bin +/cli/babeltrace2 +/cli/babeltrace2.bin +/cli/babeltrace2-log +/cli/babeltrace2-log.bin /include/config.h /include/config.h.in /include/version.i diff --git a/cli/Makefile.am b/cli/Makefile.am index ed309d02..1ae51049 100644 --- a/cli/Makefile.am +++ b/cli/Makefile.am @@ -13,25 +13,25 @@ endif AM_CPPFLAGS += '-DCONFIG_IN_TREE_PLUGIN_PATH="$(IN_TREE_PLUGIN_PATH)"' -bin_PROGRAMS = babeltrace.bin babeltrace-log.bin -noinst_PROGRAMS = babeltrace babeltrace-log +bin_PROGRAMS = babeltrace2.bin babeltrace2-log.bin +noinst_PROGRAMS = babeltrace2 babeltrace2-log -babeltrace_bin_SOURCES = \ - babeltrace.c \ - babeltrace-cfg.c \ - babeltrace-cfg.h \ - babeltrace-cfg-cli-args.c \ - babeltrace-cfg-cli-args.h \ - babeltrace-cfg-cli-args-connect.c \ - babeltrace-cfg-cli-args-connect.h \ - babeltrace-cfg-cli-args-default.h \ - babeltrace-cfg-cli-args-default.c \ +babeltrace2_bin_SOURCES = \ + babeltrace2.c \ + babeltrace2-cfg.c \ + babeltrace2-cfg.h \ + babeltrace2-cfg-cli-args.c \ + babeltrace2-cfg-cli-args.h \ + babeltrace2-cfg-cli-args-connect.c \ + babeltrace2-cfg-cli-args-connect.h \ + babeltrace2-cfg-cli-args-default.h \ + babeltrace2-cfg-cli-args-default.c \ logging.c logging.h # -Wl,--no-as-needed is needed for recent gold linker who seems to think # it knows better and considers libraries with constructors having # side-effects as dead code. -babeltrace_bin_LDFLAGS = $(LD_NO_AS_NEEDED) +babeltrace2_bin_LDFLAGS = $(LD_NO_AS_NEEDED) # Add all the convenience libraries used by Babeltrace plugins and the # library. They will be used when embedding plugins (--enable-built-in-plugins), @@ -40,7 +40,7 @@ babeltrace_bin_LDFLAGS = $(LD_NO_AS_NEEDED) # we're using --whole-archive below (needed to make sure the linker does # not discard the plugins since the CLI does not use their symbols # directly). -babeltrace_bin_LDADD = \ +babeltrace2_bin_LDADD = \ $(top_builddir)/lib/libbabeltrace.la \ $(top_builddir)/compat/libcompat.la \ $(top_builddir)/common/libbabeltrace-common.la \ @@ -65,36 +65,36 @@ if ENABLE_BUILT_IN_PLUGINS pluginarchive = $(LD_WHOLE_ARCHIVE)$(PLUGINS_PATH)/$(1)/.libs/babeltrace-plugin-$(1).a$(LD_NO_WHOLE_ARCHIVE) # Built-in plugins -babeltrace_bin_LDFLAGS += $(call pluginarchive,ctf) -babeltrace_bin_LDFLAGS += $(call pluginarchive,text) -babeltrace_bin_LDFLAGS += $(call pluginarchive,utils) +babeltrace2_bin_LDFLAGS += $(call pluginarchive,ctf) +babeltrace2_bin_LDFLAGS += $(call pluginarchive,text) +babeltrace2_bin_LDFLAGS += $(call pluginarchive,utils) if ENABLE_DEBUG_INFO -babeltrace_bin_LDFLAGS += $(call pluginarchive,lttng-utils) -babeltrace_bin_LDADD += $(ELFUTILS_LIBS) +babeltrace2_bin_LDFLAGS += $(call pluginarchive,lttng-utils) +babeltrace2_bin_LDADD += $(ELFUTILS_LIBS) endif endif if BABELTRACE_BUILD_WITH_MINGW -babeltrace_bin_LDADD += -lws2_32 -lrpcrt4 -lintl -liconv -lole32 -lpthread +babeltrace2_bin_LDADD += -lws2_32 -lrpcrt4 -lintl -liconv -lole32 -lpthread endif # Only used for in-tree execution and tests -babeltrace_SOURCES = $(babeltrace_bin_SOURCES) -babeltrace_LDFLAGS = $(babeltrace_bin_LDFLAGS) -babeltrace_LDADD = $(babeltrace_bin_LDADD) -babeltrace_CFLAGS = $(AM_CFLAGS) -DBT_SET_DEFAULT_IN_TREE_CONFIGURATION +babeltrace2_SOURCES = $(babeltrace2_bin_SOURCES) +babeltrace2_LDFLAGS = $(babeltrace2_bin_LDFLAGS) +babeltrace2_LDADD = $(babeltrace2_bin_LDADD) +babeltrace2_CFLAGS = $(AM_CFLAGS) -DBT_SET_DEFAULT_IN_TREE_CONFIGURATION -# babeltrace-log rules and config below -babeltrace_log_bin_SOURCES = babeltrace-log.c -babeltrace_log_bin_LDADD = \ +# babeltrace2-log rules and config below +babeltrace2_log_bin_SOURCES = babeltrace2-log.c +babeltrace2_log_bin_LDADD = \ $(top_builddir)/compat/libcompat.la \ $(top_builddir)/common/libbabeltrace-common.la \ $(top_builddir)/logging/libbabeltrace-logging.la \ $(POPT_LIBS) -babeltrace_log_bin_CFLAGS = $(AM_CFLAGS) '-DBT_CLI_PATH="$(abs_top_builddir)/cli/babeltrace$(EXEEXT)"' +babeltrace2_log_bin_CFLAGS = $(AM_CFLAGS) '-DBT_CLI_PATH="$(abs_top_builddir)/cli/babeltrace2$(EXEEXT)"' # Only used for in-tree execution and tests -babeltrace_log_SOURCES = $(babeltrace_log_bin_SOURCES) -babeltrace_log_LDADD = $(babeltrace_log_bin_LDADD) -babeltrace_log_CFLAGS = $(AM_CFLAGS) '-DBT_CLI_PATH="$(bindir)/babeltrace$(EXEEXT)"' +babeltrace2_log_SOURCES = $(babeltrace2_log_bin_SOURCES) +babeltrace2_log_LDADD = $(babeltrace2_log_bin_LDADD) +babeltrace2_log_CFLAGS = $(AM_CFLAGS) '-DBT_CLI_PATH="$(bindir)/babeltrace2$(EXEEXT)"' diff --git a/cli/babeltrace-cfg-cli-args-connect.c b/cli/babeltrace-cfg-cli-args-connect.c deleted file mode 100644 index ff3bf917..00000000 --- a/cli/babeltrace-cfg-cli-args-connect.c +++ /dev/null @@ -1,726 +0,0 @@ -/* - * Copyright 2017 Philippe Proulx - * - * 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 -#include -#include -#include "babeltrace-cfg.h" -#include "babeltrace-cfg-cli-args-connect.h" - -static bool all_named_and_printable_in_array(GPtrArray *comps) -{ - size_t i; - bool all_named_and_printable = 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_and_printable = false; - goto end; - } - - if (!bt_common_string_is_printable(comp->instance_name->str)) { - all_named_and_printable = false; - goto end; - } - } - -end: - return all_named_and_printable; -} - -static bool all_named_and_printable(struct bt_config *cfg) -{ - return all_named_and_printable_in_array(cfg->cmd_data.run.sources) && - all_named_and_printable_in_array(cfg->cmd_data.run.filters) && - all_named_and_printable_in_array(cfg->cmd_data.run.sinks); -} - -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->upstream_comp_name = g_string_new(NULL); - if (!cfg_connection->upstream_comp_name) { - goto error; - } - - cfg_connection->downstream_comp_name = g_string_new(NULL); - if (!cfg_connection->downstream_comp_name) { - goto error; - } - - cfg_connection->upstream_port_glob = g_string_new("*"); - if (!cfg_connection->upstream_port_glob) { - goto error; - } - - cfg_connection->downstream_port_glob = g_string_new("*"); - if (!cfg_connection->downstream_port_glob) { - 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 bool validate_port_glob(const char *port_glob) -{ - bool is_valid = true; - const char *ch = port_glob; - - BT_ASSERT(port_glob); - - while (*ch != '\0') { - switch (*ch) { - case '\\': - switch (ch[1]) { - case '\0': - goto end; - default: - ch += 2; - continue; - } - case '?': - case '[': - /* - * This is reserved for future use, to support - * full globbing patterns. Those characters must - * be escaped with `\`. - */ - is_valid = false; - goto end; - default: - ch++; - break; - } - } - -end: - return is_valid; -} - -static int normalize_glob_pattern(GString *glob_pattern_gs) -{ - int ret = 0; - char *glob_pattern = strdup(glob_pattern_gs->str); - - if (!glob_pattern) { - ret = -1; - goto end; - } - - bt_common_normalize_star_glob_pattern(glob_pattern); - g_string_assign(glob_pattern_gs, glob_pattern); - free(glob_pattern); - -end: - return ret; -} - -static struct bt_config_connection *cfg_connection_from_arg(const char *arg) -{ - const char *at = arg; - size_t end_pos; - struct bt_config_connection *cfg_conn = NULL; - GString *gs = NULL; - enum { - UPSTREAM_NAME, - DOWNSTREAM_NAME, - UPSTREAM_PORT_GLOB, - DOWNSTREAM_PORT_GLOB, - } state = UPSTREAM_NAME; - - if (!bt_common_string_is_printable(arg)) { - goto error; - } - - cfg_conn = bt_config_connection_create(arg); - if (!cfg_conn) { - goto error; - } - - while (true) { - switch (state) { - case UPSTREAM_NAME: - gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - g_string_free(cfg_conn->upstream_comp_name, TRUE); - cfg_conn->upstream_comp_name = gs; - gs = NULL; - - if (at[end_pos] == ':') { - state = DOWNSTREAM_NAME; - } else if (at[end_pos] == '.') { - state = UPSTREAM_PORT_GLOB; - } else { - goto error; - } - - at += end_pos + 1; - break; - case DOWNSTREAM_NAME: - gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - g_string_free(cfg_conn->downstream_comp_name, TRUE); - cfg_conn->downstream_comp_name = gs; - gs = NULL; - - if (at[end_pos] == '.') { - state = DOWNSTREAM_PORT_GLOB; - } else if (at[end_pos] == '\0') { - goto end; - } else { - goto error; - } - - at += end_pos + 1; - break; - case UPSTREAM_PORT_GLOB: - gs = bt_common_string_until(at, ".:", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - if (!validate_port_glob(gs->str)) { - goto error; - } - - if (normalize_glob_pattern(gs)) { - goto error; - } - - g_string_free(cfg_conn->upstream_port_glob, TRUE); - cfg_conn->upstream_port_glob = gs; - gs = NULL; - - if (at[end_pos] == ':') { - state = DOWNSTREAM_NAME; - } else { - goto error; - } - - at += end_pos + 1; - break; - case DOWNSTREAM_PORT_GLOB: - gs = bt_common_string_until(at, ".:", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - if (!validate_port_glob(gs->str)) { - goto error; - } - - if (normalize_glob_pattern(gs)) { - goto error; - } - - g_string_free(cfg_conn->downstream_port_glob, TRUE); - cfg_conn->downstream_port_glob = gs; - gs = NULL; - - if (at[end_pos] == '\0') { - goto end; - } else { - goto error; - } - break; - default: - abort(); - } - } - -error: - bt_config_connection_destroy(cfg_conn); - cfg_conn = NULL; - -end: - if (gs) { - g_string_free(gs, TRUE); - } - - return cfg_conn; -} - -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 = comp; - bt_object_get_ref(found_comp); - goto end; - } - } - -end: - return found_comp; -} - -static struct bt_config_component *find_component(struct bt_config *cfg, - const char *name) -{ - struct bt_config_component *comp; - - comp = find_component_in_array(cfg->cmd_data.run.sources, name); - if (comp) { - goto end; - } - - comp = find_component_in_array(cfg->cmd_data.run.filters, name); - if (comp) { - goto end; - } - - comp = find_component_in_array(cfg->cmd_data.run.sinks, name); - if (comp) { - 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.run.connections->len; i++) { - struct bt_config_connection *connection = - g_ptr_array_index(cfg->cmd_data.run.connections, i); - struct bt_config_component *comp; - - comp = find_component(cfg, connection->upstream_comp_name->str); - bt_object_put_ref(comp); - if (!comp) { - snprintf(error_buf, error_buf_size, - "Invalid connection: cannot find upstream component `%s`:\n %s\n", - connection->upstream_comp_name->str, - connection->arg->str); - ret = -1; - goto end; - } - - comp = find_component(cfg, connection->downstream_comp_name->str); - bt_object_put_ref(comp); - if (!comp) { - snprintf(error_buf, error_buf_size, - "Invalid connection: cannot find downstream component `%s`:\n %s\n", - connection->downstream_comp_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.run.connections->len; i++) { - struct bt_config_connection *connection = - g_ptr_array_index(cfg->cmd_data.run.connections, i); - - src_comp = find_component(cfg, - connection->upstream_comp_name->str); - BT_ASSERT(src_comp); - dst_comp = find_component(cfg, - connection->downstream_comp_name->str); - BT_ASSERT(dst_comp); - - if (src_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) { - if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER && - dst_comp->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->upstream_comp_name->str, - connection->arg->str); - ret = -1; - goto end; - } - } else if (src_comp->type == BT_COMPONENT_CLASS_TYPE_FILTER) { - if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER && - dst_comp->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->upstream_comp_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->upstream_comp_name->str, - connection->downstream_comp_name->str, - connection->arg->str); - ret = -1; - goto end; - } - - BT_OBJECT_PUT_REF_AND_RESET(src_comp); - BT_OBJECT_PUT_REF_AND_RESET(dst_comp); - } - -end: - bt_object_put_ref(src_comp); - bt_object_put_ref(dst_comp); - return ret; -} - -static int validate_no_cycles_rec(struct bt_config *cfg, GPtrArray *path, - char *error_buf, size_t error_buf_size) -{ - int ret = 0; - size_t conn_i; - const char *src_comp_name; - - BT_ASSERT(path && path->len > 0); - src_comp_name = g_ptr_array_index(path, path->len - 1); - - for (conn_i = 0; conn_i < cfg->cmd_data.run.connections->len; conn_i++) { - struct bt_config_connection *conn = - g_ptr_array_index(cfg->cmd_data.run.connections, conn_i); - - if (strcmp(conn->upstream_comp_name->str, src_comp_name) == 0) { - size_t path_i; - - for (path_i = 0; path_i < path->len; path_i++) { - const char *comp_name = - g_ptr_array_index(path, path_i); - - if (strcmp(comp_name, conn->downstream_comp_name->str) == 0) { - snprintf(error_buf, error_buf_size, - "Invalid connection: connection forms a cycle:\n %s\n", - conn->arg->str); - ret = -1; - goto end; - } - } - - g_ptr_array_add(path, conn->downstream_comp_name->str); - ret = validate_no_cycles_rec(cfg, path, error_buf, - error_buf_size); - if (ret) { - goto end; - } - - g_ptr_array_remove_index(path, path->len - 1); - } - } - -end: - return ret; -} - -static int validate_no_cycles(struct bt_config *cfg, char *error_buf, - size_t error_buf_size) -{ - size_t i; - int ret = 0; - GPtrArray *path; - - path = g_ptr_array_new(); - if (!path) { - ret = -1; - goto end; - } - - g_ptr_array_add(path, NULL); - - for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *conn = - g_ptr_array_index(cfg->cmd_data.run.connections, i); - - g_ptr_array_index(path, 0) = conn->upstream_comp_name->str; - ret = validate_no_cycles_rec(cfg, path, - error_buf, error_buf_size); - if (ret) { - goto end; - } - } - -end: - if (path) { - g_ptr_array_free(path, TRUE); - } - - return ret; -} - -static int validate_all_components_connected_in_array(GPtrArray *comps, - const 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_entry(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; - bt_value *connected_components = bt_value_map_create(); - - if (!connected_components) { - ret = -1; - goto end; - } - - for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *connection = - g_ptr_array_index(cfg->cmd_data.run.connections, i); - - ret = bt_value_map_insert_entry(connected_components, - connection->upstream_comp_name->str, bt_value_null); - if (ret) { - goto end; - } - - ret = bt_value_map_insert_entry(connected_components, - connection->downstream_comp_name->str, bt_value_null); - if (ret) { - goto end; - } - } - - ret = validate_all_components_connected_in_array( - cfg->cmd_data.run.sources, - connected_components, - error_buf, error_buf_size); - if (ret) { - goto end; - } - - ret = validate_all_components_connected_in_array( - cfg->cmd_data.run.filters, - connected_components, - error_buf, error_buf_size); - if (ret) { - goto end; - } - - ret = validate_all_components_connected_in_array( - cfg->cmd_data.run.sinks, - connected_components, - error_buf, error_buf_size); - if (ret) { - goto end; - } - -end: - bt_value_put_ref(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; - 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.run.connections->len; i++) { - struct bt_config_connection *connection = - g_ptr_array_index(cfg->cmd_data.run.connections, i); - - g_string_printf(flat_connection_name, "%s\x01%s\x01%s\x01%s", - connection->upstream_comp_name->str, - connection->upstream_port_glob->str, - connection->downstream_comp_name->str, - connection->downstream_port_glob->str); - - if (bt_value_map_has_entry(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_entry(flat_connection_names, - flat_connection_name->str, bt_value_null); - if (ret) { - goto end; - } - } - -end: - bt_value_put_ref(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_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; - } - - ret = validate_no_cycles(cfg, error_buf, error_buf_size); - if (ret) { - goto end; - } - -end: - return ret; -} - -int bt_config_cli_args_create_connections(struct bt_config *cfg, - const bt_value *connection_args, - char *error_buf, size_t error_buf_size) -{ - int ret; - size_t i; - - if (!all_named_and_printable(cfg)) { - snprintf(error_buf, error_buf_size, - "One or more components are unnamed (use --name) or contain a non-printable character\n"); - goto error; - } - - for (i = 0; i < bt_value_array_get_size(connection_args); i++) { - const bt_value *arg_value = - bt_value_array_borrow_element_by_index_const( - connection_args, i); - const char *arg; - struct bt_config_connection *cfg_connection; - - arg = bt_value_string_get(arg_value); - 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.run.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/cli/babeltrace-cfg-cli-args-connect.h b/cli/babeltrace-cfg-cli-args-connect.h deleted file mode 100644 index 5e81f794..00000000 --- a/cli/babeltrace-cfg-cli-args-connect.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H -#define CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H - -/* - * Copyright 2017 Philippe Proulx - * - * 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 -#include -#include -#include -#include "babeltrace-cfg.h" - -int bt_config_cli_args_create_connections(struct bt_config *cfg, - const bt_value *connection_args, - char *error_buf, size_t error_buf_size); - -#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H */ diff --git a/cli/babeltrace-cfg-cli-args-default.c b/cli/babeltrace-cfg-cli-args-default.c deleted file mode 100644 index ec7b4803..00000000 --- a/cli/babeltrace-cfg-cli-args-default.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2016 - Jérémie Galarneau - * Copyright 2017 - Philippe Proulx - * - * 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 -#include "babeltrace-cfg.h" -#include "babeltrace-cfg-cli-args.h" -#include "babeltrace-cfg-cli-args-default.h" - -#ifdef ENABLE_DEBUG_INFO -# define BT_ENABLE_DEBUG_INFO 1 -#else -# define BT_ENABLE_DEBUG_INFO 0 -#endif - -#ifdef BT_SET_DEFAULT_IN_TREE_CONFIGURATION - -struct bt_config *bt_config_cli_args_create_with_default(int argc, - const char *argv[], int *retcode) -{ - bt_value *initial_plugin_paths; - struct bt_config *cfg = NULL; - int ret; - - initial_plugin_paths = bt_value_array_create(); - if (!initial_plugin_paths) { - goto error; - } - - ret = bt_config_append_plugin_paths(initial_plugin_paths, - CONFIG_IN_TREE_PLUGIN_PATH); - if (ret) { - goto error; - } - - cfg = bt_config_cli_args_create(argc, argv, retcode, true, true, - initial_plugin_paths); - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - bt_value_put_ref(initial_plugin_paths); - return cfg; -} - -#else /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ - -struct bt_config *bt_config_cli_args_create_with_default(int argc, - const char *argv[], int *retcode) -{ - return bt_config_cli_args_create(argc, argv, retcode, false, false, - NULL); -} - -#endif /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ diff --git a/cli/babeltrace-cfg-cli-args-default.h b/cli/babeltrace-cfg-cli-args-default.h deleted file mode 100644 index e62b1408..00000000 --- a/cli/babeltrace-cfg-cli-args-default.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H -#define CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H - -/* - * Babeltrace Trace Converter - Default Configuration - * - * Copyright 2016 Jérémie Galarneau - * - * 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-cfg.h" - -struct bt_config *bt_config_cli_args_create_with_default(int argc, - const char *argv[], int *retcode); - -#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H */ diff --git a/cli/babeltrace-cfg-cli-args.c b/cli/babeltrace-cfg-cli-args.c deleted file mode 100644 index 05193d3b..00000000 --- a/cli/babeltrace-cfg-cli-args.c +++ /dev/null @@ -1,5107 +0,0 @@ -/* - * Babeltrace trace converter - parameter parsing - * - * Copyright 2016 Philippe Proulx - * - * 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-CLI-ARGS" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "babeltrace-cfg.h" -#include "babeltrace-cfg-cli-args.h" -#include "babeltrace-cfg-cli-args-connect.h" -#include "version.h" - -/* - * Error printf() macro which prepends "Error: " the first time it's - * called. This gives a nicer feel than having a bunch of error prefixes - * (since the following lines usually describe the error and possible - * solutions), or the error prefix just at the end. - */ -#define printf_err(fmt, args...) \ - do { \ - if (is_first_error) { \ - fprintf(stderr, "Command line error: "); \ - is_first_error = false; \ - } \ - fprintf(stderr, fmt, ##args); \ - } while (0) - -static bool is_first_error = true; - -/* INI-style parsing FSM states */ -enum ini_parsing_fsm_state { - /* Expect a map key (identifier) */ - INI_EXPECT_MAP_KEY, - - /* Expect an equal character ('=') */ - INI_EXPECT_EQUAL, - - /* Expect a value */ - INI_EXPECT_VALUE, - - /* Expect a comma character (',') */ - INI_EXPECT_COMMA, -}; - -/* INI-style parsing state variables */ -struct ini_parsing_state { - /* Lexical scanner (owned by this) */ - GScanner *scanner; - - /* Output map value object being filled (owned by this) */ - bt_value *params; - - /* Next expected FSM state */ - enum ini_parsing_fsm_state expecting; - - /* Last decoded map key (owned by this) */ - char *last_map_key; - - /* Complete INI-style string to parse (not owned by this) */ - const char *arg; - - /* Error buffer (not owned by this) */ - GString *ini_error; -}; - -/* Offset option with "is set" boolean */ -struct offset_opt { - int64_t value; - bool is_set; -}; - -/* Legacy "ctf"/"lttng-live" format options */ -struct ctf_legacy_opts { - struct offset_opt offset_s; - struct offset_opt offset_ns; - bool stream_intersection; -}; - -/* Legacy "text" format options */ -struct text_legacy_opts { - /* - * output, dbg_info_dir, dbg_info_target_prefix, names, - * and fields are owned by this. - */ - GString *output; - GString *dbg_info_dir; - GString *dbg_info_target_prefix; - const bt_value *names; - const bt_value *fields; - - /* Flags */ - bool no_delta; - bool clock_cycles; - bool clock_seconds; - bool clock_date; - bool clock_gmt; - bool dbg_info_full_path; - bool verbose; -}; - -/* Legacy input format format */ -enum legacy_input_format { - LEGACY_INPUT_FORMAT_NONE = 0, - LEGACY_INPUT_FORMAT_CTF, - LEGACY_INPUT_FORMAT_LTTNG_LIVE, -}; - -/* Legacy output format format */ -enum legacy_output_format { - LEGACY_OUTPUT_FORMAT_NONE = 0, - LEGACY_OUTPUT_FORMAT_TEXT, - LEGACY_OUTPUT_FORMAT_DUMMY, -}; - -/* - * Prints the "out of memory" error. - */ -static -void print_err_oom(void) -{ - printf_err("Out of memory\n"); -} - -/* - * Appends an "expecting token" error to the INI-style parsing state's - * error buffer. - */ -static -void ini_append_error_expecting(struct ini_parsing_state *state, - GScanner *scanner, const char *expecting) -{ - size_t i; - size_t pos; - - g_string_append_printf(state->ini_error, "Expecting %s:\n", expecting); - - /* Only print error if there's one line */ - if (strchr(state->arg, '\n') != NULL || strlen(state->arg) == 0) { - return; - } - - g_string_append_printf(state->ini_error, "\n %s\n", state->arg); - pos = g_scanner_cur_position(scanner) + 4; - - if (!g_scanner_eof(scanner)) { - pos--; - } - - for (i = 0; i < pos; ++i) { - g_string_append_printf(state->ini_error, " "); - } - - g_string_append_printf(state->ini_error, "^\n\n"); -} - -/* Parse the next token as an unsigned integer. */ -static -bt_value *ini_parse_uint(struct ini_parsing_state *state) -{ - bt_value *value = NULL; - GTokenType token_type = g_scanner_get_next_token(state->scanner); - - if (token_type != G_TOKEN_INT) { - ini_append_error_expecting(state, state->scanner, - "integer value"); - goto end; - } - - value = bt_value_unsigned_integer_create_init( - state->scanner->value.v_int64); - -end: - return value; -} - -/* Parse the next token as a number and return its negation. */ -static -bt_value *ini_parse_neg_number(struct ini_parsing_state *state) -{ - bt_value *value = NULL; - GTokenType token_type = g_scanner_get_next_token(state->scanner); - - switch (token_type) { - case G_TOKEN_INT: - { - /* Negative integer */ - uint64_t int_val = state->scanner->value.v_int64; - - if (int_val > (((uint64_t) INT64_MAX) + 1)) { - g_string_append_printf(state->ini_error, - "Integer value -%" PRIu64 " is outside the range of a 64-bit signed integer\n", - int_val); - } else { - value = bt_value_signed_integer_create_init( - -((int64_t) int_val)); - } - - break; - } - case G_TOKEN_FLOAT: - /* Negative floating point number */ - value = bt_value_real_create_init(-state->scanner->value.v_float); - break; - default: - ini_append_error_expecting(state, state->scanner, "value"); - break; - } - - return value; -} - -static bt_value *ini_parse_value(struct ini_parsing_state *state); - -/* - * Parse the current and following tokens as an array. Arrays are formatted as - * an opening `[`, a list of comma-separated values and a closing `]`. For - * convenience we support an optional trailing comma, after the last value. - * - * The current token of the parser must be the opening square bracket of the - * array. - */ -static -bt_value *ini_parse_array(struct ini_parsing_state *state) -{ - /* The [ character must have already been ingested. */ - BT_ASSERT(g_scanner_cur_token(state->scanner) == G_TOKEN_CHAR); - BT_ASSERT(g_scanner_cur_value(state->scanner).v_char == '['); - - bt_value *array_value; - GTokenType token_type; - - array_value = bt_value_array_create (); - token_type = g_scanner_get_next_token(state->scanner); - - /* While the current token is not a ]... */ - while (!(token_type == G_TOKEN_CHAR && g_scanner_cur_value(state->scanner).v_char == ']')) { - /* Parse the item... */ - bt_value *item_value; - bt_value_status status; - - item_value = ini_parse_value(state); - if (!item_value) { - goto error; - } - - /* ... and add it to the result array. */ - status = bt_value_array_append_element(array_value, item_value); - BT_VALUE_PUT_REF_AND_RESET(item_value); - - if (status != BT_VALUE_STATUS_OK) { - goto error; - } - - /* - * Ingest the token following the value, it should be either a - * comma or closing square brace. - */ - token_type = g_scanner_get_next_token(state->scanner); - - if (token_type == G_TOKEN_CHAR && g_scanner_cur_value(state->scanner).v_char == ',') { - /* - * Ingest the token following the comma. If it happens - * to be a closing square bracket, we'll exit the loop - * and we are done (we allow trailing commas). - * Otherwise, we are ready for the next ini_parse_value call. - */ - token_type = g_scanner_get_next_token(state->scanner); - } else if (token_type != G_TOKEN_CHAR || g_scanner_cur_value(state->scanner).v_char != ']') { - ini_append_error_expecting(state, state->scanner, ", or ]"); - goto error; - } - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(array_value); - -end: - return array_value; -} - -/* - * Parse the current token (and the following ones if needed) as a value, return - * it as a bt_value. - */ -static -bt_value *ini_parse_value(struct ini_parsing_state *state) -{ - bt_value *value = NULL; - GTokenType token_type = state->scanner->token; - - switch (token_type) { - case G_TOKEN_CHAR: - if (state->scanner->value.v_char == '-') { - /* Negative number */ - value = ini_parse_neg_number(state); - } else if (state->scanner->value.v_char == '+') { - /* Unsigned integer */ - value = ini_parse_uint(state); - } else if (state->scanner->value.v_char == '[') { - /* Array */ - value = ini_parse_array(state); - } else { - ini_append_error_expecting(state, state->scanner, "value"); - } - break; - case G_TOKEN_INT: - { - /* Positive, signed integer */ - uint64_t int_val = state->scanner->value.v_int64; - - if (int_val > INT64_MAX) { - g_string_append_printf(state->ini_error, - "Integer value %" PRIu64 " is outside the range of a 64-bit signed integer\n", - int_val); - } else { - value = bt_value_signed_integer_create_init( - (int64_t) int_val); - } - break; - } - case G_TOKEN_FLOAT: - /* Positive floating point number */ - value = bt_value_real_create_init(state->scanner->value.v_float); - break; - case G_TOKEN_STRING: - /* Quoted string */ - value = bt_value_string_create_init(state->scanner->value.v_string); - break; - case G_TOKEN_IDENTIFIER: - { - /* - * Using symbols would be appropriate here, - * but said symbols are allowed as map key, - * so it's easier to consider everything an - * identifier. - * - * If one of the known symbols is not - * recognized here, then fall back to creating - * a string value. - */ - const char *id = state->scanner->value.v_identifier; - - if (!strcmp(id, "null") || !strcmp(id, "NULL") || - !strcmp(id, "nul")) { - value = bt_value_null; - } else if (!strcmp(id, "true") || !strcmp(id, "TRUE") || - !strcmp(id, "yes") || - !strcmp(id, "YES")) { - value = bt_value_bool_create_init(true); - } else if (!strcmp(id, "false") || - !strcmp(id, "FALSE") || - !strcmp(id, "no") || - !strcmp(id, "NO")) { - value = bt_value_bool_create_init(false); - } else { - value = bt_value_string_create_init(id); - } - break; - } - default: - /* Unset value variable will trigger the error */ - ini_append_error_expecting(state, state->scanner, "value"); - break; - } - - return value; -} - -static -int ini_handle_state(struct ini_parsing_state *state) -{ - int ret = 0; - GTokenType token_type; - bt_value *value = NULL; - - token_type = g_scanner_get_next_token(state->scanner); - if (token_type == G_TOKEN_EOF) { - if (state->expecting != INI_EXPECT_COMMA) { - switch (state->expecting) { - case INI_EXPECT_EQUAL: - ini_append_error_expecting(state, - state->scanner, "'='"); - break; - case INI_EXPECT_VALUE: - ini_append_error_expecting(state, - state->scanner, "value"); - break; - case INI_EXPECT_MAP_KEY: - ini_append_error_expecting(state, - state->scanner, "unquoted map key"); - break; - default: - break; - } - goto error; - } - - /* We're done! */ - ret = 1; - goto success; - } - - switch (state->expecting) { - case INI_EXPECT_MAP_KEY: - if (token_type != G_TOKEN_IDENTIFIER) { - ini_append_error_expecting(state, state->scanner, - "unquoted map key"); - goto error; - } - - free(state->last_map_key); - state->last_map_key = - strdup(state->scanner->value.v_identifier); - if (!state->last_map_key) { - g_string_append(state->ini_error, - "Out of memory\n"); - goto error; - } - - if (bt_value_map_has_entry(state->params, - state->last_map_key)) { - g_string_append_printf(state->ini_error, - "Duplicate parameter key: `%s`\n", - state->last_map_key); - goto error; - } - - state->expecting = INI_EXPECT_EQUAL; - goto success; - case INI_EXPECT_EQUAL: - if (token_type != G_TOKEN_CHAR) { - ini_append_error_expecting(state, - state->scanner, "'='"); - goto error; - } - - if (state->scanner->value.v_char != '=') { - ini_append_error_expecting(state, - state->scanner, "'='"); - goto error; - } - - state->expecting = INI_EXPECT_VALUE; - goto success; - case INI_EXPECT_VALUE: - { - value = ini_parse_value(state); - if (!value) { - goto error; - } - - state->expecting = INI_EXPECT_COMMA; - goto success; - } - case INI_EXPECT_COMMA: - if (token_type != G_TOKEN_CHAR) { - ini_append_error_expecting(state, - state->scanner, "','"); - goto error; - } - - if (state->scanner->value.v_char != ',') { - ini_append_error_expecting(state, - state->scanner, "','"); - goto error; - } - - state->expecting = INI_EXPECT_MAP_KEY; - goto success; - default: - abort(); - } - -error: - ret = -1; - goto end; - -success: - if (value) { - if (bt_value_map_insert_entry(state->params, - state->last_map_key, value)) { - /* Only override return value on error */ - ret = -1; - } - } - -end: - BT_VALUE_PUT_REF_AND_RESET(value); - return ret; -} - -/* - * Converts an INI-style argument to an equivalent map value object. - * - * Return value is owned by the caller. - */ -static -bt_value *bt_value_from_ini(const char *arg, - GString *ini_error) -{ - /* Lexical scanner configuration */ - GScannerConfig scanner_config = { - /* Skip whitespaces */ - .cset_skip_characters = " \t\n", - - /* Identifier syntax is: [a-zA-Z_][a-zA-Z0-9_.:-]* */ - .cset_identifier_first = - G_CSET_a_2_z - "_" - G_CSET_A_2_Z, - .cset_identifier_nth = - G_CSET_a_2_z - "_0123456789-.:" - G_CSET_A_2_Z, - - /* "hello" and "Hello" two different keys */ - .case_sensitive = TRUE, - - /* No comments */ - .cpair_comment_single = NULL, - .skip_comment_multi = TRUE, - .skip_comment_single = TRUE, - .scan_comment_multi = FALSE, - - /* - * Do scan identifiers, including 1-char identifiers, - * but NULL is a normal identifier. - */ - .scan_identifier = TRUE, - .scan_identifier_1char = TRUE, - .scan_identifier_NULL = FALSE, - - /* - * No specific symbols: null and boolean "symbols" are - * scanned as plain identifiers. - */ - .scan_symbols = FALSE, - .symbol_2_token = FALSE, - .scope_0_fallback = FALSE, - - /* - * Scan "0b"-, "0"-, and "0x"-prefixed integers, but not - * integers prefixed with "$". - */ - .scan_binary = TRUE, - .scan_octal = TRUE, - .scan_float = TRUE, - .scan_hex = TRUE, - .scan_hex_dollar = FALSE, - - /* Convert scanned numbers to integer tokens */ - .numbers_2_int = TRUE, - - /* Support both integers and floating-point numbers */ - .int_2_float = FALSE, - - /* Scan integers as 64-bit signed integers */ - .store_int64 = TRUE, - - /* Only scan double-quoted strings */ - .scan_string_sq = FALSE, - .scan_string_dq = TRUE, - - /* Do not converter identifiers to string tokens */ - .identifier_2_string = FALSE, - - /* Scan characters as G_TOKEN_CHAR token */ - .char_2_token = FALSE, - }; - struct ini_parsing_state state = { - .scanner = NULL, - .params = NULL, - .expecting = INI_EXPECT_MAP_KEY, - .arg = arg, - .ini_error = ini_error, - }; - - state.params = bt_value_map_create(); - if (!state.params) { - goto error; - } - - state.scanner = g_scanner_new(&scanner_config); - if (!state.scanner) { - goto error; - } - - /* Let the scan begin */ - g_scanner_input_text(state.scanner, arg, strlen(arg)); - - while (true) { - int ret = ini_handle_state(&state); - - if (ret < 0) { - /* Error */ - goto error; - } else if (ret > 0) { - /* Done */ - break; - } - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(state.params); - -end: - if (state.scanner) { - g_scanner_destroy(state.scanner); - } - - free(state.last_map_key); - return state.params; -} - -/* - * Returns the parameters map value object from a command-line - * parameter option's argument. - * - * Return value is owned by the caller. - */ -static -bt_value *bt_value_from_arg(const char *arg) -{ - bt_value *params = NULL; - GString *ini_error = NULL; - - ini_error = g_string_new(NULL); - if (!ini_error) { - print_err_oom(); - goto end; - } - - /* Try INI-style parsing */ - params = bt_value_from_ini(arg, ini_error); - if (!params) { - printf_err("%s", ini_error->str); - goto end; - } - -end: - if (ini_error) { - g_string_free(ini_error, TRUE); - } - - return params; -} - -/* - * Returns the plugin name, component class name, component class type, - * and component name from a command-line --component option's argument. - * arg must have the following format: - * - * [NAME:]TYPE.PLUGIN.CLS - * - * where NAME is the optional component name, TYPE is either `source`, - * `filter`, or `sink`, PLUGIN is the plugin name, and CLS is the - * component class name. - * - * On success, both *plugin and *component are not NULL. *plugin - * and *comp_cls are owned by the caller. On success, *name can be NULL - * if no component class name was found, and *comp_cls_type is set. - */ -static -void plugin_comp_cls_names(const char *arg, char **name, char **plugin, - char **comp_cls, bt_component_class_type *comp_cls_type) -{ - const char *at = arg; - GString *gs_name = NULL; - GString *gs_comp_cls_type = NULL; - GString *gs_plugin = NULL; - GString *gs_comp_cls = NULL; - size_t end_pos; - - BT_ASSERT(arg); - BT_ASSERT(plugin); - BT_ASSERT(comp_cls); - BT_ASSERT(comp_cls_type); - - if (!bt_common_string_is_printable(arg)) { - printf_err("Argument contains a non-printable character\n"); - goto error; - } - - /* Parse the component name */ - gs_name = bt_common_string_until(at, ".:\\", ":", &end_pos); - if (!gs_name) { - goto error; - } - - if (arg[end_pos] == ':') { - at += end_pos + 1; - } else { - /* No name */ - g_string_assign(gs_name, ""); - } - - /* Parse the component class type */ - gs_comp_cls_type = bt_common_string_until(at, ".:\\", ".", &end_pos); - if (!gs_comp_cls_type || at[end_pos] == '\0') { - printf_err("Missing component class type (`source`, `filter`, or `sink`)\n"); - goto error; - } - - if (strcmp(gs_comp_cls_type->str, "source") == 0 || - strcmp(gs_comp_cls_type->str, "src") == 0) { - *comp_cls_type = BT_COMPONENT_CLASS_TYPE_SOURCE; - } else if (strcmp(gs_comp_cls_type->str, "filter") == 0 || - strcmp(gs_comp_cls_type->str, "flt") == 0) { - *comp_cls_type = BT_COMPONENT_CLASS_TYPE_FILTER; - } else if (strcmp(gs_comp_cls_type->str, "sink") == 0) { - *comp_cls_type = BT_COMPONENT_CLASS_TYPE_SINK; - } else { - printf_err("Unknown component class type: `%s`\n", - gs_comp_cls_type->str); - goto error; - } - - at += end_pos + 1; - - /* Parse the plugin name */ - gs_plugin = bt_common_string_until(at, ".:\\", ".", &end_pos); - if (!gs_plugin || gs_plugin->len == 0 || at[end_pos] == '\0') { - printf_err("Missing plugin or component class name\n"); - goto error; - } - - at += end_pos + 1; - - /* Parse the component class name */ - gs_comp_cls = bt_common_string_until(at, ".:\\", ".", &end_pos); - if (!gs_comp_cls || gs_comp_cls->len == 0) { - printf_err("Missing component class name\n"); - goto error; - } - - if (at[end_pos] != '\0') { - /* Found a non-escaped `.` */ - goto error; - } - - if (name) { - if (gs_name->len == 0) { - *name = NULL; - g_string_free(gs_name, TRUE); - } else { - *name = gs_name->str; - g_string_free(gs_name, FALSE); - } - } else { - g_string_free(gs_name, TRUE); - } - - *plugin = gs_plugin->str; - *comp_cls = gs_comp_cls->str; - g_string_free(gs_plugin, FALSE); - g_string_free(gs_comp_cls, FALSE); - gs_name = NULL; - gs_plugin = NULL; - gs_comp_cls = NULL; - goto end; - -error: - if (name) { - *name = NULL; - } - - *plugin = NULL; - *comp_cls = NULL; - -end: - if (gs_name) { - g_string_free(gs_name, TRUE); - } - - if (gs_plugin) { - g_string_free(gs_plugin, TRUE); - } - - if (gs_comp_cls) { - g_string_free(gs_comp_cls, TRUE); - } - - if (gs_comp_cls_type) { - g_string_free(gs_comp_cls_type, TRUE); - } - - return; -} - -/* - * Prints the Babeltrace version. - */ -static -void print_version(void) -{ - if (GIT_VERSION[0] == '\0') { - puts("Babeltrace " VERSION); - } else { - puts("Babeltrace " VERSION " - " GIT_VERSION); - } -} - -/* - * Destroys a component configuration. - */ -static -void bt_config_component_destroy(bt_object *obj) -{ - struct bt_config_component *bt_config_component = - container_of(obj, struct bt_config_component, base); - - if (!obj) { - goto end; - } - - if (bt_config_component->plugin_name) { - g_string_free(bt_config_component->plugin_name, TRUE); - } - - if (bt_config_component->comp_cls_name) { - g_string_free(bt_config_component->comp_cls_name, TRUE); - } - - if (bt_config_component->instance_name) { - g_string_free(bt_config_component->instance_name, TRUE); - } - - BT_VALUE_PUT_REF_AND_RESET(bt_config_component->params); - g_free(bt_config_component); - -end: - return; -} - -/* - * Creates a component configuration using the given plugin name and - * component name. `plugin_name` and `comp_cls_name` are copied (belong - * to the return value). - * - * Return value is owned by the caller. - */ -static -struct bt_config_component *bt_config_component_create( - bt_component_class_type type, - const char *plugin_name, const char *comp_cls_name) -{ - struct bt_config_component *cfg_component = NULL; - - cfg_component = g_new0(struct bt_config_component, 1); - if (!cfg_component) { - print_err_oom(); - goto error; - } - - bt_object_init_shared(&cfg_component->base, - bt_config_component_destroy); - cfg_component->type = type; - cfg_component->plugin_name = g_string_new(plugin_name); - if (!cfg_component->plugin_name) { - print_err_oom(); - goto error; - } - - cfg_component->comp_cls_name = g_string_new(comp_cls_name); - if (!cfg_component->comp_cls_name) { - print_err_oom(); - goto error; - } - - cfg_component->instance_name = g_string_new(NULL); - if (!cfg_component->instance_name) { - print_err_oom(); - goto error; - } - - /* Start with empty parameters */ - cfg_component->params = bt_value_map_create(); - if (!cfg_component->params) { - print_err_oom(); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg_component); - -end: - return cfg_component; -} - -/* - * Creates a component configuration from a command-line --component - * option's argument. - */ -static -struct bt_config_component *bt_config_component_from_arg(const char *arg) -{ - struct bt_config_component *cfg_comp = NULL; - char *name = NULL; - char *plugin_name = NULL; - char *comp_cls_name = NULL; - bt_component_class_type type; - - plugin_comp_cls_names(arg, &name, &plugin_name, &comp_cls_name, &type); - if (!plugin_name || !comp_cls_name) { - goto error; - } - - cfg_comp = bt_config_component_create(type, plugin_name, comp_cls_name); - if (!cfg_comp) { - goto error; - } - - if (name) { - g_string_assign(cfg_comp->instance_name, name); - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg_comp); - -end: - g_free(name); - g_free(plugin_name); - g_free(comp_cls_name); - return cfg_comp; -} - -/* - * Destroys a configuration. - */ -static -void bt_config_destroy(bt_object *obj) -{ - struct bt_config *cfg = - container_of(obj, struct bt_config, base); - - if (!obj) { - goto end; - } - - BT_VALUE_PUT_REF_AND_RESET(cfg->plugin_paths); - - switch (cfg->command) { - case BT_CONFIG_COMMAND_RUN: - if (cfg->cmd_data.run.sources) { - g_ptr_array_free(cfg->cmd_data.run.sources, TRUE); - } - - if (cfg->cmd_data.run.filters) { - g_ptr_array_free(cfg->cmd_data.run.filters, TRUE); - } - - if (cfg->cmd_data.run.sinks) { - g_ptr_array_free(cfg->cmd_data.run.sinks, TRUE); - } - - if (cfg->cmd_data.run.connections) { - g_ptr_array_free(cfg->cmd_data.run.connections, - TRUE); - } - break; - case BT_CONFIG_COMMAND_LIST_PLUGINS: - break; - case BT_CONFIG_COMMAND_HELP: - BT_OBJECT_PUT_REF_AND_RESET(cfg->cmd_data.help.cfg_component); - break; - case BT_CONFIG_COMMAND_QUERY: - BT_OBJECT_PUT_REF_AND_RESET(cfg->cmd_data.query.cfg_component); - - if (cfg->cmd_data.query.object) { - g_string_free(cfg->cmd_data.query.object, TRUE); - } - break; - case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: - if (cfg->cmd_data.print_ctf_metadata.path) { - g_string_free(cfg->cmd_data.print_ctf_metadata.path, - TRUE); - g_string_free( - cfg->cmd_data.print_ctf_metadata.output_path, - TRUE); - } - break; - case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: - if (cfg->cmd_data.print_lttng_live_sessions.url) { - g_string_free( - cfg->cmd_data.print_lttng_live_sessions.url, - TRUE); - g_string_free( - cfg->cmd_data.print_lttng_live_sessions.output_path, - TRUE); - } - break; - default: - abort(); - } - - g_free(cfg); - -end: - return; -} - -static -void destroy_glist_of_gstring(GList *list) -{ - GList *at; - - if (!list) { - return; - } - - for (at = list; at != NULL; at = g_list_next(at)) { - g_string_free(at->data, TRUE); - } - - g_list_free(list); -} - -/* - * Creates a simple lexical scanner for parsing comma-delimited names - * and fields. - * - * Return value is owned by the caller. - */ -static -GScanner *create_csv_identifiers_scanner(void) -{ - GScanner *scanner; - GScannerConfig scanner_config = { - .cset_skip_characters = " \t\n", - .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "_", - .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z ":_-", - .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, - }; - - scanner = g_scanner_new(&scanner_config); - if (!scanner) { - print_err_oom(); - } - - return scanner; -} - -/* - * Converts a comma-delimited list of known names (--names option) to - * an array value object containing those names as string value objects. - * - * Return value is owned by the caller. - */ -static -bt_value *names_from_arg(const char *arg) -{ - GScanner *scanner = NULL; - bt_value *names = NULL; - bool found_all = false, found_none = false, found_item = false; - - names = bt_value_array_create(); - if (!names) { - print_err_oom(); - goto error; - } - - scanner = create_csv_identifiers_scanner(); - if (!scanner) { - goto error; - } - - g_scanner_input_text(scanner, arg, strlen(arg)); - - while (true) { - GTokenType token_type = g_scanner_get_next_token(scanner); - - switch (token_type) { - case G_TOKEN_IDENTIFIER: - { - const char *identifier = scanner->value.v_identifier; - - if (!strcmp(identifier, "payload") || - !strcmp(identifier, "args") || - !strcmp(identifier, "arg")) { - found_item = true; - if (bt_value_array_append_string_element(names, - "payload")) { - goto error; - } - } else if (!strcmp(identifier, "context") || - !strcmp(identifier, "ctx")) { - found_item = true; - if (bt_value_array_append_string_element(names, - "context")) { - goto error; - } - } else if (!strcmp(identifier, "scope") || - !strcmp(identifier, "header")) { - found_item = true; - if (bt_value_array_append_string_element(names, - identifier)) { - goto error; - } - } else if (!strcmp(identifier, "all")) { - found_all = true; - if (bt_value_array_append_string_element(names, - identifier)) { - goto error; - } - } else if (!strcmp(identifier, "none")) { - found_none = true; - if (bt_value_array_append_string_element(names, - identifier)) { - goto error; - } - } else { - printf_err("Unknown name: `%s`\n", - identifier); - goto error; - } - break; - } - case G_TOKEN_COMMA: - continue; - case G_TOKEN_EOF: - goto end; - default: - goto error; - } - } - -end: - if (found_none && found_all) { - printf_err("Only either `all` or `none` can be specified in the list given to the --names option, but not both.\n"); - goto error; - } - /* - * Legacy behavior is to clear the defaults (show none) when at - * least one item is specified. - */ - if (found_item && !found_none && !found_all) { - if (bt_value_array_append_string_element(names, "none")) { - goto error; - } - } - if (scanner) { - g_scanner_destroy(scanner); - } - return names; - -error: - BT_VALUE_PUT_REF_AND_RESET(names); - if (scanner) { - g_scanner_destroy(scanner); - } - return names; -} - -/* - * Converts a comma-delimited list of known fields (--fields option) to - * an array value object containing those fields as string - * value objects. - * - * Return value is owned by the caller. - */ -static -bt_value *fields_from_arg(const char *arg) -{ - GScanner *scanner = NULL; - bt_value *fields; - - fields = bt_value_array_create(); - if (!fields) { - print_err_oom(); - goto error; - } - - scanner = create_csv_identifiers_scanner(); - if (!scanner) { - goto error; - } - - g_scanner_input_text(scanner, arg, strlen(arg)); - - while (true) { - GTokenType token_type = g_scanner_get_next_token(scanner); - - switch (token_type) { - case G_TOKEN_IDENTIFIER: - { - const char *identifier = scanner->value.v_identifier; - - if (!strcmp(identifier, "trace") || - !strcmp(identifier, "trace:hostname") || - !strcmp(identifier, "trace:domain") || - !strcmp(identifier, "trace:procname") || - !strcmp(identifier, "trace:vpid") || - !strcmp(identifier, "loglevel") || - !strcmp(identifier, "emf") || - !strcmp(identifier, "callsite") || - !strcmp(identifier, "all")) { - if (bt_value_array_append_string_element(fields, - identifier)) { - goto error; - } - } else { - printf_err("Unknown field: `%s`\n", - identifier); - goto error; - } - break; - } - case G_TOKEN_COMMA: - continue; - case G_TOKEN_EOF: - goto end; - default: - goto error; - } - } - - goto end; - -error: - BT_VALUE_PUT_REF_AND_RESET(fields); - -end: - if (scanner) { - g_scanner_destroy(scanner); - } - return fields; -} - -static -void append_param_arg(GString *params_arg, const char *key, const char *value) -{ - BT_ASSERT(params_arg); - BT_ASSERT(key); - BT_ASSERT(value); - - if (params_arg->len != 0) { - g_string_append_c(params_arg, ','); - } - - g_string_append(params_arg, key); - g_string_append_c(params_arg, '='); - g_string_append(params_arg, value); -} - -/* - * Inserts the equivalent "prefix-NAME=yes" strings into params_arg - * where the names are in names_array. - */ -static -int insert_flat_params_from_array(GString *params_arg, - const bt_value *names_array, const char *prefix) -{ - int ret = 0; - int i; - GString *tmpstr = NULL, *default_value = NULL; - bool default_set = false, non_default_set = false; - - /* - * names_array may be NULL if no CLI options were specified to - * trigger its creation. - */ - if (!names_array) { - goto end; - } - - tmpstr = g_string_new(NULL); - if (!tmpstr) { - print_err_oom(); - ret = -1; - goto end; - } - - default_value = g_string_new(NULL); - if (!default_value) { - print_err_oom(); - ret = -1; - goto end; - } - - for (i = 0; i < bt_value_array_get_size(names_array); i++) { - const bt_value *str_obj = - bt_value_array_borrow_element_by_index_const(names_array, - i); - const char *suffix; - bool is_default = false; - - if (!str_obj) { - printf_err("Unexpected error\n"); - ret = -1; - goto end; - } - - suffix = bt_value_string_get(str_obj); - - g_string_assign(tmpstr, prefix); - g_string_append(tmpstr, "-"); - - /* Special-case for "all" and "none". */ - if (!strcmp(suffix, "all")) { - is_default = true; - g_string_assign(default_value, "show"); - } else if (!strcmp(suffix, "none")) { - is_default = true; - g_string_assign(default_value, "hide"); - } - if (is_default) { - default_set = true; - g_string_append(tmpstr, "default"); - append_param_arg(params_arg, tmpstr->str, - default_value->str); - } else { - non_default_set = true; - g_string_append(tmpstr, suffix); - append_param_arg(params_arg, tmpstr->str, "yes"); - } - } - - /* Implicit field-default=hide if any non-default option is set. */ - if (non_default_set && !default_set) { - g_string_assign(tmpstr, prefix); - g_string_append(tmpstr, "-default"); - g_string_assign(default_value, "hide"); - append_param_arg(params_arg, tmpstr->str, default_value->str); - } - -end: - if (default_value) { - g_string_free(default_value, TRUE); - } - - if (tmpstr) { - g_string_free(tmpstr, TRUE); - } - - return ret; -} - -/* popt options */ -enum { - OPT_NONE = 0, - OPT_BASE_PARAMS, - OPT_BEGIN, - OPT_CLOCK_CYCLES, - OPT_CLOCK_DATE, - OPT_CLOCK_FORCE_CORRELATE, - OPT_CLOCK_GMT, - OPT_CLOCK_OFFSET, - OPT_CLOCK_OFFSET_NS, - OPT_CLOCK_SECONDS, - OPT_COLOR, - OPT_COMPONENT, - OPT_CONNECT, - OPT_DEBUG, - OPT_DEBUG_INFO, - OPT_DEBUG_INFO_DIR, - OPT_DEBUG_INFO_FULL_PATH, - OPT_DEBUG_INFO_TARGET_PREFIX, - OPT_END, - OPT_FIELDS, - OPT_HELP, - OPT_INPUT_FORMAT, - OPT_LIST, - OPT_NAME, - OPT_NAMES, - OPT_NO_DELTA, - OPT_OMIT_HOME_PLUGIN_PATH, - OPT_OMIT_SYSTEM_PLUGIN_PATH, - OPT_OUTPUT, - OPT_OUTPUT_FORMAT, - OPT_PARAMS, - OPT_PATH, - OPT_PLUGIN_PATH, - OPT_RESET_BASE_PARAMS, - OPT_RETRY_DURATION, - OPT_RUN_ARGS, - OPT_RUN_ARGS_0, - OPT_STREAM_INTERSECTION, - OPT_TIMERANGE, - OPT_URL, - OPT_VERBOSE, -}; - -enum bt_config_component_dest { - BT_CONFIG_COMPONENT_DEST_UNKNOWN = -1, - BT_CONFIG_COMPONENT_DEST_SOURCE, - BT_CONFIG_COMPONENT_DEST_FILTER, - BT_CONFIG_COMPONENT_DEST_SINK, -}; - -/* - * Adds a configuration component to the appropriate configuration - * array depending on the destination. - */ -static -void add_run_cfg_comp(struct bt_config *cfg, - struct bt_config_component *cfg_comp, - enum bt_config_component_dest dest) -{ - bt_object_get_ref(cfg_comp); - - switch (dest) { - case BT_CONFIG_COMPONENT_DEST_SOURCE: - g_ptr_array_add(cfg->cmd_data.run.sources, cfg_comp); - break; - case BT_CONFIG_COMPONENT_DEST_FILTER: - g_ptr_array_add(cfg->cmd_data.run.filters, cfg_comp); - break; - case BT_CONFIG_COMPONENT_DEST_SINK: - g_ptr_array_add(cfg->cmd_data.run.sinks, cfg_comp); - break; - default: - abort(); - } -} - -static -int add_run_cfg_comp_check_name(struct bt_config *cfg, - struct bt_config_component *cfg_comp, - enum bt_config_component_dest dest, - bt_value *instance_names) -{ - int ret = 0; - - if (cfg_comp->instance_name->len == 0) { - printf_err("Found an unnamed component\n"); - ret = -1; - goto end; - } - - if (bt_value_map_has_entry(instance_names, - cfg_comp->instance_name->str)) { - printf_err("Duplicate component instance name:\n %s\n", - cfg_comp->instance_name->str); - ret = -1; - goto end; - } - - if (bt_value_map_insert_entry(instance_names, - cfg_comp->instance_name->str, bt_value_null)) { - print_err_oom(); - ret = -1; - goto end; - } - - add_run_cfg_comp(cfg, cfg_comp, dest); - -end: - return ret; -} - -static -int append_env_var_plugin_paths(bt_value *plugin_paths) -{ - int ret = 0; - const char *envvar; - - if (bt_common_is_setuid_setgid()) { - BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); - goto end; - } - - envvar = getenv("BABELTRACE_PLUGIN_PATH"); - if (!envvar) { - goto end; - } - - ret = bt_config_append_plugin_paths(plugin_paths, envvar); - -end: - if (ret) { - printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); - } - - return ret; -} - -static -int append_home_and_system_plugin_paths(bt_value *plugin_paths, - bool omit_system_plugin_path, bool omit_home_plugin_path) -{ - int ret; - - if (!omit_home_plugin_path) { - if (bt_common_is_setuid_setgid()) { - BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); - } else { - char *home_plugin_dir = - bt_common_get_home_plugin_path(); - - if (home_plugin_dir) { - ret = bt_config_append_plugin_paths( - plugin_paths, home_plugin_dir); - free(home_plugin_dir); - - if (ret) { - printf_err("Invalid home plugin path\n"); - goto error; - } - } - } - } - - if (!omit_system_plugin_path) { - if (bt_config_append_plugin_paths(plugin_paths, - bt_common_get_system_plugin_path())) { - printf_err("Invalid system plugin path\n"); - goto error; - } - } - return 0; -error: - printf_err("Cannot append home and system plugin paths\n"); - return -1; -} - -static -int append_home_and_system_plugin_paths_cfg(struct bt_config *cfg) -{ - return append_home_and_system_plugin_paths(cfg->plugin_paths, - cfg->omit_system_plugin_path, cfg->omit_home_plugin_path); -} - -static -struct bt_config *bt_config_base_create(enum bt_config_command command, - const bt_value *initial_plugin_paths, - bool needs_plugins) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = g_new0(struct bt_config, 1); - if (!cfg) { - print_err_oom(); - goto error; - } - - bt_object_init_shared(&cfg->base, bt_config_destroy); - cfg->command = command; - cfg->command_needs_plugins = needs_plugins; - - if (initial_plugin_paths) { - bt_value *initial_plugin_paths_copy; - - (void) bt_value_copy(initial_plugin_paths, - &initial_plugin_paths_copy); - cfg->plugin_paths = initial_plugin_paths_copy; - } else { - cfg->plugin_paths = bt_value_array_create(); - if (!cfg->plugin_paths) { - print_err_oom(); - goto error; - } - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_run_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_RUN, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - cfg->cmd_data.run.sources = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); - if (!cfg->cmd_data.run.sources) { - print_err_oom(); - goto error; - } - - cfg->cmd_data.run.filters = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); - if (!cfg->cmd_data.run.filters) { - print_err_oom(); - goto error; - } - - cfg->cmd_data.run.sinks = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); - if (!cfg->cmd_data.run.sinks) { - print_err_oom(); - goto error; - } - - cfg->cmd_data.run.connections = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_config_connection_destroy); - if (!cfg->cmd_data.run.connections) { - print_err_oom(); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_list_plugins_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_LIST_PLUGINS, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_help_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_HELP, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - cfg->cmd_data.help.cfg_component = - bt_config_component_create(-1, NULL, NULL); - if (!cfg->cmd_data.help.cfg_component) { - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_query_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_QUERY, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - cfg->cmd_data.query.object = g_string_new(NULL); - if (!cfg->cmd_data.query.object) { - print_err_oom(); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_print_ctf_metadata_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_CTF_METADATA, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - cfg->cmd_data.print_ctf_metadata.path = g_string_new(NULL); - if (!cfg->cmd_data.print_ctf_metadata.path) { - print_err_oom(); - goto error; - } - - cfg->cmd_data.print_ctf_metadata.output_path = g_string_new(NULL); - if (!cfg->cmd_data.print_ctf_metadata.output_path) { - print_err_oom(); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -struct bt_config *bt_config_print_lttng_live_sessions_create( - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg; - - /* Create config */ - cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS, - initial_plugin_paths, true); - if (!cfg) { - goto error; - } - - cfg->cmd_data.print_lttng_live_sessions.url = g_string_new(NULL); - if (!cfg->cmd_data.print_lttng_live_sessions.url) { - print_err_oom(); - goto error; - } - - cfg->cmd_data.print_lttng_live_sessions.output_path = - g_string_new(NULL); - if (!cfg->cmd_data.print_lttng_live_sessions.output_path) { - print_err_oom(); - goto error; - } - - goto end; - -error: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - return cfg; -} - -static -int bt_config_append_plugin_paths_check_setuid_setgid( - bt_value *plugin_paths, const char *arg) -{ - int ret = 0; - - if (bt_common_is_setuid_setgid()) { - BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); - goto end; - } - - if (bt_config_append_plugin_paths(plugin_paths, arg)) { - printf_err("Invalid --plugin-path option's argument:\n %s\n", - arg); - ret = -1; - goto end; - } - -end: - return ret; -} - -/* - * Prints the expected format for a --params option. - */ -static -void print_expected_params_format(FILE *fp) -{ - fprintf(fp, "Expected format of PARAMS\n"); - fprintf(fp, "-------------------------\n"); - fprintf(fp, "\n"); - fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n"); - fprintf(fp, "\n"); - fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n"); - fprintf(fp, "where PARAM is the parameter name (C identifier plus the [:.-] characters),\n"); - fprintf(fp, "and VALUE can be one of:\n"); - fprintf(fp, "\n"); - fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n"); - fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n"); - fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n"); - fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n"); - fprintf(fp, " (`0x` prefix) unsigned (with `+` prefix) or signed 64-bit integer.\n"); - fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n"); - fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n"); - fprintf(fp, " the null and boolean value symbols above.\n"); - fprintf(fp, "* Double-quoted string (accepts escape characters).\n"); - fprintf(fp, "* Array, formatted as an opening `[`, a list of comma-separated values\n"); - fprintf(fp, " (as described by the current list) and a closing `]`.\n"); - fprintf(fp, "\n"); - fprintf(fp, "You can put whitespaces allowed around individual `=` and `,` symbols.\n"); - fprintf(fp, "\n"); - fprintf(fp, "Example:\n"); - fprintf(fp, "\n"); - fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n"); - fprintf(fp, " play=+23, observe=3.14, simple=beef, needs-quotes=\"some string\",\n"); - fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\",\n"); - fprintf(fp, " things=[1, \"2\", 3]\n"); - fprintf(fp, "\n"); - fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n"); - fprintf(fp, "babeltrace from a shell.\n"); -} - - -/* - * Prints the help command usage. - */ -static -void print_help_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] help [OPTIONS] PLUGIN\n"); - fprintf(fp, " babeltrace [GENERAL OPTIONS] help [OPTIONS] TYPE.PLUGIN.CLS\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "See `babeltrace --help` for the list of general options.\n"); - fprintf(fp, "\n"); - fprintf(fp, "Use `babeltrace list-plugins` to show the list of available plugins.\n"); -} - -static -struct poptOption help_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, -}; - -/* - * Creates a Babeltrace config object from the arguments of a help - * command. - * - * *retcode is set to the appropriate exit code to use. - */ -static -struct bt_config *bt_config_help_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - poptContext pc = NULL; - char *arg = NULL; - int opt; - int ret; - struct bt_config *cfg = NULL; - const char *leftover; - char *plugin_name = NULL, *comp_cls_name = NULL; - - *retcode = 0; - cfg = bt_config_help_create(initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { - goto error; - } - - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - help_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; - case OPT_HELP: - print_help_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - leftover = poptGetArg(pc); - if (leftover) { - plugin_comp_cls_names(leftover, NULL, - &plugin_name, &comp_cls_name, - &cfg->cmd_data.help.cfg_component->type); - if (plugin_name && comp_cls_name) { - /* Component class help */ - g_string_assign( - cfg->cmd_data.help.cfg_component->plugin_name, - plugin_name); - g_string_assign( - cfg->cmd_data.help.cfg_component->comp_cls_name, - comp_cls_name); - } else { - /* Fall back to plugin help */ - g_string_assign( - cfg->cmd_data.help.cfg_component->plugin_name, - leftover); - } - } else { - print_help_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } - - if (append_home_and_system_plugin_paths_cfg(cfg)) { - goto error; - } - - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - g_free(plugin_name); - g_free(comp_cls_name); - - if (pc) { - poptFreeContext(pc); - } - - free(arg); - return cfg; -} - -/* - * Prints the help command usage. - */ -static -void print_query_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace [GEN OPTS] query [OPTS] TYPE.PLUGIN.CLS OBJECT\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " -p, --params=PARAMS Set the query parameters to PARAMS\n"); - fprintf(fp, " (see the expected format of PARAMS below)\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, "\n\n"); - print_expected_params_format(fp); -} - -static -struct poptOption query_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, -}; - -/* - * Creates a Babeltrace config object from the arguments of a query - * command. - * - * *retcode is set to the appropriate exit code to use. - */ -static -struct bt_config *bt_config_query_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - poptContext pc = NULL; - char *arg = NULL; - int opt; - int ret; - struct bt_config *cfg = NULL; - const char *leftover; - bt_value *params; - - params = bt_value_null; - bt_value_get_ref(bt_value_null); - - *retcode = 0; - cfg = bt_config_query_create(initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { - goto error; - } - - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - query_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; - case OPT_PARAMS: - { - bt_value_put_ref(params); - params = bt_value_from_arg(arg); - if (!params) { - printf_err("Invalid format for --params option's argument:\n %s\n", - arg); - goto error; - } - break; - } - case OPT_HELP: - print_query_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - /* - * We need exactly two leftover arguments which are the - * mandatory component class specification and query object. - */ - leftover = poptGetArg(pc); - if (leftover) { - cfg->cmd_data.query.cfg_component = - bt_config_component_from_arg(leftover); - if (!cfg->cmd_data.query.cfg_component) { - printf_err("Invalid format for component class specification:\n %s\n", - leftover); - goto error; - } - - BT_ASSERT(params); - BT_OBJECT_MOVE_REF(cfg->cmd_data.query.cfg_component->params, - params); - } else { - print_query_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } - - leftover = poptGetArg(pc); - if (leftover) { - if (strlen(leftover) == 0) { - printf_err("Invalid empty object\n"); - goto error; - } - - g_string_assign(cfg->cmd_data.query.object, leftover); - } else { - print_query_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } - - leftover = poptGetArg(pc); - if (leftover) { - printf_err("Unexpected argument: %s\n", leftover); - goto error; - } - - if (append_home_and_system_plugin_paths_cfg(cfg)) { - goto error; - } - - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - if (pc) { - poptFreeContext(pc); - } - - bt_value_put_ref(params); - free(arg); - return cfg; -} - -/* - * Prints the list-plugins command usage. - */ -static -void print_list_plugins_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] list-plugins [OPTIONS]\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "See `babeltrace --help` for the list of general options.\n"); - fprintf(fp, "\n"); - fprintf(fp, "Use `babeltrace help` to get help for a specific plugin or component class.\n"); -} - -static -struct poptOption list_plugins_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, -}; - -/* - * Creates a Babeltrace config object from the arguments of a - * list-plugins command. - * - * *retcode is set to the appropriate exit code to use. - */ -static -struct bt_config *bt_config_list_plugins_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - poptContext pc = NULL; - char *arg = NULL; - int opt; - int ret; - struct bt_config *cfg = NULL; - const char *leftover; - - *retcode = 0; - cfg = bt_config_list_plugins_create(initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { - goto error; - } - - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - list_plugins_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; - case OPT_HELP: - print_list_plugins_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - leftover = poptGetArg(pc); - if (leftover) { - printf_err("Unexpected argument: %s\n", leftover); - goto error; - } - - if (append_home_and_system_plugin_paths_cfg(cfg)) { - goto error; - } - - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - if (pc) { - poptFreeContext(pc); - } - - free(arg); - return cfg; -} - -/* - * Prints the run command usage. - */ -static -void print_run_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] run [OPTIONS]\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n"); - fprintf(fp, " for all the following components until\n"); - fprintf(fp, " --reset-base-params is encountered\n"); - fprintf(fp, " (see the expected format of PARAMS below)\n"); - fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n"); - fprintf(fp, " Instantiate the component class CLS of type\n"); - fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n"); - fprintf(fp, " in the plugin PLUGIN, add it to the graph,\n"); - fprintf(fp, " and optionally name it NAME (you can also\n"); - fprintf(fp, " specify the name with --name)\n"); - fprintf(fp, " -x, --connect=CONNECTION Connect two created components (see the\n"); - fprintf(fp, " expected format of CONNECTION below)\n"); - fprintf(fp, " -n, --name=NAME Set the name of the current component\n"); - fprintf(fp, " to NAME (must be unique amongst all the\n"); - fprintf(fp, " names of the created components)\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n"); - fprintf(fp, " current component (see the expected format\n"); - fprintf(fp, " of PARAMS below)\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " -r, --reset-base-params Reset the current base parameters to an\n"); - fprintf(fp, " empty map\n"); - fprintf(fp, " --retry-duration=DUR When babeltrace(1) needs to retry to run\n"); - fprintf(fp, " the graph later, retry in DUR µs\n"); - fprintf(fp, " (default: 100000)\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "See `babeltrace --help` for the list of general options.\n"); - fprintf(fp, "\n\n"); - fprintf(fp, "Expected format of CONNECTION\n"); - fprintf(fp, "-----------------------------\n"); - fprintf(fp, "\n"); - fprintf(fp, " UPSTREAM[.UPSTREAM-PORT]:DOWNSTREAM[.DOWNSTREAM-PORT]\n"); - fprintf(fp, "\n"); - fprintf(fp, "UPSTREAM and DOWNSTREAM are names of the upstream and downstream\n"); - fprintf(fp, "components to connect together. You must escape the following characters\n\n"); - fprintf(fp, "with `\\`: `\\`, `.`, and `:`. You can set the name of the current\n"); - fprintf(fp, "component with the --name option.\n"); - fprintf(fp, "\n"); - fprintf(fp, "UPSTREAM-PORT and DOWNSTREAM-PORT are optional globbing patterns to\n"); - fprintf(fp, "identify the upstream and downstream ports to use for the connection.\n"); - fprintf(fp, "When the port is not specified, `*` is used.\n"); - fprintf(fp, "\n"); - fprintf(fp, "When a component named UPSTREAM has an available port which matches the\n"); - fprintf(fp, "UPSTREAM-PORT globbing pattern, it is connected to the first port which\n"); - fprintf(fp, "matches the DOWNSTREAM-PORT globbing pattern of the component named\n"); - fprintf(fp, "DOWNSTREAM.\n"); - fprintf(fp, "\n"); - fprintf(fp, "The only special character in UPSTREAM-PORT and DOWNSTREAM-PORT is `*`\n"); - fprintf(fp, "which matches anything. You must escape the following characters\n"); - fprintf(fp, "with `\\`: `\\`, `*`, `?`, `[`, `.`, and `:`.\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, "Examples:\n"); - fprintf(fp, "\n"); - fprintf(fp, " my-src:my-sink\n"); - fprintf(fp, " ctf-fs.*stream*:utils-muxer:*\n"); - fprintf(fp, "\n"); - fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n"); - fprintf(fp, "babeltrace from a shell.\n"); - fprintf(fp, "\n\n"); - print_expected_params_format(fp); -} - -/* - * Creates a Babeltrace config object from the arguments of a run - * command. - * - * *retcode is set to the appropriate exit code to use. - */ -static -struct bt_config *bt_config_run_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - poptContext pc = NULL; - char *arg = NULL; - struct bt_config_component *cur_cfg_comp = NULL; - enum bt_config_component_dest cur_cfg_comp_dest = - BT_CONFIG_COMPONENT_DEST_UNKNOWN; - bt_value *cur_base_params = NULL; - int opt, ret = 0; - struct bt_config *cfg = NULL; - bt_value *instance_names = NULL; - bt_value *connection_args = NULL; - char error_buf[256] = { 0 }; - long retry_duration = -1; - bt_value_status status; - struct poptOption run_long_options[] = { - { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL }, - { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, NULL, NULL }, - { "connect", 'x', POPT_ARG_STRING, NULL, OPT_CONNECT, NULL, NULL }, - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "name", 'n', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, NULL, NULL }, - { "retry-duration", '\0', POPT_ARG_LONG, &retry_duration, OPT_RETRY_DURATION, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, - }; - - *retcode = 0; - - if (argc <= 1) { - print_run_usage(stdout); - *retcode = -1; - goto end; - } - - cfg = bt_config_run_create(initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->cmd_data.run.retry_duration_us = 100000; - cfg->omit_system_plugin_path = force_omit_system_plugin_path; - cfg->omit_home_plugin_path = force_omit_home_plugin_path; - cur_base_params = bt_value_map_create(); - if (!cur_base_params) { - print_err_oom(); - goto error; - } - - instance_names = bt_value_map_create(); - if (!instance_names) { - print_err_oom(); - goto error; - } - - connection_args = bt_value_array_create(); - if (!connection_args) { - print_err_oom(); - goto error; - } - - ret = append_env_var_plugin_paths(cfg->plugin_paths); - if (ret) { - goto error; - } - - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, - run_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - cfg->plugin_paths, arg)) { - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; - break; - case OPT_COMPONENT: - { - enum bt_config_component_dest new_dest; - - if (cur_cfg_comp) { - ret = add_run_cfg_comp_check_name(cfg, - cur_cfg_comp, cur_cfg_comp_dest, - instance_names); - BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); - if (ret) { - goto error; - } - } - - cur_cfg_comp = bt_config_component_from_arg(arg); - if (!cur_cfg_comp) { - printf_err("Invalid format for --component option's argument:\n %s\n", - arg); - goto error; - } - - switch (cur_cfg_comp->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - new_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - new_dest = BT_CONFIG_COMPONENT_DEST_FILTER; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - new_dest = BT_CONFIG_COMPONENT_DEST_SINK; - break; - default: - abort(); - } - - BT_ASSERT(cur_base_params); - bt_value_put_ref(cur_cfg_comp->params); - status = bt_value_copy(cur_base_params, - &cur_cfg_comp->params); - if (status != BT_VALUE_STATUS_OK) { - print_err_oom(); - goto error; - } - - cur_cfg_comp_dest = new_dest; - break; - } - case OPT_PARAMS: - { - bt_value *params; - bt_value *params_to_set; - - if (!cur_cfg_comp) { - printf_err("Cannot add parameters to unavailable component:\n %s\n", - arg); - goto error; - } - - params = bt_value_from_arg(arg); - if (!params) { - printf_err("Invalid format for --params option's argument:\n %s\n", - arg); - goto error; - } - - status = bt_value_map_extend(cur_cfg_comp->params, - params, ¶ms_to_set); - BT_VALUE_PUT_REF_AND_RESET(params); - if (status != BT_VALUE_STATUS_OK) { - printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n", - arg); - goto error; - } - - BT_OBJECT_MOVE_REF(cur_cfg_comp->params, params_to_set); - break; - } - case OPT_NAME: - if (!cur_cfg_comp) { - printf_err("Cannot set the name of unavailable component:\n %s\n", - arg); - goto error; - } - - g_string_assign(cur_cfg_comp->instance_name, arg); - break; - case OPT_BASE_PARAMS: - { - bt_value *params = - bt_value_from_arg(arg); - - if (!params) { - printf_err("Invalid format for --base-params option's argument:\n %s\n", - arg); - goto error; - } - - BT_OBJECT_MOVE_REF(cur_base_params, params); - break; - } - case OPT_RESET_BASE_PARAMS: - BT_VALUE_PUT_REF_AND_RESET(cur_base_params); - cur_base_params = bt_value_map_create(); - if (!cur_base_params) { - print_err_oom(); - goto error; - } - break; - case OPT_CONNECT: - if (bt_value_array_append_string_element( - connection_args, arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_RETRY_DURATION: - if (retry_duration < 0) { - printf_err("--retry-duration option's argument must be positive or 0: %ld\n", - retry_duration); - goto error; - } - - cfg->cmd_data.run.retry_duration_us = - (uint64_t) retry_duration; - break; - case OPT_HELP: - print_run_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - /* This command does not accept leftover arguments */ - if (poptPeekArg(pc)) { - printf_err("Unexpected argument: %s\n", poptPeekArg(pc)); - goto error; - } - - /* Add current component */ - if (cur_cfg_comp) { - ret = add_run_cfg_comp_check_name(cfg, cur_cfg_comp, - cur_cfg_comp_dest, instance_names); - BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); - if (ret) { - goto error; - } - } - - if (cfg->cmd_data.run.sources->len == 0) { - printf_err("Incomplete graph: no source component\n"); - goto error; - } - - if (cfg->cmd_data.run.sinks->len == 0) { - printf_err("Incomplete graph: no sink component\n"); - goto error; - } - - if (append_home_and_system_plugin_paths_cfg(cfg)) { - goto error; - } - - ret = bt_config_cli_args_create_connections(cfg, - connection_args, - error_buf, 256); - if (ret) { - printf_err("Cannot creation connections:\n%s", error_buf); - goto error; - } - - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - if (pc) { - poptFreeContext(pc); - } - - free(arg); - BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); - BT_VALUE_PUT_REF_AND_RESET(cur_base_params); - BT_VALUE_PUT_REF_AND_RESET(instance_names); - BT_VALUE_PUT_REF_AND_RESET(connection_args); - return cfg; -} - -static -struct bt_config *bt_config_run_from_args_array(const bt_value *run_args, - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - struct bt_config *cfg = NULL; - const char **argv; - int64_t i, len; - const size_t argc = bt_value_array_get_size(run_args) + 1; - - argv = calloc(argc, sizeof(*argv)); - if (!argv) { - print_err_oom(); - goto end; - } - - argv[0] = "run"; - - len = bt_value_array_get_size(run_args); - if (len < 0) { - printf_err("Invalid executable arguments\n"); - goto end; - } - for (i = 0; i < len; i++) { - const bt_value *arg_value = - bt_value_array_borrow_element_by_index_const(run_args, - i); - const char *arg; - - BT_ASSERT(arg_value); - arg = bt_value_string_get(arg_value); - BT_ASSERT(arg); - argv[i + 1] = arg; - } - - cfg = bt_config_run_from_args(argc, argv, retcode, - force_omit_system_plugin_path, force_omit_home_plugin_path, - initial_plugin_paths); - -end: - free(argv); - return cfg; -} - -/* - * Prints the convert command usage. - */ -static -void print_convert_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] [convert] [OPTIONS] [PATH/URL]\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n"); - fprintf(fp, " Instantiate the component class CLS of type\n"); - fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n"); - fprintf(fp, " in the plugin PLUGIN, add it to the\n"); - fprintf(fp, " conversion graph, and optionally name it\n"); - fprintf(fp, " NAME (you can also specify the name with\n"); - fprintf(fp, " --name)\n"); - fprintf(fp, " --name=NAME Set the name of the current component\n"); - fprintf(fp, " to NAME (must be unique amongst all the\n"); - fprintf(fp, " names of the created components)\n"); - fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); - fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n"); - fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); - fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n"); - fprintf(fp, " current component (see the expected format\n"); - fprintf(fp, " of PARAMS below)\n"); - fprintf(fp, " -P, --path=PATH Set the `path` string parameter of the\n"); - fprintf(fp, " current component to PATH\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); - fprintf(fp, " --retry-duration=DUR When babeltrace(1) needs to retry to run\n"); - fprintf(fp, " the graph later, retry in DUR µs\n"); - fprintf(fp, " (default: 100000)\n"); - fprintf(fp, " dynamic plugins can be loaded\n"); - fprintf(fp, " --run-args Print the equivalent arguments for the\n"); - fprintf(fp, " `run` command to the standard output,\n"); - fprintf(fp, " formatted for a shell, and quit\n"); - fprintf(fp, " --run-args-0 Print the equivalent arguments for the\n"); - fprintf(fp, " `run` command to the standard output,\n"); - fprintf(fp, " formatted for `xargs -0`, and quit\n"); - fprintf(fp, " --stream-intersection Only process events when all streams\n"); - fprintf(fp, " are active\n"); - fprintf(fp, " -u, --url=URL Set the `url` string parameter of the\n"); - fprintf(fp, " current component to URL\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "Implicit `source.ctf.fs` component options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n"); - fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS ns\n"); - fprintf(fp, "\n"); - fprintf(fp, "Implicit `sink.text.pretty` component options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n"); - fprintf(fp, " --clock-date Print timestamp dates\n"); - fprintf(fp, " --clock-gmt Print and parse timestamps in the GMT\n"); - fprintf(fp, " time zone instead of the local time zone\n"); - fprintf(fp, " --clock-seconds Print the timestamps as `SEC.NS` instead\n"); - fprintf(fp, " of `hh:mm:ss.nnnnnnnnn`\n"); - fprintf(fp, " --color=(never | auto | always)\n"); - fprintf(fp, " Never, automatically, or always emit\n"); - fprintf(fp, " console color codes\n"); - fprintf(fp, " -f, --fields=FIELD[,FIELD]... Print additional fields; FIELD can be:\n"); - fprintf(fp, " `all`, `trace`, `trace:hostname`,\n"); - fprintf(fp, " `trace:domain`, `trace:procname`,\n"); - fprintf(fp, " `trace:vpid`, `loglevel`, `emf`\n"); - fprintf(fp, " -n, --names=NAME[,NAME]... Print field names; NAME can be:\n"); - fprintf(fp, " `payload` (or `arg` or `args`), `none`,\n"); - fprintf(fp, " `all`, `scope`, `header`, `context`\n"); - fprintf(fp, " (or `ctx`)\n"); - fprintf(fp, " --no-delta Do not print time delta between\n"); - fprintf(fp, " consecutive events\n"); - fprintf(fp, " -w, --output=PATH Write output text to PATH instead of\n"); - fprintf(fp, " the standard output\n"); - fprintf(fp, "\n"); - fprintf(fp, "Implicit `filter.utils.muxer` component options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --clock-force-correlate Assume that clocks are inherently\n"); - fprintf(fp, " correlated across traces\n"); - fprintf(fp, "\n"); - fprintf(fp, "Implicit `filter.utils.trimmer` component options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -b, --begin=BEGIN Set the beginning time of the conversion\n"); - fprintf(fp, " time range to BEGIN (see the format of\n"); - fprintf(fp, " BEGIN below)\n"); - fprintf(fp, " -e, --end=END Set the end time of the conversion time\n"); - fprintf(fp, " range to END (see the format of END below)\n"); - fprintf(fp, " -t, --timerange=TIMERANGE Set conversion time range to TIMERANGE:\n"); - fprintf(fp, " BEGIN,END or [BEGIN,END] (literally `[` and\n"); - fprintf(fp, " `]`) (see the format of BEGIN/END below)\n"); - fprintf(fp, "\n"); - fprintf(fp, "Implicit `filter.lttng-utils.debug-info` component options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --debug-info Create an implicit\n"); - fprintf(fp, " `filter.lttng-utils.debug-info` component\n"); - fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n"); - fprintf(fp, " instead of `/usr/lib/debug`\n"); - fprintf(fp, " --debug-info-full-path Show full debug info source and\n"); - fprintf(fp, " binary paths instead of just names\n"); - fprintf(fp, " --debug-info-target-prefix=DIR\n"); - fprintf(fp, " Use directory DIR as a prefix when\n"); - fprintf(fp, " looking up executables during debug\n"); - fprintf(fp, " info analysis\n"); - fprintf(fp, "\n"); - fprintf(fp, "Legacy options that still work:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -i, --input-format=(ctf | lttng-live)\n"); - fprintf(fp, " `ctf`:\n"); - fprintf(fp, " Create an implicit `source.ctf.fs`\n"); - fprintf(fp, " component\n"); - fprintf(fp, " `lttng-live`:\n"); - fprintf(fp, " Create an implicit `source.ctf.lttng-live`\n"); - fprintf(fp, " component\n"); - fprintf(fp, " -o, --output-format=(text | ctf | dummy | ctf-metadata)\n"); - fprintf(fp, " `text`:\n"); - fprintf(fp, " Create an implicit `sink.text.pretty`\n"); - fprintf(fp, " component\n"); - fprintf(fp, " `ctf`:\n"); - fprintf(fp, " Create an implicit `sink.ctf.fs`\n"); - fprintf(fp, " component\n"); - fprintf(fp, " `dummy`:\n"); - fprintf(fp, " Create an implicit `sink.utils.dummy`\n"); - fprintf(fp, " component\n"); - fprintf(fp, " `ctf-metadata`:\n"); - fprintf(fp, " Query the `source.ctf.fs` component class\n"); - fprintf(fp, " for metadata text and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "See `babeltrace --help` for the list of general options.\n"); - fprintf(fp, "\n\n"); - fprintf(fp, "Format of BEGIN and END\n"); - fprintf(fp, "-----------------------\n"); - fprintf(fp, "\n"); - fprintf(fp, " [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n"); - fprintf(fp, "\n\n"); - print_expected_params_format(fp); -} - -static -struct poptOption convert_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "begin", 'b', POPT_ARG_STRING, NULL, OPT_BEGIN, NULL, NULL }, - { "clock-cycles", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL }, - { "clock-date", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL }, - { "clock-force-correlate", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL }, - { "clock-gmt", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL }, - { "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 }, - { "color", '\0', POPT_ARG_STRING, NULL, OPT_COLOR, NULL, NULL }, - { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, 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 }, - { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL }, - { "end", 'e', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL }, - { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL }, - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL }, - { "name", '\0', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL }, - { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL }, - { "debug-info", '\0', POPT_ARG_NONE, NULL, OPT_DEBUG_INFO, NULL, NULL }, - { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT, NULL, NULL }, - { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL }, - { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, - { "path", 'P', POPT_ARG_STRING, NULL, OPT_PATH, NULL, NULL }, - { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, - { "retry-duration", '\0', POPT_ARG_STRING, NULL, OPT_RETRY_DURATION, NULL, NULL }, - { "run-args", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS, NULL, NULL }, - { "run-args-0", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS_0, NULL, NULL }, - { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL }, - { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL }, - { "url", 'u', POPT_ARG_STRING, NULL, OPT_URL, NULL, NULL }, - { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL }, - { NULL, 0, '\0', NULL, 0, NULL, NULL }, -}; - -static -GString *get_component_auto_name(const char *prefix, - const bt_value *existing_names) -{ - unsigned int i = 0; - GString *auto_name = g_string_new(NULL); - - if (!auto_name) { - print_err_oom(); - goto end; - } - - if (!bt_value_map_has_entry(existing_names, prefix)) { - g_string_assign(auto_name, prefix); - goto end; - } - - do { - g_string_printf(auto_name, "%s-%d", prefix, i); - i++; - } while (bt_value_map_has_entry(existing_names, auto_name->str)); - -end: - return auto_name; -} - -struct implicit_component_args { - bool exists; - GString *comp_arg; - GString *name_arg; - GString *params_arg; - bt_value *extra_params; -}; - -static -int assign_name_to_implicit_component(struct implicit_component_args *args, - const char *prefix, bt_value *existing_names, - GList **comp_names, bool append_to_comp_names) -{ - int ret = 0; - GString *name = NULL; - - if (!args->exists) { - goto end; - } - - name = get_component_auto_name(prefix, - existing_names); - - if (!name) { - ret = -1; - goto end; - } - - g_string_assign(args->name_arg, name->str); - - if (bt_value_map_insert_entry(existing_names, name->str, - bt_value_null)) { - print_err_oom(); - ret = -1; - goto end; - } - - if (append_to_comp_names) { - *comp_names = g_list_append(*comp_names, name); - name = NULL; - } - -end: - if (name) { - g_string_free(name, TRUE); - } - - return ret; -} - -static -int append_run_args_for_implicit_component( - struct implicit_component_args *impl_args, - bt_value *run_args) -{ - int ret = 0; - size_t i; - - if (!impl_args->exists) { - goto end; - } - - if (bt_value_array_append_string_element(run_args, "--component")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, impl_args->comp_arg->str)) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, "--name")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, impl_args->name_arg->str)) { - print_err_oom(); - goto error; - } - - if (impl_args->params_arg->len > 0) { - if (bt_value_array_append_string_element(run_args, "--params")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, - impl_args->params_arg->str)) { - print_err_oom(); - goto error; - } - } - - for (i = 0; i < bt_value_array_get_size(impl_args->extra_params); - i++) { - const bt_value *elem; - const char *arg; - - elem = bt_value_array_borrow_element_by_index(impl_args->extra_params, - i); - if (!elem) { - goto error; - } - - BT_ASSERT(bt_value_is_string(elem)); - arg = bt_value_string_get(elem); - ret = bt_value_array_append_string_element(run_args, arg); - if (ret) { - print_err_oom(); - goto error; - } - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -void finalize_implicit_component_args(struct implicit_component_args *args) -{ - BT_ASSERT(args); - - if (args->comp_arg) { - g_string_free(args->comp_arg, TRUE); - } - - if (args->name_arg) { - g_string_free(args->name_arg, TRUE); - } - - if (args->params_arg) { - g_string_free(args->params_arg, TRUE); - } - - bt_value_put_ref(args->extra_params); -} - -static -int init_implicit_component_args(struct implicit_component_args *args, - const char *comp_arg, bool exists) -{ - int ret = 0; - - args->exists = exists; - args->comp_arg = g_string_new(comp_arg); - args->name_arg = g_string_new(NULL); - args->params_arg = g_string_new(NULL); - args->extra_params = bt_value_array_create(); - - if (!args->comp_arg || !args->name_arg || - !args->params_arg || !args->extra_params) { - ret = -1; - finalize_implicit_component_args(args); - print_err_oom(); - goto end; - } - -end: - return ret; -} - -static -void append_implicit_component_param(struct implicit_component_args *args, - const char *key, const char *value) -{ - BT_ASSERT(args); - BT_ASSERT(key); - BT_ASSERT(value); - append_param_arg(args->params_arg, key, value); -} - -/* Escape value to make it suitable to use as a string parameter value. */ -static -gchar *escape_string_value(const char *value) -{ - GString *ret; - const char *in; - - ret = g_string_new(NULL); - if (!ret) { - print_err_oom(); - goto end; - } - - in = value; - while (*in) { - switch (*in) { - case '"': - case '\\': - g_string_append_c(ret, '\\'); - break; - } - - g_string_append_c(ret, *in); - - in++; - } - -end: - return g_string_free(ret, FALSE); -} - -static -int bt_value_to_cli_param_value_append(const bt_value *value, GString *buf) -{ - BT_ASSERT(buf); - - int ret = -1; - - switch (bt_value_get_type(value)) { - case BT_VALUE_TYPE_STRING: - { - const char *str_value = bt_value_string_get(value); - gchar *escaped_str_value; - - escaped_str_value = escape_string_value(str_value); - if (!escaped_str_value) { - goto end; - } - - g_string_append_printf(buf, "\"%s\"", escaped_str_value); - - g_free(escaped_str_value); - break; - } - case BT_VALUE_TYPE_ARRAY: { - g_string_append_c(buf, '['); - uint64_t sz = bt_value_array_get_size(value); - for (uint64_t i = 0; i < sz; i++) { - const bt_value *item; - int ret; - - if (i > 0) { - g_string_append(buf, ", "); - } - - item = bt_value_array_borrow_element_by_index_const( - value, i); - ret = bt_value_to_cli_param_value_append(item, buf); - - if (ret) { - goto end; - } - } - g_string_append_c(buf, ']'); - break; - } - default: - abort(); - } - - ret = 0; - -end: - return ret; -} - -/* - * Convert `value` to its equivalent representation as a command line parameter - * value. - */ - -static -gchar *bt_value_to_cli_param_value(bt_value *value) -{ - GString *buf; - gchar *result = NULL; - - buf = g_string_new(NULL); - if (!buf) { - print_err_oom(); - goto error; - } - - if (bt_value_to_cli_param_value_append(value, buf)) { - goto error; - } - - result = g_string_free(buf, FALSE); - buf = NULL; - - goto end; - -error: - if (buf) { - g_string_free(buf, TRUE); - } - -end: - return result; -} - -static -int append_parameter_to_args(bt_value *args, const char *key, bt_value *value) -{ - BT_ASSERT(args); - BT_ASSERT(bt_value_get_type(args) == BT_VALUE_TYPE_ARRAY); - BT_ASSERT(key); - BT_ASSERT(value); - - int ret = 0; - gchar *str_value = NULL; - GString *parameter = NULL; - - if (bt_value_array_append_string_element(args, "--params")) { - print_err_oom(); - ret = -1; - goto end; - } - - str_value = bt_value_to_cli_param_value(value); - if (!str_value) { - ret = -1; - goto end; - } - - parameter = g_string_new(NULL); - if (!parameter) { - print_err_oom(); - ret = -1; - goto end; - } - - g_string_printf(parameter, "%s=%s", key, str_value); - - if (bt_value_array_append_string_element(args, parameter->str)) { - print_err_oom(); - ret = -1; - goto end; - } - -end: - if (parameter) { - g_string_free(parameter, TRUE); - parameter = NULL; - } - - if (str_value) { - g_free(str_value); - str_value = NULL; - } - - return ret; -} - -static -int append_string_parameter_to_args(bt_value *args, const char *key, const char *value) -{ - bt_value *str_value; - int ret; - - str_value = bt_value_string_create_init(value); - - if (!str_value) { - print_err_oom(); - ret = -1; - goto end; - } - - ret = append_parameter_to_args(args, key, str_value); - -end: - BT_VALUE_PUT_REF_AND_RESET(str_value); - return ret; -} - -static -int append_implicit_component_extra_param(struct implicit_component_args *args, - const char *key, const char *value) -{ - return append_string_parameter_to_args(args->extra_params, key, value); -} - -static -int convert_append_name_param(enum bt_config_component_dest dest, - GString *cur_name, GString *cur_name_prefix, - bt_value *run_args, - bt_value *all_names, - GList **source_names, GList **filter_names, - GList **sink_names) -{ - int ret = 0; - - if (cur_name_prefix->len > 0) { - /* We're after a --component option */ - GString *name = NULL; - bool append_name_opt = false; - - if (cur_name->len == 0) { - /* - * No explicit name was provided for the user - * component. - */ - name = get_component_auto_name(cur_name_prefix->str, - all_names); - append_name_opt = true; - } else { - /* - * An explicit name was provided for the user - * component. - */ - if (bt_value_map_has_entry(all_names, - cur_name->str)) { - printf_err("Duplicate component instance name:\n %s\n", - cur_name->str); - goto error; - } - - name = g_string_new(cur_name->str); - } - - if (!name) { - print_err_oom(); - goto error; - } - - /* - * Remember this name globally, for the uniqueness of - * all component names. - */ - if (bt_value_map_insert_entry(all_names, name->str, bt_value_null)) { - print_err_oom(); - goto error; - } - - /* - * Append the --name option if necessary. - */ - if (append_name_opt) { - if (bt_value_array_append_string_element(run_args, "--name")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, name->str)) { - print_err_oom(); - goto error; - } - } - - /* - * Remember this name specifically for the type of the - * component. This is to create connection arguments. - */ - switch (dest) { - case BT_CONFIG_COMPONENT_DEST_SOURCE: - *source_names = g_list_append(*source_names, name); - break; - case BT_CONFIG_COMPONENT_DEST_FILTER: - *filter_names = g_list_append(*filter_names, name); - break; - case BT_CONFIG_COMPONENT_DEST_SINK: - *sink_names = g_list_append(*sink_names, name); - break; - default: - abort(); - } - - g_string_assign(cur_name_prefix, ""); - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -/* - * Escapes `.`, `:`, and `\` of `input` with `\`. - */ -static -GString *escape_dot_colon(const char *input) -{ - GString *output = g_string_new(NULL); - const char *ch; - - if (!output) { - print_err_oom(); - goto end; - } - - for (ch = input; *ch != '\0'; ch++) { - if (*ch == '\\' || *ch == '.' || *ch == ':') { - g_string_append_c(output, '\\'); - } - - g_string_append_c(output, *ch); - } - -end: - return output; -} - -/* - * Appends a --connect option to a list of arguments. `upstream_name` - * and `downstream_name` are escaped with escape_dot_colon() in this - * function. - */ -static -int append_connect_arg(bt_value *run_args, - const char *upstream_name, const char *downstream_name) -{ - int ret = 0; - GString *e_upstream_name = escape_dot_colon(upstream_name); - GString *e_downstream_name = escape_dot_colon(downstream_name); - GString *arg = g_string_new(NULL); - - if (!e_upstream_name || !e_downstream_name || !arg) { - print_err_oom(); - ret = -1; - goto end; - } - - ret = bt_value_array_append_string_element(run_args, "--connect"); - if (ret) { - print_err_oom(); - ret = -1; - goto end; - } - - g_string_append(arg, e_upstream_name->str); - g_string_append_c(arg, ':'); - g_string_append(arg, e_downstream_name->str); - ret = bt_value_array_append_string_element(run_args, arg->str); - if (ret) { - print_err_oom(); - ret = -1; - goto end; - } - -end: - if (arg) { - g_string_free(arg, TRUE); - } - - if (e_upstream_name) { - g_string_free(e_upstream_name, TRUE); - } - - if (e_downstream_name) { - g_string_free(e_downstream_name, TRUE); - } - - return ret; -} - -/* - * Appends the run command's --connect options for the convert command. - */ -static -int convert_auto_connect(bt_value *run_args, - GList *source_names, GList *filter_names, - GList *sink_names) -{ - int ret = 0; - GList *source_at = source_names; - GList *filter_at = filter_names; - GList *filter_prev; - GList *sink_at = sink_names; - - BT_ASSERT(source_names); - BT_ASSERT(filter_names); - BT_ASSERT(sink_names); - - /* Connect all sources to the first filter */ - for (source_at = source_names; source_at != NULL; source_at = g_list_next(source_at)) { - GString *source_name = source_at->data; - GString *filter_name = filter_at->data; - - ret = append_connect_arg(run_args, source_name->str, - filter_name->str); - if (ret) { - goto error; - } - } - - filter_prev = filter_at; - filter_at = g_list_next(filter_at); - - /* Connect remaining filters */ - for (; filter_at != NULL; filter_prev = filter_at, filter_at = g_list_next(filter_at)) { - GString *filter_name = filter_at->data; - GString *filter_prev_name = filter_prev->data; - - ret = append_connect_arg(run_args, filter_prev_name->str, - filter_name->str); - if (ret) { - goto error; - } - } - - /* Connect last filter to all sinks */ - for (sink_at = sink_names; sink_at != NULL; sink_at = g_list_next(sink_at)) { - GString *filter_name = filter_prev->data; - GString *sink_name = sink_at->data; - - ret = append_connect_arg(run_args, filter_name->str, - sink_name->str); - if (ret) { - goto error; - } - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -int split_timerange(const char *arg, char **begin, char **end) -{ - int ret = 0; - const char *ch = arg; - size_t end_pos; - GString *g_begin = NULL; - GString *g_end = NULL; - - BT_ASSERT(arg); - - if (*ch == '[') { - ch++; - } - - g_begin = bt_common_string_until(ch, "", ",", &end_pos); - if (!g_begin || ch[end_pos] != ',' || g_begin->len == 0) { - goto error; - } - - ch += end_pos + 1; - - g_end = bt_common_string_until(ch, "", "]", &end_pos); - if (!g_end || g_end->len == 0) { - goto error; - } - - BT_ASSERT(begin); - BT_ASSERT(end); - *begin = g_begin->str; - *end = g_end->str; - g_string_free(g_begin, FALSE); - g_string_free(g_end, FALSE); - g_begin = NULL; - g_end = NULL; - goto end; - -error: - ret = -1; - -end: - if (g_begin) { - g_string_free(g_begin, TRUE); - } - - if (g_end) { - g_string_free(g_end, TRUE); - } - - return ret; -} - -static -int g_list_prepend_gstring(GList **list, const char *string) -{ - int ret = 0; - GString *gs = g_string_new(string); - - BT_ASSERT(list); - - if (!gs) { - print_err_oom(); - goto end; - } - - *list = g_list_prepend(*list, gs); - -end: - return ret; -} - -/* - * Creates a Babeltrace config object from the arguments of a convert - * command. - * - * *retcode is set to the appropriate exit code to use. - */ -static -struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths, char *log_level) -{ - poptContext pc = NULL; - char *arg = NULL; - enum bt_config_component_dest cur_comp_dest = - BT_CONFIG_COMPONENT_DEST_UNKNOWN; - int opt, ret = 0; - struct bt_config *cfg = NULL; - bool got_input_format_opt = false; - bool got_output_format_opt = false; - bool trimmer_has_begin = false; - bool trimmer_has_end = false; - bool stream_intersection_mode = false; - GString *cur_name = NULL; - GString *cur_name_prefix = NULL; - const char *leftover = NULL; - bool print_run_args = false; - bool print_run_args_0 = false; - bool print_ctf_metadata = false; - bt_value *run_args = NULL; - bt_value *all_names = NULL; - GList *source_names = NULL; - GList *filter_names = NULL; - GList *sink_names = NULL; - bt_value *leftovers = NULL; - struct implicit_component_args implicit_ctf_input_args = { 0 }; - struct implicit_component_args implicit_ctf_output_args = { 0 }; - struct implicit_component_args implicit_lttng_live_args = { 0 }; - struct implicit_component_args implicit_dummy_args = { 0 }; - struct implicit_component_args implicit_text_args = { 0 }; - struct implicit_component_args implicit_debug_info_args = { 0 }; - struct implicit_component_args implicit_muxer_args = { 0 }; - struct implicit_component_args implicit_trimmer_args = { 0 }; - bt_value *plugin_paths; - char error_buf[256] = { 0 }; - size_t i; - struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 }; - char *output = NULL; - - (void) bt_value_copy(initial_plugin_paths, &plugin_paths); - - *retcode = 0; - - if (argc <= 1) { - print_convert_usage(stdout); - *retcode = -1; - goto end; - } - - if (init_implicit_component_args(&implicit_ctf_input_args, - "source.ctf.fs", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_ctf_output_args, - "sink.ctf.fs", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_lttng_live_args, - "source.ctf.lttng-live", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_text_args, - "sink.text.pretty", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_dummy_args, - "sink.utils.dummy", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_debug_info_args, - "filter.lttng-utils.debug-info", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_muxer_args, - "filter.utils.muxer", true)) { - goto error; - } - - if (init_implicit_component_args(&implicit_trimmer_args, - "filter.utils.trimmer", false)) { - goto error; - } - - all_names = bt_value_map_create(); - if (!all_names) { - print_err_oom(); - goto error; - } - - run_args = bt_value_array_create(); - if (!run_args) { - print_err_oom(); - goto error; - } - - cur_name = g_string_new(NULL); - if (!cur_name) { - print_err_oom(); - goto error; - } - - cur_name_prefix = g_string_new(NULL); - if (!cur_name_prefix) { - print_err_oom(); - goto error; - } - - ret = append_env_var_plugin_paths(plugin_paths); - if (ret) { - goto error; - } - - leftovers = bt_value_array_create(); - if (!leftovers) { - print_err_oom(); - goto error; - } - - /* - * First pass: collect all arguments which need to be passed - * as is to the run command. This pass can also add --name - * arguments if needed to automatically name unnamed component - * instances. Also it does the following transformations: - * - * --path=PATH -> --params=path="PATH" - * --url=URL -> --params=url="URL" - * - * Also it appends the plugin paths of --plugin-path to - * `plugin_paths`. - */ - pc = poptGetContext(NULL, argc, (const char **) argv, - convert_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - char *name = NULL; - char *plugin_name = NULL; - char *comp_cls_name = NULL; - - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_COMPONENT: - { - bt_component_class_type type; - const char *type_prefix; - - /* Append current component's name if needed */ - ret = convert_append_name_param(cur_comp_dest, cur_name, - cur_name_prefix, run_args, all_names, - &source_names, &filter_names, &sink_names); - if (ret) { - goto error; - } - - /* Parse the argument */ - plugin_comp_cls_names(arg, &name, &plugin_name, - &comp_cls_name, &type); - if (!plugin_name || !comp_cls_name) { - printf_err("Invalid format for --component option's argument:\n %s\n", - arg); - goto error; - } - - if (name) { - g_string_assign(cur_name, name); - } else { - g_string_assign(cur_name, ""); - } - - switch (type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; - type_prefix = "source"; - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - cur_comp_dest = BT_CONFIG_COMPONENT_DEST_FILTER; - type_prefix = "filter"; - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK; - type_prefix = "sink"; - break; - default: - abort(); - } - - if (bt_value_array_append_string_element(run_args, - "--component")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - - g_string_assign(cur_name_prefix, ""); - g_string_append_printf(cur_name_prefix, "%s.%s.%s", - type_prefix, plugin_name, comp_cls_name); - free(name); - free(plugin_name); - free(comp_cls_name); - name = NULL; - plugin_name = NULL; - comp_cls_name = NULL; - break; - } - case OPT_PARAMS: - if (cur_name_prefix->len == 0) { - printf_err("No current component of which to set parameters:\n %s\n", - arg); - goto error; - } - - if (bt_value_array_append_string_element(run_args, - "--params")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_PATH: - if (cur_name_prefix->len == 0) { - printf_err("No current component of which to set `path` parameter:\n %s\n", - arg); - goto error; - } - - if (append_string_parameter_to_args(run_args, "path", arg)) { - goto error; - } - break; - case OPT_URL: - if (cur_name_prefix->len == 0) { - printf_err("No current component of which to set `url` parameter:\n %s\n", - arg); - goto error; - } - - - if (append_string_parameter_to_args(run_args, "url", arg)) { - goto error; - } - break; - case OPT_NAME: - if (cur_name_prefix->len == 0) { - printf_err("No current component to name:\n %s\n", - arg); - goto error; - } - - if (bt_value_array_append_string_element(run_args, "--name")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - - g_string_assign(cur_name, arg); - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - force_omit_home_plugin_path = true; - - if (bt_value_array_append_string_element(run_args, - "--omit-home-plugin-path")) { - print_err_oom(); - goto error; - } - break; - case OPT_RETRY_DURATION: - if (bt_value_array_append_string_element(run_args, - "--retry-duration")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - force_omit_system_plugin_path = true; - - if (bt_value_array_append_string_element(run_args, - "--omit-system-plugin-path")) { - print_err_oom(); - goto error; - } - break; - case OPT_PLUGIN_PATH: - if (bt_config_append_plugin_paths_check_setuid_setgid( - plugin_paths, arg)) { - goto error; - } - - if (bt_value_array_append_string_element(run_args, - "--plugin-path")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string_element(run_args, arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_HELP: - print_convert_usage(stdout); - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - case OPT_BEGIN: - case OPT_CLOCK_CYCLES: - case OPT_CLOCK_DATE: - case OPT_CLOCK_FORCE_CORRELATE: - case OPT_CLOCK_GMT: - case OPT_CLOCK_OFFSET: - case OPT_CLOCK_OFFSET_NS: - case OPT_CLOCK_SECONDS: - case OPT_COLOR: - case OPT_DEBUG: - case OPT_DEBUG_INFO: - case OPT_DEBUG_INFO_DIR: - case OPT_DEBUG_INFO_FULL_PATH: - case OPT_DEBUG_INFO_TARGET_PREFIX: - case OPT_END: - case OPT_FIELDS: - case OPT_INPUT_FORMAT: - case OPT_NAMES: - case OPT_NO_DELTA: - case OPT_OUTPUT_FORMAT: - case OPT_OUTPUT: - case OPT_RUN_ARGS: - case OPT_RUN_ARGS_0: - case OPT_STREAM_INTERSECTION: - case OPT_TIMERANGE: - case OPT_VERBOSE: - /* Ignore in this pass */ - break; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - /* Append current component's name if needed */ - ret = convert_append_name_param(cur_comp_dest, cur_name, - cur_name_prefix, run_args, all_names, &source_names, - &filter_names, &sink_names); - if (ret) { - goto error; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - poptFreeContext(pc); - free(arg); - arg = NULL; - - /* - * Second pass: transform the convert-specific options and - * arguments into implicit component instances for the run - * command. - */ - pc = poptGetContext(NULL, argc, (const char **) argv, - convert_long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - arg = poptGetOptArg(pc); - - switch (opt) { - case OPT_BEGIN: - if (trimmer_has_begin) { - printf("At --begin option: --begin or --timerange option already specified\n %s\n", - arg); - goto error; - } - - trimmer_has_begin = true; - ret = append_implicit_component_extra_param( - &implicit_trimmer_args, "begin", arg); - implicit_trimmer_args.exists = true; - if (ret) { - goto error; - } - break; - case OPT_END: - if (trimmer_has_end) { - printf("At --end option: --end or --timerange option already specified\n %s\n", - arg); - goto error; - } - - trimmer_has_end = true; - ret = append_implicit_component_extra_param( - &implicit_trimmer_args, "end", arg); - implicit_trimmer_args.exists = true; - if (ret) { - goto error; - } - break; - case OPT_TIMERANGE: - { - char *begin; - char *end; - - if (trimmer_has_begin || trimmer_has_end) { - printf("At --timerange option: --begin, --end, or --timerange option already specified\n %s\n", - arg); - goto error; - } - - ret = split_timerange(arg, &begin, &end); - if (ret) { - printf_err("Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:\n %s\n", - arg); - goto error; - } - - ret = append_implicit_component_extra_param( - &implicit_trimmer_args, "begin", begin); - ret |= append_implicit_component_extra_param( - &implicit_trimmer_args, "end", end); - implicit_trimmer_args.exists = true; - free(begin); - free(end); - if (ret) { - goto error; - } - break; - } - case OPT_CLOCK_CYCLES: - append_implicit_component_param( - &implicit_text_args, "clock-cycles", "yes"); - implicit_text_args.exists = true; - break; - case OPT_CLOCK_DATE: - append_implicit_component_param( - &implicit_text_args, "clock-date", "yes"); - implicit_text_args.exists = true; - break; - case OPT_CLOCK_FORCE_CORRELATE: - append_implicit_component_param( - &implicit_muxer_args, - "assume-absolute-clock-classes", "yes"); - break; - case OPT_CLOCK_GMT: - append_implicit_component_param( - &implicit_text_args, "clock-gmt", "yes"); - append_implicit_component_param( - &implicit_trimmer_args, "gmt", "yes"); - implicit_text_args.exists = true; - break; - case OPT_CLOCK_OFFSET: - implicit_ctf_input_args.exists = true; - append_implicit_component_param( - &implicit_ctf_input_args, - "clock-class-offset-s", arg); - break; - case OPT_CLOCK_OFFSET_NS: - implicit_ctf_input_args.exists = true; - append_implicit_component_param( - &implicit_ctf_input_args, - "clock-class-offset-ns", arg); - break; - case OPT_CLOCK_SECONDS: - append_implicit_component_param( - &implicit_text_args, "clock-seconds", "yes"); - implicit_text_args.exists = true; - break; - case OPT_COLOR: - implicit_text_args.exists = true; - ret = append_implicit_component_extra_param( - &implicit_text_args, "color", arg); - if (ret) { - goto error; - } - break; - case OPT_DEBUG_INFO: - implicit_debug_info_args.exists = true; - break; - case OPT_DEBUG_INFO_DIR: - implicit_debug_info_args.exists = true; - ret = append_implicit_component_extra_param( - &implicit_debug_info_args, "debug-info-dir", arg); - if (ret) { - goto error; - } - break; - case OPT_DEBUG_INFO_FULL_PATH: - implicit_debug_info_args.exists = true; - append_implicit_component_param( - &implicit_debug_info_args, "full-path", "yes"); - break; - case OPT_DEBUG_INFO_TARGET_PREFIX: - implicit_debug_info_args.exists = true; - ret = append_implicit_component_extra_param( - &implicit_debug_info_args, - "target-prefix", arg); - if (ret) { - goto error; - } - break; - case OPT_FIELDS: - { - bt_value *fields = fields_from_arg(arg); - - if (!fields) { - goto error; - } - - implicit_text_args.exists = true; - ret = insert_flat_params_from_array( - implicit_text_args.params_arg, - fields, "field"); - bt_value_put_ref(fields); - if (ret) { - goto error; - } - break; - } - case OPT_NAMES: - { - bt_value *names = names_from_arg(arg); - - if (!names) { - goto error; - } - - implicit_text_args.exists = true; - ret = insert_flat_params_from_array( - implicit_text_args.params_arg, - names, "name"); - bt_value_put_ref(names); - if (ret) { - goto error; - } - break; - } - case OPT_NO_DELTA: - append_implicit_component_param( - &implicit_text_args, "no-delta", "yes"); - implicit_text_args.exists = true; - break; - case OPT_INPUT_FORMAT: - if (got_input_format_opt) { - printf_err("Duplicate --input-format option\n"); - goto error; - } - - got_input_format_opt = true; - - if (strcmp(arg, "ctf") == 0) { - implicit_ctf_input_args.exists = true; - } else if (strcmp(arg, "lttng-live") == 0) { - implicit_lttng_live_args.exists = true; - } else { - printf_err("Unknown legacy input format:\n %s\n", - arg); - goto error; - } - break; - case OPT_OUTPUT_FORMAT: - if (got_output_format_opt) { - printf_err("Duplicate --output-format option\n"); - goto error; - } - - got_output_format_opt = true; - - if (strcmp(arg, "text") == 0) { - implicit_text_args.exists = true; - } else if (strcmp(arg, "ctf") == 0) { - implicit_ctf_output_args.exists = true; - } else if (strcmp(arg, "dummy") == 0) { - implicit_dummy_args.exists = true; - } else if (strcmp(arg, "ctf-metadata") == 0) { - print_ctf_metadata = true; - } else { - printf_err("Unknown legacy output format:\n %s\n", - arg); - goto error; - } - break; - case OPT_OUTPUT: - if (output) { - printf_err("Duplicate --output option\n"); - goto error; - } - - output = strdup(arg); - if (!output) { - print_err_oom(); - goto error; - } - break; - case OPT_RUN_ARGS: - if (print_run_args_0) { - printf_err("Cannot specify --run-args and --run-args-0\n"); - goto error; - } - - print_run_args = true; - break; - case OPT_RUN_ARGS_0: - if (print_run_args) { - printf_err("Cannot specify --run-args and --run-args-0\n"); - goto error; - } - - print_run_args_0 = true; - break; - case OPT_STREAM_INTERSECTION: - /* - * Applies to all traces implementing the trace-info - * query. - */ - stream_intersection_mode = true; - break; - case OPT_VERBOSE: - if (*log_level != 'V' && *log_level != 'D') { - *log_level = 'I'; - } - break; - case OPT_DEBUG: - *log_level = 'V'; - break; - } - - free(arg); - arg = NULL; - } - - /* Check for option parsing error */ - if (opt < -1) { - printf_err("While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - /* - * Legacy behaviour: --verbose used to make the `text` output - * format print more information. --verbose is now equivalent to - * the INFO log level, which is why we compare to 'I' here. - */ - if (*log_level == 'I') { - append_implicit_component_param(&implicit_text_args, - "verbose", "yes"); - } - - /* - * Append home and system plugin paths now that we possibly got - * --plugin-path. - */ - if (append_home_and_system_plugin_paths(plugin_paths, - force_omit_system_plugin_path, - force_omit_home_plugin_path)) { - goto error; - } - - /* Consume and keep leftover arguments */ - while ((leftover = poptGetArg(pc))) { - bt_value_status status = bt_value_array_append_string_element(leftovers, leftover); - if (status != BT_VALUE_STATUS_OK) { - print_err_oom(); - goto error; - } - } - - /* Print CTF metadata or print LTTng live sessions */ - if (print_ctf_metadata) { - const bt_value *bt_val_leftover; - - if (bt_value_array_is_empty(leftovers)) { - printf_err("--output-format=ctf-metadata specified without a path\n"); - goto error; - } - - if (bt_value_array_get_size(leftovers) > 1) { - printf_err("Too many paths specified for --output-format=ctf-metadata\n"); - goto error; - } - - cfg = bt_config_print_ctf_metadata_create(plugin_paths); - if (!cfg) { - goto error; - } - - bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0); - g_string_assign(cfg->cmd_data.print_ctf_metadata.path, - bt_value_string_get(bt_val_leftover)); - - if (output) { - g_string_assign( - cfg->cmd_data.print_ctf_metadata.output_path, - output); - } - - goto end; - } - - /* - * If -o ctf was specified, make sure an output path (--output) - * was also specified. --output does not imply -o ctf because - * it's also used for the default, implicit -o text if -o ctf - * is not specified. - */ - if (implicit_ctf_output_args.exists) { - if (!output) { - printf_err("--output-format=ctf specified without --output (trace output path)\n"); - goto error; - } - - /* - * At this point we know that -o ctf AND --output were - * specified. Make sure that no options were specified - * which would imply -o text because --output would be - * ambiguous in this case. For example, this is wrong: - * - * babeltrace --names=all -o ctf --output=/tmp/path my-trace - * - * because --names=all implies -o text, and --output - * could apply to both the sink.text.pretty and - * sink.ctf.fs implicit components. - */ - if (implicit_text_args.exists) { - printf_err("Ambiguous --output option: --output-format=ctf specified but another option implies --output-format=text\n"); - goto error; - } - } - - /* - * If -o dummy and -o ctf were not specified, and if there are - * no explicit sink components, then use an implicit - * `sink.text.pretty` component. - */ - if (!implicit_dummy_args.exists && !implicit_ctf_output_args.exists && - !sink_names) { - implicit_text_args.exists = true; - } - - /* - * Set implicit `sink.text.pretty` or `sink.ctf.fs` component's - * `path` parameter if --output was specified. - */ - if (output) { - if (implicit_text_args.exists) { - append_implicit_component_extra_param(&implicit_text_args, - "path", output); - } else if (implicit_ctf_output_args.exists) { - append_implicit_component_extra_param(&implicit_ctf_output_args, - "path", output); - } - } - - /* Decide where the leftover argument(s) go */ - if (bt_value_array_get_size(leftovers) > 0) { - if (implicit_lttng_live_args.exists) { - const bt_value *bt_val_leftover; - - if (bt_value_array_get_size(leftovers) > 1) { - printf_err("Too many URLs specified for --output-format=lttng-live\n"); - goto error; - } - - bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0); - lttng_live_url_parts = - bt_common_parse_lttng_live_url(bt_value_string_get(bt_val_leftover), - error_buf, sizeof(error_buf)); - if (!lttng_live_url_parts.proto) { - printf_err("Invalid LTTng live URL format: %s\n", - error_buf); - goto error; - } - - if (!lttng_live_url_parts.session_name) { - /* Print LTTng live sessions */ - cfg = bt_config_print_lttng_live_sessions_create( - plugin_paths); - if (!cfg) { - goto error; - } - - g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url, - bt_value_string_get(bt_val_leftover)); - - if (output) { - g_string_assign( - cfg->cmd_data.print_lttng_live_sessions.output_path, - output); - } - - goto end; - } - - ret = append_implicit_component_extra_param( - &implicit_lttng_live_args, "url", - bt_value_string_get(bt_val_leftover)); - if (ret) { - goto error; - } - - ret = append_implicit_component_extra_param( - &implicit_lttng_live_args, - "session-not-found-action", "end"); - if (ret) { - goto error; - } - } else { - /* - * Create one source.ctf.fs component, pass it an array - * with the leftovers. - * Note that it still has to be named later. - */ - implicit_ctf_input_args.exists = true; - ret = append_parameter_to_args(implicit_ctf_input_args.extra_params, - "paths", leftovers); - if (ret) { - goto error; - } - } - } - - /* - * Ensure mutual exclusion between implicit `source.ctf.fs` and - * `source.ctf.lttng-live` components. - */ - if (implicit_ctf_input_args.exists && implicit_lttng_live_args.exists) { - printf_err("Cannot create both implicit `%s` and `%s` components\n", - implicit_ctf_input_args.comp_arg->str, - implicit_lttng_live_args.comp_arg->str); - goto error; - } - - /* - * If the implicit `source.ctf.fs` or `source.ctf.lttng-live` - * components exists, make sure there's at least one leftover - * (which is the path or URL). - */ - if (implicit_ctf_input_args.exists && bt_value_array_is_empty(leftovers)) { - printf_err("Missing path for implicit `%s` component\n", - implicit_ctf_input_args.comp_arg->str); - goto error; - } - - if (implicit_lttng_live_args.exists && bt_value_array_is_empty(leftovers)) { - printf_err("Missing URL for implicit `%s` component\n", - implicit_lttng_live_args.comp_arg->str); - goto error; - } - - /* Assign names to implicit components */ - ret = assign_name_to_implicit_component(&implicit_ctf_input_args, - "source-ctf-fs", all_names, &source_names, true); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_lttng_live_args, - "lttng-live", all_names, &source_names, true); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_text_args, - "pretty", all_names, &sink_names, true); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_ctf_output_args, - "sink-ctf-fs", all_names, &sink_names, true); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_dummy_args, - "dummy", all_names, &sink_names, true); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_muxer_args, - "muxer", all_names, NULL, false); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_trimmer_args, - "trimmer", all_names, NULL, false); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_debug_info_args, - "debug-info", all_names, NULL, false); - if (ret) { - goto error; - } - - /* Make sure there's at least one source and one sink */ - if (!source_names) { - printf_err("No source component\n"); - goto error; - } - - if (!sink_names) { - printf_err("No sink component\n"); - goto error; - } - - /* - * Prepend the muxer, the trimmer, and the debug info to the - * filter chain so that we have: - * - * sources -> muxer -> [trimmer] -> [debug info] -> - * [user filters] -> sinks - */ - if (implicit_debug_info_args.exists) { - if (g_list_prepend_gstring(&filter_names, - implicit_debug_info_args.name_arg->str)) { - goto error; - } - } - - if (implicit_trimmer_args.exists) { - if (g_list_prepend_gstring(&filter_names, - implicit_trimmer_args.name_arg->str)) { - goto error; - } - } - - if (g_list_prepend_gstring(&filter_names, - implicit_muxer_args.name_arg->str)) { - goto error; - } - - /* - * Append the equivalent run arguments for the implicit - * components. - */ - ret = append_run_args_for_implicit_component(&implicit_ctf_input_args, run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_lttng_live_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_text_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_ctf_output_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_dummy_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_muxer_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_trimmer_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component(&implicit_debug_info_args, - run_args); - if (ret) { - goto error; - } - - /* Auto-connect components */ - ret = convert_auto_connect(run_args, source_names, filter_names, - sink_names); - if (ret) { - printf_err("Cannot auto-connect components\n"); - goto error; - } - - /* - * We have all the run command arguments now. Depending on - * --run-args, we pass this to the run command or print them - * here. - */ - if (print_run_args || print_run_args_0) { - if (stream_intersection_mode) { - printf_err("Cannot specify --stream-intersection with --run-args or --run-args-0\n"); - goto error; - } - - for (i = 0; i < bt_value_array_get_size(run_args); i++) { - const bt_value *arg_value = - bt_value_array_borrow_element_by_index(run_args, - i); - const char *arg; - GString *quoted = NULL; - const char *arg_to_print; - - BT_ASSERT(arg_value); - arg = bt_value_string_get(arg_value); - - if (print_run_args) { - quoted = bt_common_shell_quote(arg, true); - if (!quoted) { - goto error; - } - - arg_to_print = quoted->str; - } else { - arg_to_print = arg; - } - - printf("%s", arg_to_print); - - if (quoted) { - g_string_free(quoted, TRUE); - } - - if (i < bt_value_array_get_size(run_args) - 1) { - if (print_run_args) { - putchar(' '); - } else { - putchar('\0'); - } - } - } - - *retcode = -1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - goto end; - } - - cfg = bt_config_run_from_args_array(run_args, retcode, - force_omit_system_plugin_path, - force_omit_home_plugin_path, - initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->cmd_data.run.stream_intersection_mode = stream_intersection_mode; - goto end; - -error: - *retcode = 1; - BT_OBJECT_PUT_REF_AND_RESET(cfg); - -end: - if (pc) { - poptFreeContext(pc); - } - - free(arg); - free(output); - - if (cur_name) { - g_string_free(cur_name, TRUE); - } - - if (cur_name_prefix) { - g_string_free(cur_name_prefix, TRUE); - } - - bt_value_put_ref(run_args); - bt_value_put_ref(all_names); - destroy_glist_of_gstring(source_names); - destroy_glist_of_gstring(filter_names); - destroy_glist_of_gstring(sink_names); - bt_value_put_ref(leftovers); - finalize_implicit_component_args(&implicit_ctf_input_args); - finalize_implicit_component_args(&implicit_ctf_output_args); - finalize_implicit_component_args(&implicit_lttng_live_args); - finalize_implicit_component_args(&implicit_dummy_args); - finalize_implicit_component_args(&implicit_text_args); - finalize_implicit_component_args(&implicit_debug_info_args); - finalize_implicit_component_args(&implicit_muxer_args); - finalize_implicit_component_args(&implicit_trimmer_args); - bt_value_put_ref(plugin_paths); - bt_common_destroy_lttng_live_url_parts(<tng_live_url_parts); - return cfg; -} - -/* - * Prints the Babeltrace 2.x general usage. - */ -static -void print_gen_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] [COMMAND] [COMMAND ARGUMENTS]\n"); - fprintf(fp, "\n"); - fprintf(fp, "General options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -d, --debug Enable debug mode (same as --log-level=V)\n"); - fprintf(fp, " -h, --help Show this help and quit\n"); - fprintf(fp, " -l, --log-level=LVL Set all log levels to LVL (`N`, `V`, `D`,\n"); - fprintf(fp, " `I`, `W` (default), `E`, or `F`)\n"); - fprintf(fp, " -v, --verbose Enable verbose mode (same as --log-level=I)\n"); - fprintf(fp, " -V, --version Show version and quit\n"); - fprintf(fp, "\n"); - fprintf(fp, "Available commands:\n"); - fprintf(fp, "\n"); - fprintf(fp, " convert Convert and trim traces (default)\n"); - fprintf(fp, " help Get help for a plugin or a component class\n"); - fprintf(fp, " list-plugins List available plugins and their content\n"); - fprintf(fp, " query Query objects from a component class\n"); - fprintf(fp, " run Build a processing graph and run it\n"); - fprintf(fp, "\n"); - fprintf(fp, "Use `babeltrace COMMAND --help` to show the help of COMMAND.\n"); -} - -static -char log_level_from_arg(const char *arg) -{ - char level = 'U'; - - if (strcmp(arg, "VERBOSE") == 0 || - strcmp(arg, "V") == 0) { - level = 'V'; - } else if (strcmp(arg, "DEBUG") == 0 || - strcmp(arg, "D") == 0) { - level = 'D'; - } else if (strcmp(arg, "INFO") == 0 || - strcmp(arg, "I") == 0) { - level = 'I'; - } else if (strcmp(arg, "WARN") == 0 || - strcmp(arg, "WARNING") == 0 || - strcmp(arg, "W") == 0) { - level = 'W'; - } else if (strcmp(arg, "ERROR") == 0 || - strcmp(arg, "E") == 0) { - level = 'E'; - } else if (strcmp(arg, "FATAL") == 0 || - strcmp(arg, "F") == 0) { - level = 'F'; - } else if (strcmp(arg, "NONE") == 0 || - strcmp(arg, "N") == 0) { - level = 'N'; - } - - return level; -} - -struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths) -{ - struct bt_config *config = NULL; - int i; - const char **command_argv = NULL; - int command_argc = -1; - const char *command_name = NULL; - char log_level = 'U'; - - enum command_type { - COMMAND_TYPE_NONE = -1, - COMMAND_TYPE_RUN = 0, - COMMAND_TYPE_CONVERT, - COMMAND_TYPE_LIST_PLUGINS, - COMMAND_TYPE_HELP, - COMMAND_TYPE_QUERY, - } command_type = COMMAND_TYPE_NONE; - - *retcode = -1; - - if (!initial_plugin_paths) { - initial_plugin_paths = bt_value_array_create(); - if (!initial_plugin_paths) { - *retcode = 1; - goto end; - } - } else { - bt_value_get_ref(initial_plugin_paths); - } - - if (argc <= 1) { - print_version(); - puts(""); - print_gen_usage(stdout); - goto end; - } - - for (i = 1; i < argc; i++) { - const char *cur_arg = argv[i]; - const char *next_arg = i == (argc - 1) ? NULL : argv[i + 1]; - - if (strcmp(cur_arg, "-d") == 0 || - strcmp(cur_arg, "--debug") == 0) { - log_level = 'V'; - } else if (strcmp(cur_arg, "-v") == 0 || - strcmp(cur_arg, "--verbose") == 0) { - if (log_level != 'V' && log_level != 'D') { - /* - * Legacy: do not override a previous - * --debug because --verbose and --debug - * can be specified together (in this - * case we want the lowest log level to - * apply, VERBOSE). - */ - log_level = 'I'; - } - } else if (strcmp(cur_arg, "--log-level") == 0 || - strcmp(cur_arg, "-l") == 0) { - if (!next_arg) { - printf_err("Missing log level value for --log-level option\n"); - *retcode = 1; - goto end; - } - - log_level = log_level_from_arg(next_arg); - if (log_level == 'U') { - printf_err("Invalid argument for --log-level option:\n %s\n", - next_arg); - *retcode = 1; - goto end; - } - - i++; - } else if (strncmp(cur_arg, "--log-level=", 12) == 0) { - const char *arg = &cur_arg[12]; - - log_level = log_level_from_arg(arg); - if (log_level == 'U') { - printf_err("Invalid argument for --log-level option:\n %s\n", - arg); - *retcode = 1; - goto end; - } - } else if (strncmp(cur_arg, "-l", 2) == 0) { - const char *arg = &cur_arg[2]; - - log_level = log_level_from_arg(arg); - if (log_level == 'U') { - printf_err("Invalid argument for --log-level option:\n %s\n", - arg); - *retcode = 1; - goto end; - } - } else if (strcmp(cur_arg, "-V") == 0 || - strcmp(cur_arg, "--version") == 0) { - print_version(); - goto end; - } else if (strcmp(cur_arg, "-h") == 0 || - strcmp(cur_arg, "--help") == 0) { - print_gen_usage(stdout); - goto end; - } else { - /* - * First unknown argument: is it a known command - * name? - */ - command_argv = &argv[i]; - command_argc = argc - i; - - if (strcmp(cur_arg, "convert") == 0) { - command_type = COMMAND_TYPE_CONVERT; - } else if (strcmp(cur_arg, "list-plugins") == 0) { - command_type = COMMAND_TYPE_LIST_PLUGINS; - } else if (strcmp(cur_arg, "help") == 0) { - command_type = COMMAND_TYPE_HELP; - } else if (strcmp(cur_arg, "query") == 0) { - command_type = COMMAND_TYPE_QUERY; - } else if (strcmp(cur_arg, "run") == 0) { - command_type = COMMAND_TYPE_RUN; - } else { - /* - * Unknown argument, but not a known - * command name: assume the default - * `convert` command. - */ - command_type = COMMAND_TYPE_CONVERT; - command_name = "convert"; - command_argv = &argv[i - 1]; - command_argc = argc - i + 1; - } - break; - } - } - - if (command_type == COMMAND_TYPE_NONE) { - /* - * We only got non-help, non-version general options - * like --verbose and --debug, without any other - * arguments, so we can't do anything useful: print the - * usage and quit. - */ - print_gen_usage(stdout); - goto end; - } - - BT_ASSERT(command_argv); - BT_ASSERT(command_argc >= 0); - - switch (command_type) { - case COMMAND_TYPE_RUN: - config = bt_config_run_from_args(command_argc, command_argv, - retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths); - break; - case COMMAND_TYPE_CONVERT: - config = bt_config_convert_from_args(command_argc, command_argv, - retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, - initial_plugin_paths, &log_level); - break; - case COMMAND_TYPE_LIST_PLUGINS: - config = bt_config_list_plugins_from_args(command_argc, - command_argv, retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths); - break; - case COMMAND_TYPE_HELP: - config = bt_config_help_from_args(command_argc, - command_argv, retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths); - break; - case COMMAND_TYPE_QUERY: - config = bt_config_query_from_args(command_argc, - command_argv, retcode, force_omit_system_plugin_path, - force_omit_home_plugin_path, initial_plugin_paths); - break; - default: - abort(); - } - - if (config) { - if (log_level == 'U') { - log_level = 'W'; - } - - config->log_level = log_level; - config->command_name = command_name; - } - -end: - bt_value_put_ref(initial_plugin_paths); - return config; -} diff --git a/cli/babeltrace-cfg-cli-args.h b/cli/babeltrace-cfg-cli-args.h deleted file mode 100644 index 78899c56..00000000 --- a/cli/babeltrace-cfg-cli-args.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_H -#define CLI_BABELTRACE_CFG_CLI_ARGS_H - -/* - * Copyright 2016-2017 Philippe Proulx - * - * 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 -#include -#include -#include -#include -#include -#include - -#include "babeltrace-cfg.h" - -struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], - int *retcode, bool force_omit_system_plugin_path, - bool force_omit_home_plugin_path, - const bt_value *initial_plugin_paths); - -#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_H */ diff --git a/cli/babeltrace-cfg.c b/cli/babeltrace-cfg.c deleted file mode 100644 index 1205b21d..00000000 --- a/cli/babeltrace-cfg.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Babeltrace trace converter - parameter parsing - * - * Copyright 2016 Philippe Proulx - * - * 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 -#include -#include -#include "babeltrace-cfg.h" - -static -void destroy_gstring(void *data) -{ - g_string_free(data, TRUE); -} - -/* - * Extracts the various paths from the string arg, delimited by ':', - * and appends them to the array value object plugin_paths. - */ -int bt_config_append_plugin_paths( - bt_value *plugin_paths, const char *arg) -{ - int ret = 0; - GPtrArray *dirs = g_ptr_array_new_with_free_func(destroy_gstring); - size_t i; - - if (!dirs) { - ret = -1; - goto end; - } - - ret = bt_common_append_plugin_path_dirs(arg, dirs); - if (ret) { - ret = -1; - goto end; - } - - for (i = 0; i < dirs->len; i++) { - GString *dir = g_ptr_array_index(dirs, i); - - ret = bt_value_array_append_string_element( - plugin_paths, dir->str); - if (ret != BT_VALUE_STATUS_OK) { - ret = -1; - goto end; - } - } - -end: - g_ptr_array_free(dirs, TRUE); - return ret; -} - -void bt_config_connection_destroy(struct bt_config_connection *connection) -{ - if (!connection) { - return; - } - - if (connection->upstream_comp_name) { - g_string_free(connection->upstream_comp_name, TRUE); - } - - if (connection->downstream_comp_name) { - g_string_free(connection->downstream_comp_name, TRUE); - } - - if (connection->upstream_port_glob) { - g_string_free(connection->upstream_port_glob, TRUE); - } - - if (connection->downstream_port_glob) { - g_string_free(connection->downstream_port_glob, TRUE); - } - - if (connection->arg) { - g_string_free(connection->arg, TRUE); - } - - g_free(connection); -} diff --git a/cli/babeltrace-cfg.h b/cli/babeltrace-cfg.h deleted file mode 100644 index 6d98cd33..00000000 --- a/cli/babeltrace-cfg.h +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef CLI_BABELTRACE_CFG_H -#define CLI_BABELTRACE_CFG_H - -/* - * Babeltrace trace converter - CLI tool's configuration - * - * Copyright 2016-2017 Philippe Proulx - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -enum bt_config_command { - BT_CONFIG_COMMAND_RUN, - BT_CONFIG_COMMAND_PRINT_CTF_METADATA, - BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS, - BT_CONFIG_COMMAND_LIST_PLUGINS, - BT_CONFIG_COMMAND_HELP, - BT_CONFIG_COMMAND_QUERY, -}; - -struct bt_config_component { - bt_object base; - bt_component_class_type type; - GString *plugin_name; - GString *comp_cls_name; - bt_value *params; - GString *instance_name; -}; - -struct bt_config_connection { - GString *upstream_comp_name; - GString *downstream_comp_name; - GString *upstream_port_glob; - GString *downstream_port_glob; - GString *arg; -}; - -struct bt_config { - bt_object base; - bool debug; - bool verbose; - bt_value *plugin_paths; - bool omit_system_plugin_path; - bool omit_home_plugin_path; - bool command_needs_plugins; - const char *command_name; - char log_level; - enum bt_config_command command; - union { - /* BT_CONFIG_COMMAND_RUN */ - struct { - /* 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; - - /* - * Number of microseconds to sleep when we need - * to retry to run the graph. - */ - uint64_t retry_duration_us; - - /* - * Whether or not to trim the source trace to the - * intersection of its streams. - */ - bool stream_intersection_mode; - } run; - - /* BT_CONFIG_COMMAND_HELP */ - struct { - struct bt_config_component *cfg_component; - } help; - - /* BT_CONFIG_COMMAND_QUERY */ - struct { - GString *object; - struct bt_config_component *cfg_component; - } query; - - /* BT_CONFIG_COMMAND_PRINT_CTF_METADATA */ - struct { - GString *path; - GString *output_path; - } print_ctf_metadata; - - /* BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS */ - struct { - GString *url; - GString *output_path; - } print_lttng_live_sessions; - } cmd_data; -}; - -static inline -struct bt_config_component *bt_config_get_component(GPtrArray *array, - size_t index) -{ - struct bt_config_component *comp = g_ptr_array_index(array, index); - - bt_object_get_ref(comp); - return comp; -} - -int bt_config_append_plugin_paths(bt_value *plugin_paths, - const char *arg); - -void bt_config_connection_destroy(struct bt_config_connection *connection); - -#endif /* CLI_BABELTRACE_CFG_H */ diff --git a/cli/babeltrace-log.c b/cli/babeltrace-log.c deleted file mode 100644 index d429ceb1..00000000 --- a/cli/babeltrace-log.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2017 Philippe Proulx - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -static -void print_usage(FILE *fp) -{ - fprintf(stderr, "Usage: babeltrace-log [OPTIONS] OUTPUT-PATH\n"); - fprintf(stderr, "\n"); - fprintf(stderr, "Options:\n"); - fprintf(stderr, "\n"); - fprintf(stderr, " -t, --with-timestamps Extract timestamps from lines and map them to\n"); - fprintf(stderr, " a CTF clock class\n"); -} - -static -int parse_params(int argc, char *argv[], char **output_path, - bool *no_extract_ts) -{ - enum { - OPT_WITH_TIMESTAMPS = 1, - OPT_HELP = 2, - }; - static struct poptOption opts[] = { - { "with-timestamps", 't', POPT_ARG_NONE, NULL, OPT_WITH_TIMESTAMPS, NULL, NULL }, - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { NULL, '\0', 0, NULL, 0, NULL, NULL }, - }; - poptContext pc = NULL; - int opt; - int ret = 0; - const char *leftover; - - *no_extract_ts = true; - pc = poptGetContext(NULL, argc, (const char **) argv, opts, 0); - if (!pc) { - fprintf(stderr, "Cannot get popt context\n"); - goto error; - } - - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) > 0) { - switch (opt) { - case OPT_HELP: - print_usage(stdout); - goto end; - case OPT_WITH_TIMESTAMPS: - *no_extract_ts = false; - break; - default: - fprintf(stderr, "Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - } - - if (opt < -1) { - fprintf(stderr, "While parsing command-line options, at option %s: %s\n", - poptBadOption(pc, 0), poptStrerror(opt)); - goto error; - } - - leftover = poptGetArg(pc); - if (!leftover) { - fprintf(stderr, "Command line error: Missing output path\n"); - print_usage(stderr); - goto error; - } - - *output_path = strdup(leftover); - BT_ASSERT(*output_path); - goto end; - -error: - ret = 1; - -end: - if (pc) { - poptFreeContext(pc); - } - - return ret; -} - -int main(int argc, char *argv[]) -{ - char *output_path = NULL; - bool no_extract_ts; - int retcode; - GError *error = NULL; - gchar *bt_argv[] = { - BT_CLI_PATH, - "run", - "--component", - "dmesg:src.text.dmesg", - "--params", - NULL, /* no-extract-timestamp=? placeholder */ - "--component", - "ctf:sink.ctf.fs", - "--key", - "path", - "--value", - NULL, /* output path placeholder */ - "--params", - "single-trace=yes", - "--connect", - "dmesg:ctf", - NULL, /* sentinel */ - }; - - retcode = parse_params(argc, argv, &output_path, &no_extract_ts); - if (retcode) { - goto end; - } - - if (no_extract_ts) { - bt_argv[5] = "no-extract-timestamp=yes"; - } else { - bt_argv[5] = "no-extract-timestamp=no"; - } - - bt_argv[11] = output_path; - (void) g_spawn_sync(NULL, bt_argv, NULL, - G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, - NULL, NULL, &retcode, &error); - - if (error) { - fprintf(stderr, "Failed to execute \"%s\": %s (%d)\n", - bt_argv[0], error->message, error->code); - } - -end: - free(output_path); - - if (error) { - g_error_free(error); - } - - return retcode; -} diff --git a/cli/babeltrace.c b/cli/babeltrace.c deleted file mode 100644 index 913a7421..00000000 --- a/cli/babeltrace.c +++ /dev/null @@ -1,2950 +0,0 @@ -/* - * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation - * - * Author: Mathieu Desnoyers - * - * 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" -#include "logging.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "babeltrace-cfg.h" -#include "babeltrace-cfg-cli-args.h" -#include "babeltrace-cfg-cli-args-default.h" - -#define ENV_BABELTRACE_WARN_COMMAND_NAME_DIRECTORY_CLASH "BABELTRACE_CLI_WARN_COMMAND_NAME_DIRECTORY_CLASH" -#define ENV_BABELTRACE_CLI_LOG_LEVEL "BABELTRACE_CLI_LOG_LEVEL" -#define NSEC_PER_SEC 1000000000LL - -/* - * Known environment variable names for the log levels of the project's - * modules. - */ -static const char* log_level_env_var_names[] = { - "BABELTRACE_COMMON_LOG_LEVEL", - "BABELTRACE_COMPAT_LOG_LEVEL", - "BABELTRACE_CTFSER_LOG_LEVEL", - "BABELTRACE_FD_CACHE_LOG_LEVEL", - "BABELTRACE_FLT_LTTNG_UTILS_DEBUG_INFO_LOG_LEVEL", - "BABELTRACE_FLT_UTILS_COUNTER_LOG_LEVEL", - "BABELTRACE_FLT_UTILS_MUXER_LOG_LEVEL", - "BABELTRACE_FLT_UTILS_TRIMMER_LOG_LEVEL", - "BABELTRACE_PLUGIN_CTF_BFCR_LOG_LEVEL", - "BABELTRACE_PLUGIN_CTF_METADATA_LOG_LEVEL", - "BABELTRACE_PLUGIN_CTF_MSG_ITER_LOG_LEVEL", - "BABELTRACE_PLUGIN_CTF_UTILS_LOG_LEVEL", - "BABELTRACE_PYTHON_BT2_LOG_LEVEL", - "BABELTRACE_SINK_CTF_FS_LOG_LEVEL", - "BABELTRACE_SINK_TEXT_PRETTY_LOG_LEVEL", - "BABELTRACE_SRC_CTF_FS_LOG_LEVEL", - "BABELTRACE_SRC_CTF_LTTNG_LIVE_LOG_LEVEL", - "BABELTRACE_SRC_TEXT_DMESG_LOG_LEVEL", - NULL, -}; - -/* Application's processing graph (weak) */ -static bt_graph *the_graph; -static bt_query_executor *the_query_executor; -static bool canceled = false; - -GPtrArray *loaded_plugins; - -#ifdef __MINGW32__ - -#include - -static -BOOL WINAPI signal_handler(DWORD signal) { - if (the_graph) { - bt_graph_cancel(the_graph); - } - - canceled = true; - - return TRUE; -} - -static -void set_signal_handler(void) -{ - if (!SetConsoleCtrlHandler(signal_handler, TRUE)) { - BT_LOGE("Failed to set the ctrl+c handler."); - } -} - -#else /* __MINGW32__ */ - -static -void signal_handler(int signum) -{ - if (signum != SIGINT) { - return; - } - - if (the_graph) { - bt_graph_cancel(the_graph); - } - - if (the_query_executor) { - bt_query_executor_cancel(the_query_executor); - } - - canceled = true; -} - -static -void set_signal_handler(void) -{ - struct sigaction new_action, old_action; - - new_action.sa_handler = signal_handler; - sigemptyset(&new_action.sa_mask); - new_action.sa_flags = 0; - sigaction(SIGINT, NULL, &old_action); - - if (old_action.sa_handler != SIG_IGN) { - sigaction(SIGINT, &new_action, NULL); - } -} - -#endif /* __MINGW32__ */ - -static -void init_static_data(void) -{ - loaded_plugins = g_ptr_array_new_with_free_func( - (GDestroyNotify) bt_object_put_ref); -} - -static -void fini_static_data(void) -{ - g_ptr_array_free(loaded_plugins, TRUE); -} - -static -int create_the_query_executor(void) -{ - int ret = 0; - - the_query_executor = bt_query_executor_create(); - if (!the_query_executor) { - BT_LOGE_STR("Cannot create a query executor."); - ret = -1; - } - - return ret; -} - -static -void destroy_the_query_executor(void) -{ - BT_QUERY_EXECUTOR_PUT_REF_AND_RESET(the_query_executor); -} - -static -int query(const bt_component_class *comp_cls, const char *obj, - const bt_value *params, const bt_value **user_result, - const char **fail_reason) -{ - const bt_value *result = NULL; - bt_query_executor_status status; - *fail_reason = "unknown error"; - int ret = 0; - - BT_ASSERT(fail_reason); - BT_ASSERT(user_result); - ret = create_the_query_executor(); - if (ret) { - /* create_the_query_executor() logs errors */ - goto end; - } - - if (canceled) { - BT_LOGI("Canceled by user before executing the query: " - "comp-cls-addr=%p, comp-cls-name=\"%s\", " - "query-obj=\"%s\"", comp_cls, - bt_component_class_get_name(comp_cls), obj); - *fail_reason = "canceled by user"; - goto error; - } - - while (true) { - status = bt_query_executor_query(the_query_executor, - comp_cls, obj, params, &result); - switch (status) { - case BT_QUERY_EXECUTOR_STATUS_OK: - goto ok; - case BT_QUERY_EXECUTOR_STATUS_AGAIN: - { - const uint64_t sleep_time_us = 100000; - - /* Wait 100 ms and retry */ - BT_LOGV("Got BT_QUERY_EXECUTOR_STATUS_AGAIN: sleeping: " - "time-us=%" PRIu64, sleep_time_us); - - if (usleep(sleep_time_us)) { - if (bt_query_executor_is_canceled(the_query_executor)) { - BT_LOGI("Query was canceled by user: " - "comp-cls-addr=%p, comp-cls-name=\"%s\", " - "query-obj=\"%s\"", comp_cls, - bt_component_class_get_name(comp_cls), - obj); - *fail_reason = "canceled by user"; - goto error; - } - } - - continue; - } - case BT_QUERY_EXECUTOR_STATUS_CANCELED: - *fail_reason = "canceled by user"; - goto error; - case BT_QUERY_EXECUTOR_STATUS_ERROR: - goto error; - case BT_QUERY_EXECUTOR_STATUS_INVALID_OBJECT: - *fail_reason = "invalid or unknown query object"; - goto error; - case BT_QUERY_EXECUTOR_STATUS_INVALID_PARAMS: - *fail_reason = "invalid query parameters"; - goto error; - case BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED: - *fail_reason = "unsupported action"; - goto error; - case BT_QUERY_EXECUTOR_STATUS_NOMEM: - *fail_reason = "not enough memory"; - goto error; - default: - BT_LOGF("Unknown query status: status=%d", status); - abort(); - } - } - -ok: - *user_result = result; - result = NULL; - goto end; - -error: - ret = -1; - -end: - destroy_the_query_executor(); - bt_value_put_ref(result); - return ret; -} - -static -const bt_plugin *find_plugin(const char *name) -{ - int i; - const bt_plugin *plugin = NULL; - - BT_ASSERT(name); - BT_LOGD("Finding plugin: name=\"%s\"", name); - - for (i = 0; i < loaded_plugins->len; i++) { - plugin = g_ptr_array_index(loaded_plugins, i); - - if (strcmp(name, bt_plugin_get_name(plugin)) == 0) { - break; - } - - plugin = NULL; - } - - if (BT_LOG_ON_DEBUG) { - if (plugin) { - BT_LOGD("Found plugin: plugin-addr=%p", plugin); - } else { - BT_LOGD("Cannot find plugin."); - } - } - - bt_plugin_get_ref(plugin); - return plugin; -} - -typedef const void *(*plugin_borrow_comp_cls_func_t)( - const bt_plugin *, const char *); - -static -const void *find_component_class_from_plugin(const char *plugin_name, - const char *comp_class_name, - plugin_borrow_comp_cls_func_t plugin_borrow_comp_cls_func) -{ - const void *comp_class = NULL; - const bt_plugin *plugin; - - BT_LOGD("Finding component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\"", plugin_name, comp_class_name); - - plugin = find_plugin(plugin_name); - if (!plugin) { - goto end; - } - - comp_class = plugin_borrow_comp_cls_func(plugin, comp_class_name); - bt_object_get_ref(comp_class); - BT_PLUGIN_PUT_REF_AND_RESET(plugin); - -end: - if (BT_LOG_ON_DEBUG) { - if (comp_class) { - BT_LOGD("Found component class: comp-cls-addr=%p", - comp_class); - } else { - BT_LOGD("Cannot find source component class."); - } - } - - return comp_class; -} - -static -const bt_component_class_source *find_source_component_class( - const char *plugin_name, const char *comp_class_name) -{ - return (const void *) find_component_class_from_plugin( - plugin_name, comp_class_name, - (plugin_borrow_comp_cls_func_t) - bt_plugin_borrow_source_component_class_by_name_const); -} - -static -const bt_component_class_filter *find_filter_component_class( - const char *plugin_name, const char *comp_class_name) -{ - return (const void *) find_component_class_from_plugin( - plugin_name, comp_class_name, - (plugin_borrow_comp_cls_func_t) - bt_plugin_borrow_filter_component_class_by_name_const); -} - -static -const bt_component_class_sink *find_sink_component_class( - const char *plugin_name, const char *comp_class_name) -{ - return (const void *) find_component_class_from_plugin(plugin_name, - comp_class_name, - (plugin_borrow_comp_cls_func_t) - bt_plugin_borrow_sink_component_class_by_name_const); -} - -static -const bt_component_class *find_component_class(const char *plugin_name, - const char *comp_class_name, - bt_component_class_type comp_class_type) -{ - const bt_component_class *comp_cls = NULL; - - switch (comp_class_type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - comp_cls = bt_component_class_source_as_component_class_const(find_source_component_class(plugin_name, comp_class_name)); - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - comp_cls = bt_component_class_filter_as_component_class_const(find_filter_component_class(plugin_name, comp_class_name)); - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - comp_cls = bt_component_class_sink_as_component_class_const(find_sink_component_class(plugin_name, comp_class_name)); - break; - default: - abort(); - } - - return comp_cls; -} - -static -void print_indent(FILE *fp, size_t indent) -{ - size_t i; - - for (i = 0; i < indent; i++) { - fprintf(fp, " "); - } -} - -static -const char *component_type_str(bt_component_class_type type) -{ - switch (type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - return "source"; - case BT_COMPONENT_CLASS_TYPE_SINK: - return "sink"; - case BT_COMPONENT_CLASS_TYPE_FILTER: - return "filter"; - default: - return "(unknown)"; - } -} - -static -void print_plugin_comp_cls_opt(FILE *fh, const char *plugin_name, - const char *comp_cls_name, bt_component_class_type type) -{ - GString *shell_plugin_name = NULL; - GString *shell_comp_cls_name = NULL; - - shell_plugin_name = bt_common_shell_quote(plugin_name, false); - if (!shell_plugin_name) { - goto end; - } - - shell_comp_cls_name = bt_common_shell_quote(comp_cls_name, false); - if (!shell_comp_cls_name) { - goto end; - } - - fprintf(fh, "'%s%s%s%s.%s%s%s.%s%s%s'", - bt_common_color_bold(), - bt_common_color_fg_cyan(), - component_type_str(type), - bt_common_color_fg_default(), - bt_common_color_fg_blue(), - shell_plugin_name->str, - bt_common_color_fg_default(), - bt_common_color_fg_yellow(), - shell_comp_cls_name->str, - bt_common_color_reset()); - -end: - if (shell_plugin_name) { - g_string_free(shell_plugin_name, TRUE); - } - - if (shell_comp_cls_name) { - g_string_free(shell_comp_cls_name, TRUE); - } -} - -static -void print_value(FILE *, const bt_value *, size_t); - -static -void print_value_rec(FILE *, const bt_value *, size_t); - -struct print_map_value_data { - size_t indent; - FILE *fp; -}; - -static -bt_bool print_map_value(const char *key, const bt_value *object, - void *data) -{ - struct print_map_value_data *print_map_value_data = data; - - print_indent(print_map_value_data->fp, print_map_value_data->indent); - fprintf(print_map_value_data->fp, "%s: ", key); - BT_ASSERT(object); - - if (bt_value_is_array(object) && - bt_value_array_is_empty(object)) { - fprintf(print_map_value_data->fp, "[ ]\n"); - return true; - } - - if (bt_value_is_map(object) && - bt_value_map_is_empty(object)) { - fprintf(print_map_value_data->fp, "{ }\n"); - return true; - } - - if (bt_value_is_array(object) || - bt_value_is_map(object)) { - fprintf(print_map_value_data->fp, "\n"); - } - - print_value_rec(print_map_value_data->fp, object, - print_map_value_data->indent + 2); - return BT_TRUE; -} - -static -void print_value_rec(FILE *fp, const bt_value *value, size_t indent) -{ - bt_bool bool_val; - int64_t int_val; - uint64_t uint_val; - double dbl_val; - const char *str_val; - int size; - int i; - - if (!value) { - return; - } - - switch (bt_value_get_type(value)) { - case BT_VALUE_TYPE_NULL: - fprintf(fp, "%snull%s\n", bt_common_color_bold(), - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_BOOL: - bool_val = bt_value_bool_get(value); - fprintf(fp, "%s%s%s%s\n", bt_common_color_bold(), - bt_common_color_fg_cyan(), bool_val ? "yes" : "no", - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_UNSIGNED_INTEGER: - uint_val = bt_value_unsigned_integer_get(value); - fprintf(fp, "%s%s%" PRIu64 "%s\n", bt_common_color_bold(), - bt_common_color_fg_red(), uint_val, - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_SIGNED_INTEGER: - int_val = bt_value_signed_integer_get(value); - fprintf(fp, "%s%s%" PRId64 "%s\n", bt_common_color_bold(), - bt_common_color_fg_red(), int_val, - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_REAL: - dbl_val = bt_value_real_get(value); - fprintf(fp, "%s%s%lf%s\n", bt_common_color_bold(), - bt_common_color_fg_red(), dbl_val, - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_STRING: - str_val = bt_value_string_get(value); - fprintf(fp, "%s%s%s%s\n", bt_common_color_bold(), - bt_common_color_fg_green(), str_val, - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_ARRAY: - size = bt_value_array_get_size(value); - if (size < 0) { - goto error; - } - - if (size == 0) { - print_indent(fp, indent); - fprintf(fp, "[ ]\n"); - break; - } - - for (i = 0; i < size; i++) { - const bt_value *element = - bt_value_array_borrow_element_by_index_const( - value, i); - - if (!element) { - goto error; - } - print_indent(fp, indent); - fprintf(fp, "- "); - - if (bt_value_is_array(element) && - bt_value_array_is_empty(element)) { - fprintf(fp, "[ ]\n"); - continue; - } - - if (bt_value_is_map(element) && - bt_value_map_is_empty(element)) { - fprintf(fp, "{ }\n"); - continue; - } - - if (bt_value_is_array(element) || - bt_value_is_map(element)) { - fprintf(fp, "\n"); - } - - print_value_rec(fp, element, indent + 2); - } - break; - case BT_VALUE_TYPE_MAP: - { - struct print_map_value_data data = { - .indent = indent, - .fp = fp, - }; - - if (bt_value_map_is_empty(value)) { - print_indent(fp, indent); - fprintf(fp, "{ }\n"); - break; - } - - bt_value_map_foreach_entry_const(value, print_map_value, &data); - break; - } - default: - abort(); - } - return; - -error: - BT_LOGE("Error printing value of type %s.", - bt_common_value_type_string(bt_value_get_type(value))); -} - -static -void print_value(FILE *fp, const bt_value *value, size_t indent) -{ - if (!bt_value_is_array(value) && !bt_value_is_map(value)) { - print_indent(fp, indent); - } - - print_value_rec(fp, value, indent); -} - -static -void print_bt_config_component(struct bt_config_component *bt_config_component) -{ - fprintf(stderr, " "); - print_plugin_comp_cls_opt(stderr, bt_config_component->plugin_name->str, - bt_config_component->comp_cls_name->str, - bt_config_component->type); - fprintf(stderr, ":\n"); - - if (bt_config_component->instance_name->len > 0) { - fprintf(stderr, " Name: %s\n", - bt_config_component->instance_name->str); - } - - fprintf(stderr, " Parameters:\n"); - print_value(stderr, bt_config_component->params, 8); -} - -static -void print_bt_config_components(GPtrArray *array) -{ - size_t i; - - for (i = 0; i < array->len; i++) { - struct bt_config_component *cfg_component = - bt_config_get_component(array, i); - print_bt_config_component(cfg_component); - BT_OBJECT_PUT_REF_AND_RESET(cfg_component); - } -} - -static -void print_plugin_paths(const bt_value *plugin_paths) -{ - fprintf(stderr, " Plugin paths:\n"); - print_value(stderr, plugin_paths, 4); -} - -static -void print_cfg_run(struct bt_config *cfg) -{ - size_t i; - - print_plugin_paths(cfg->plugin_paths); - fprintf(stderr, " Source component instances:\n"); - print_bt_config_components(cfg->cmd_data.run.sources); - - if (cfg->cmd_data.run.filters->len > 0) { - fprintf(stderr, " Filter component instances:\n"); - print_bt_config_components(cfg->cmd_data.run.filters); - } - - fprintf(stderr, " Sink component instances:\n"); - print_bt_config_components(cfg->cmd_data.run.sinks); - fprintf(stderr, " Connections:\n"); - - for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *cfg_connection = - g_ptr_array_index(cfg->cmd_data.run.connections, - i); - - fprintf(stderr, " %s%s%s -> %s%s%s\n", - cfg_connection->upstream_comp_name->str, - cfg_connection->upstream_port_glob->len > 0 ? "." : "", - cfg_connection->upstream_port_glob->str, - cfg_connection->downstream_comp_name->str, - cfg_connection->downstream_port_glob->len > 0 ? "." : "", - cfg_connection->downstream_port_glob->str); - } -} - -static -void print_cfg_list_plugins(struct bt_config *cfg) -{ - print_plugin_paths(cfg->plugin_paths); -} - -static -void print_cfg_help(struct bt_config *cfg) -{ - print_plugin_paths(cfg->plugin_paths); -} - -static -void print_cfg_print_ctf_metadata(struct bt_config *cfg) -{ - print_plugin_paths(cfg->plugin_paths); - fprintf(stderr, " Path: %s\n", - cfg->cmd_data.print_ctf_metadata.path->str); -} - -static -void print_cfg_print_lttng_live_sessions(struct bt_config *cfg) -{ - print_plugin_paths(cfg->plugin_paths); - fprintf(stderr, " URL: %s\n", - cfg->cmd_data.print_lttng_live_sessions.url->str); -} - -static -void print_cfg_query(struct bt_config *cfg) -{ - print_plugin_paths(cfg->plugin_paths); - fprintf(stderr, " Object: `%s`\n", cfg->cmd_data.query.object->str); - fprintf(stderr, " Component class:\n"); - print_bt_config_component(cfg->cmd_data.query.cfg_component); -} - -static -void print_cfg(struct bt_config *cfg) -{ - if (!BT_LOG_ON_INFO) { - return; - } - - BT_LOGI_STR("Configuration:"); - fprintf(stderr, " Debug mode: %s\n", cfg->debug ? "yes" : "no"); - fprintf(stderr, " Verbose mode: %s\n", cfg->verbose ? "yes" : "no"); - - switch (cfg->command) { - case BT_CONFIG_COMMAND_RUN: - print_cfg_run(cfg); - break; - case BT_CONFIG_COMMAND_LIST_PLUGINS: - print_cfg_list_plugins(cfg); - break; - case BT_CONFIG_COMMAND_HELP: - print_cfg_help(cfg); - break; - case BT_CONFIG_COMMAND_QUERY: - print_cfg_query(cfg); - break; - case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: - print_cfg_print_ctf_metadata(cfg); - break; - case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: - print_cfg_print_lttng_live_sessions(cfg); - break; - default: - abort(); - } -} - -static -void add_to_loaded_plugins(const bt_plugin_set *plugin_set) -{ - int64_t i; - int64_t count; - - count = bt_plugin_set_get_plugin_count(plugin_set); - BT_ASSERT(count >= 0); - - for (i = 0; i < count; i++) { - const bt_plugin *plugin = - bt_plugin_set_borrow_plugin_by_index_const(plugin_set, i); - const bt_plugin *loaded_plugin = - find_plugin(bt_plugin_get_name(plugin)); - - BT_ASSERT(plugin); - - if (loaded_plugin) { - BT_LOGI("Not using plugin: another one already exists with the same name: " - "plugin-name=\"%s\", plugin-path=\"%s\", " - "existing-plugin-path=\"%s\"", - bt_plugin_get_name(plugin), - bt_plugin_get_path(plugin), - bt_plugin_get_path(loaded_plugin)); - bt_plugin_put_ref(loaded_plugin); - } else { - /* Add to global array. */ - BT_LOGD("Adding plugin to loaded plugins: plugin-path=\"%s\"", - bt_plugin_get_name(plugin)); - bt_plugin_get_ref(plugin); - g_ptr_array_add(loaded_plugins, (void *) plugin); - } - } -} - -static -int load_dynamic_plugins(const bt_value *plugin_paths) -{ - int nr_paths, i, ret = 0; - - nr_paths = bt_value_array_get_size(plugin_paths); - if (nr_paths < 0) { - BT_LOGE_STR("Cannot load dynamic plugins: no plugin path."); - ret = -1; - goto end; - } - - BT_LOGI("Loading dynamic plugins."); - - for (i = 0; i < nr_paths; i++) { - const bt_value *plugin_path_value = NULL; - const char *plugin_path; - const bt_plugin_set *plugin_set; - - plugin_path_value = - bt_value_array_borrow_element_by_index_const( - plugin_paths, i); - plugin_path = bt_value_string_get(plugin_path_value); - - /* - * Skip this if the directory does not exist because - * bt_plugin_find_all_from_dir() expects an existing - * directory. - */ - if (!g_file_test(plugin_path, G_FILE_TEST_IS_DIR)) { - BT_LOGV("Skipping nonexistent directory path: " - "path=\"%s\"", plugin_path); - continue; - } - - plugin_set = bt_plugin_find_all_from_dir(plugin_path, false); - if (!plugin_set) { - BT_LOGD("Unable to load dynamic plugins: path=\"%s\"", - plugin_path); - continue; - } - - add_to_loaded_plugins(plugin_set); - bt_plugin_set_put_ref(plugin_set); - } -end: - return ret; -} - -static -int load_static_plugins(void) -{ - int ret = 0; - const bt_plugin_set *plugin_set; - - BT_LOGI("Loading static plugins."); - plugin_set = bt_plugin_find_all_from_static(); - if (!plugin_set) { - BT_LOGE("Unable to load static plugins."); - ret = -1; - goto end; - } - - add_to_loaded_plugins(plugin_set); - bt_plugin_set_put_ref(plugin_set); -end: - return ret; -} - -static -int load_all_plugins(const bt_value *plugin_paths) -{ - int ret = 0; - - if (load_dynamic_plugins(plugin_paths)) { - ret = -1; - goto end; - } - - if (load_static_plugins()) { - ret = -1; - goto end; - } - - BT_LOGI("Loaded all plugins: count=%u", loaded_plugins->len); - -end: - return ret; -} - -static -void print_plugin_info(const bt_plugin *plugin) -{ - unsigned int major, minor, patch; - const char *extra; - bt_property_availability version_avail; - const char *plugin_name; - const char *path; - const char *author; - const char *license; - const char *plugin_description; - - plugin_name = bt_plugin_get_name(plugin); - path = bt_plugin_get_path(plugin); - author = bt_plugin_get_author(plugin); - license = bt_plugin_get_license(plugin); - plugin_description = bt_plugin_get_description(plugin); - version_avail = bt_plugin_get_version(plugin, &major, &minor, - &patch, &extra); - printf("%s%s%s%s:\n", bt_common_color_bold(), - bt_common_color_fg_blue(), plugin_name, - bt_common_color_reset()); - if (path) { - printf(" %sPath%s: %s\n", bt_common_color_bold(), - bt_common_color_reset(), path); - } else { - puts(" Built-in"); - } - - if (version_avail == BT_PROPERTY_AVAILABILITY_AVAILABLE) { - printf(" %sVersion%s: %u.%u.%u", - bt_common_color_bold(), bt_common_color_reset(), - major, minor, patch); - - if (extra) { - printf("%s", extra); - } - - printf("\n"); - } - - printf(" %sDescription%s: %s\n", bt_common_color_bold(), - bt_common_color_reset(), - plugin_description ? plugin_description : "(None)"); - printf(" %sAuthor%s: %s\n", bt_common_color_bold(), - bt_common_color_reset(), author ? author : "(Unknown)"); - printf(" %sLicense%s: %s\n", bt_common_color_bold(), - bt_common_color_reset(), - license ? license : "(Unknown)"); -} - -static -int cmd_query(struct bt_config *cfg) -{ - int ret = 0; - const bt_component_class *comp_cls = NULL; - const bt_value *results = NULL; - const char *fail_reason = NULL; - - comp_cls = find_component_class( - cfg->cmd_data.query.cfg_component->plugin_name->str, - cfg->cmd_data.query.cfg_component->comp_cls_name->str, - cfg->cmd_data.query.cfg_component->type); - if (!comp_cls) { - BT_LOGE("Cannot find component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d", - cfg->cmd_data.query.cfg_component->plugin_name->str, - cfg->cmd_data.query.cfg_component->comp_cls_name->str, - cfg->cmd_data.query.cfg_component->type); - fprintf(stderr, "%s%sCannot find component class %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, - cfg->cmd_data.query.cfg_component->plugin_name->str, - cfg->cmd_data.query.cfg_component->comp_cls_name->str, - cfg->cmd_data.query.cfg_component->type); - fprintf(stderr, "\n"); - ret = -1; - goto end; - } - - ret = query(comp_cls, cfg->cmd_data.query.object->str, - cfg->cmd_data.query.cfg_component->params, - &results, &fail_reason); - if (ret) { - goto failed; - } - - print_value(stdout, results, 0); - goto end; - -failed: - BT_LOGE("Failed to query component class: %s: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d " - "object=\"%s\"", fail_reason, - cfg->cmd_data.query.cfg_component->plugin_name->str, - cfg->cmd_data.query.cfg_component->comp_cls_name->str, - cfg->cmd_data.query.cfg_component->type, - cfg->cmd_data.query.object->str); - fprintf(stderr, "%s%sFailed to query info to %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, - cfg->cmd_data.query.cfg_component->plugin_name->str, - cfg->cmd_data.query.cfg_component->comp_cls_name->str, - cfg->cmd_data.query.cfg_component->type); - fprintf(stderr, "%s%s with object `%s`: %s%s\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - cfg->cmd_data.query.object->str, - fail_reason, - bt_common_color_reset()); - ret = -1; - -end: - bt_component_class_put_ref(comp_cls); - bt_value_put_ref(results); - return ret; -} - -static -void print_component_class_help(const char *plugin_name, - const bt_component_class *comp_cls) -{ - const char *comp_class_name = - bt_component_class_get_name(comp_cls); - const char *comp_class_description = - bt_component_class_get_description(comp_cls); - const char *comp_class_help = - bt_component_class_get_help(comp_cls); - bt_component_class_type type = - bt_component_class_get_type(comp_cls); - - print_plugin_comp_cls_opt(stdout, plugin_name, comp_class_name, type); - printf("\n"); - printf(" %sDescription%s: %s\n", bt_common_color_bold(), - bt_common_color_reset(), - comp_class_description ? comp_class_description : "(None)"); - - if (comp_class_help) { - printf("\n%s\n", comp_class_help); - } -} - -static -int cmd_help(struct bt_config *cfg) -{ - int ret = 0; - const bt_plugin *plugin = NULL; - const bt_component_class *needed_comp_cls = NULL; - - plugin = find_plugin(cfg->cmd_data.help.cfg_component->plugin_name->str); - if (!plugin) { - BT_LOGE("Cannot find plugin: plugin-name=\"%s\"", - cfg->cmd_data.help.cfg_component->plugin_name->str); - fprintf(stderr, "%s%sCannot find plugin %s%s%s\n", - bt_common_color_bold(), bt_common_color_fg_red(), - bt_common_color_fg_blue(), - cfg->cmd_data.help.cfg_component->plugin_name->str, - bt_common_color_reset()); - ret = -1; - goto end; - } - - print_plugin_info(plugin); - printf(" %sSource component classes%s: %d\n", - bt_common_color_bold(), - bt_common_color_reset(), - (int) bt_plugin_get_source_component_class_count(plugin)); - printf(" %sFilter component classes%s: %d\n", - bt_common_color_bold(), - bt_common_color_reset(), - (int) bt_plugin_get_filter_component_class_count(plugin)); - printf(" %sSink component classes%s: %d\n", - bt_common_color_bold(), - bt_common_color_reset(), - (int) bt_plugin_get_sink_component_class_count(plugin)); - - if (strlen(cfg->cmd_data.help.cfg_component->comp_cls_name->str) == 0) { - /* Plugin help only */ - goto end; - } - - needed_comp_cls = find_component_class( - cfg->cmd_data.help.cfg_component->plugin_name->str, - cfg->cmd_data.help.cfg_component->comp_cls_name->str, - cfg->cmd_data.help.cfg_component->type); - if (!needed_comp_cls) { - BT_LOGE("Cannot find component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d", - cfg->cmd_data.help.cfg_component->plugin_name->str, - cfg->cmd_data.help.cfg_component->comp_cls_name->str, - cfg->cmd_data.help.cfg_component->type); - fprintf(stderr, "\n%s%sCannot find component class %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, - cfg->cmd_data.help.cfg_component->plugin_name->str, - cfg->cmd_data.help.cfg_component->comp_cls_name->str, - cfg->cmd_data.help.cfg_component->type); - fprintf(stderr, "\n"); - ret = -1; - goto end; - } - - printf("\n"); - print_component_class_help( - cfg->cmd_data.help.cfg_component->plugin_name->str, - needed_comp_cls); - -end: - bt_component_class_put_ref(needed_comp_cls); - bt_plugin_put_ref(plugin); - return ret; -} - -typedef void *(* plugin_borrow_comp_cls_by_index_func_t)(const bt_plugin *, - uint64_t); -typedef const bt_component_class *(* spec_comp_cls_borrow_comp_cls_func_t)( - void *); - -void cmd_list_plugins_print_component_classes(const bt_plugin *plugin, - const char *cc_type_name, uint64_t count, - plugin_borrow_comp_cls_by_index_func_t borrow_comp_cls_by_index_func, - spec_comp_cls_borrow_comp_cls_func_t spec_comp_cls_borrow_comp_cls_func) -{ - uint64_t i; - - if (count == 0) { - printf(" %s%s component classes%s: (none)\n", - bt_common_color_bold(), - cc_type_name, - bt_common_color_reset()); - goto end; - } else { - printf(" %s%s component classes%s:\n", - bt_common_color_bold(), - cc_type_name, - bt_common_color_reset()); - } - - for (i = 0; i < count; i++) { - const bt_component_class *comp_class = - spec_comp_cls_borrow_comp_cls_func( - borrow_comp_cls_by_index_func(plugin, i)); - const char *comp_class_name = - bt_component_class_get_name(comp_class); - const char *comp_class_description = - bt_component_class_get_description(comp_class); - bt_component_class_type type = - bt_component_class_get_type(comp_class); - - printf(" "); - print_plugin_comp_cls_opt(stdout, - bt_plugin_get_name(plugin), comp_class_name, - type); - - if (comp_class_description) { - printf(": %s", comp_class_description); - } - - printf("\n"); - } - -end: - return; -} - -static -int cmd_list_plugins(struct bt_config *cfg) -{ - int ret = 0; - int plugins_count, component_classes_count = 0, i; - - printf("From the following plugin paths:\n\n"); - print_value(stdout, cfg->plugin_paths, 2); - printf("\n"); - plugins_count = loaded_plugins->len; - if (plugins_count == 0) { - printf("No plugins found.\n"); - goto end; - } - - for (i = 0; i < plugins_count; i++) { - const bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); - - component_classes_count += - bt_plugin_get_source_component_class_count(plugin) + - bt_plugin_get_filter_component_class_count(plugin) + - bt_plugin_get_sink_component_class_count(plugin); - } - - printf("Found %s%d%s component classes in %s%d%s plugins.\n", - bt_common_color_bold(), - component_classes_count, - bt_common_color_reset(), - bt_common_color_bold(), - plugins_count, - bt_common_color_reset()); - - for (i = 0; i < plugins_count; i++) { - const bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); - - printf("\n"); - print_plugin_info(plugin); - cmd_list_plugins_print_component_classes(plugin, "Source", - bt_plugin_get_source_component_class_count(plugin), - (plugin_borrow_comp_cls_by_index_func_t) - bt_plugin_borrow_source_component_class_by_index_const, - (spec_comp_cls_borrow_comp_cls_func_t) - bt_component_class_source_as_component_class); - cmd_list_plugins_print_component_classes(plugin, "Filter", - bt_plugin_get_filter_component_class_count(plugin), - (plugin_borrow_comp_cls_by_index_func_t) - bt_plugin_borrow_filter_component_class_by_index_const, - (spec_comp_cls_borrow_comp_cls_func_t) - bt_component_class_filter_as_component_class); - cmd_list_plugins_print_component_classes(plugin, "Sink", - bt_plugin_get_sink_component_class_count(plugin), - (plugin_borrow_comp_cls_by_index_func_t) - bt_plugin_borrow_sink_component_class_by_index_const, - (spec_comp_cls_borrow_comp_cls_func_t) - bt_component_class_sink_as_component_class); - } - -end: - return ret; -} - -static -int cmd_print_lttng_live_sessions(struct bt_config *cfg) -{ - int ret = 0; - const bt_component_class *comp_cls = NULL; - const bt_value *results = NULL; - bt_value *params = NULL; - const bt_value *map = NULL; - const bt_value *v = NULL; - static const char * const plugin_name = "ctf"; - static const char * const comp_cls_name = "lttng-live"; - static const bt_component_class_type comp_cls_type = - BT_COMPONENT_CLASS_TYPE_SOURCE; - int64_t array_size, i; - const char *fail_reason = NULL; - FILE *out_stream = stdout; - - BT_ASSERT(cfg->cmd_data.print_lttng_live_sessions.url); - comp_cls = find_component_class(plugin_name, comp_cls_name, - comp_cls_type); - if (!comp_cls) { - BT_LOGE("Cannot find component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d", - plugin_name, comp_cls_name, - BT_COMPONENT_CLASS_TYPE_SOURCE); - fprintf(stderr, "%s%sCannot find component class %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, plugin_name, - comp_cls_name, comp_cls_type); - fprintf(stderr, "\n"); - goto error; - } - - params = bt_value_map_create(); - if (!params) { - goto error; - } - - ret = bt_value_map_insert_string_entry(params, "url", - cfg->cmd_data.print_lttng_live_sessions.url->str); - if (ret) { - goto error; - } - - ret = query(comp_cls, "sessions", params, - &results, &fail_reason); - if (ret) { - goto failed; - } - - BT_ASSERT(results); - - if (!bt_value_is_array(results)) { - BT_LOGE_STR("Expecting an array for sessions query."); - fprintf(stderr, "%s%sUnexpected type returned by session query%s\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - goto error; - } - - if (cfg->cmd_data.print_lttng_live_sessions.output_path->len > 0) { - out_stream = - fopen(cfg->cmd_data.print_lttng_live_sessions.output_path->str, - "w"); - if (!out_stream) { - ret = -1; - BT_LOGE_ERRNO("Cannot open file for writing", - ": path=\"%s\"", - cfg->cmd_data.print_lttng_live_sessions.output_path->str); - goto end; - } - } - - array_size = bt_value_array_get_size(results); - for (i = 0; i < array_size; i++) { - const char *url_text; - int64_t timer_us, streams, clients; - - map = bt_value_array_borrow_element_by_index_const(results, i); - if (!map) { - BT_LOGE_STR("Unexpected empty array entry."); - goto error; - } - if (!bt_value_is_map(map)) { - BT_LOGE_STR("Unexpected entry type."); - goto error; - } - - v = bt_value_map_borrow_entry_value_const(map, "url"); - if (!v) { - BT_LOGE_STR("Unexpected empty array \"url\" entry."); - goto error; - } - url_text = bt_value_string_get(v); - fprintf(out_stream, "%s", url_text); - v = bt_value_map_borrow_entry_value_const(map, "timer-us"); - if (!v) { - BT_LOGE_STR("Unexpected empty array \"timer-us\" entry."); - goto error; - } - timer_us = bt_value_signed_integer_get(v); - fprintf(out_stream, " (timer = %" PRIu64 ", ", timer_us); - v = bt_value_map_borrow_entry_value_const(map, "stream-count"); - if (!v) { - BT_LOGE_STR("Unexpected empty array \"stream-count\" entry."); - goto error; - } - streams = bt_value_signed_integer_get(v); - fprintf(out_stream, "%" PRIu64 " stream(s), ", streams); - v = bt_value_map_borrow_entry_value_const(map, "client-count"); - if (!v) { - BT_LOGE_STR("Unexpected empty array \"client-count\" entry."); - goto error; - } - clients = bt_value_signed_integer_get(v); - fprintf(out_stream, "%" PRIu64 " client(s) connected)\n", clients); - } - - goto end; - -failed: - BT_LOGE("Failed to query for sessions: %s", fail_reason); - fprintf(stderr, "%s%sFailed to request sessions: %s%s\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - fail_reason, - bt_common_color_reset()); - -error: - ret = -1; - -end: - bt_value_put_ref(results); - bt_value_put_ref(params); - bt_component_class_put_ref(comp_cls); - - if (out_stream && out_stream != stdout) { - int fclose_ret = fclose(out_stream); - - if (fclose_ret) { - BT_LOGE_ERRNO("Cannot close file stream", - ": path=\"%s\"", - cfg->cmd_data.print_lttng_live_sessions.output_path->str); - } - } - - return ret; -} - -static -int cmd_print_ctf_metadata(struct bt_config *cfg) -{ - int ret = 0; - const bt_component_class *comp_cls = NULL; - const bt_value *results = NULL; - bt_value *params = NULL; - const bt_value *metadata_text_value = NULL; - const char *metadata_text = NULL; - static const char * const plugin_name = "ctf"; - static const char * const comp_cls_name = "fs"; - static const bt_component_class_type comp_cls_type = - BT_COMPONENT_CLASS_TYPE_SOURCE; - const char *fail_reason = NULL; - FILE *out_stream = stdout; - - BT_ASSERT(cfg->cmd_data.print_ctf_metadata.path); - comp_cls = find_component_class(plugin_name, comp_cls_name, - comp_cls_type); - if (!comp_cls) { - BT_LOGE("Cannot find component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d", - plugin_name, comp_cls_name, - BT_COMPONENT_CLASS_TYPE_SOURCE); - fprintf(stderr, "%s%sCannot find component class %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, plugin_name, - comp_cls_name, comp_cls_type); - fprintf(stderr, "\n"); - ret = -1; - goto end; - } - - params = bt_value_map_create(); - if (!params) { - ret = -1; - goto end; - } - - ret = bt_value_map_insert_string_entry(params, "path", - cfg->cmd_data.print_ctf_metadata.path->str); - if (ret) { - ret = -1; - goto end; - } - - ret = query(comp_cls, "metadata-info", - params, &results, &fail_reason); - if (ret) { - goto failed; - } - - metadata_text_value = bt_value_map_borrow_entry_value_const(results, - "text"); - if (!metadata_text_value) { - BT_LOGE_STR("Cannot find `text` string value in the resulting metadata info object."); - ret = -1; - goto end; - } - - metadata_text = bt_value_string_get(metadata_text_value); - - if (cfg->cmd_data.print_ctf_metadata.output_path->len > 0) { - out_stream = - fopen(cfg->cmd_data.print_ctf_metadata.output_path->str, - "w"); - if (!out_stream) { - ret = -1; - BT_LOGE_ERRNO("Cannot open file for writing", - ": path=\"%s\"", - cfg->cmd_data.print_ctf_metadata.output_path->str); - goto end; - } - } - - ret = fprintf(out_stream, "%s\n", metadata_text); - if (ret < 0) { - BT_LOGE("Cannot write whole metadata text to output stream: " - "ret=%d", ret); - goto end; - } - - ret = 0; - - goto end; - -failed: - ret = -1; - BT_LOGE("Failed to query for metadata info: %s", fail_reason); - fprintf(stderr, "%s%sFailed to request metadata info: %s%s\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - fail_reason, - bt_common_color_reset()); - -end: - bt_value_put_ref(results); - bt_value_put_ref(params); - bt_component_class_put_ref(comp_cls); - - if (out_stream && out_stream != stdout) { - int fclose_ret = fclose(out_stream); - - if (fclose_ret) { - BT_LOGE_ERRNO("Cannot close file stream", - ": path=\"%s\"", - cfg->cmd_data.print_ctf_metadata.output_path->str); - } - } - - return ret; -} - -struct port_id { - char *instance_name; - char *port_name; -}; - -struct trace_range { - uint64_t intersection_range_begin_ns; - uint64_t intersection_range_end_ns; -}; - -static -guint port_id_hash(gconstpointer v) -{ - const struct port_id *id = v; - - BT_ASSERT(id->instance_name); - BT_ASSERT(id->port_name); - - return g_str_hash(id->instance_name) ^ g_str_hash(id->port_name); -} - -static -gboolean port_id_equal(gconstpointer v1, gconstpointer v2) -{ - const struct port_id *id1 = v1; - const struct port_id *id2 = v2; - - return !strcmp(id1->instance_name, id2->instance_name) && - !strcmp(id1->port_name, id2->port_name); -} - -static -void port_id_destroy(gpointer data) -{ - struct port_id *id = data; - - free(id->instance_name); - free(id->port_name); - free(id); -} - -static -void trace_range_destroy(gpointer data) -{ - free(data); -} - -struct cmd_run_ctx { - /* Owned by this */ - GHashTable *src_components; - - /* Owned by this */ - GHashTable *flt_components; - - /* Owned by this */ - GHashTable *sink_components; - - /* Owned by this */ - bt_graph *graph; - - /* Weak */ - struct bt_config *cfg; - - bool connect_ports; - - bool stream_intersection_mode; - - /* - * Association of struct port_id -> struct trace_range. - */ - GHashTable *intersections; -}; - -/* Returns a timestamp of the form "(-)s.ns" */ -static -char *s_from_ns(int64_t ns) -{ - int ret; - char *s_ret = NULL; - bool is_negative; - int64_t ts_sec_abs, ts_nsec_abs; - int64_t ts_sec = ns / NSEC_PER_SEC; - int64_t ts_nsec = ns % NSEC_PER_SEC; - - if (ts_sec >= 0 && ts_nsec >= 0) { - is_negative = false; - ts_sec_abs = ts_sec; - ts_nsec_abs = ts_nsec; - } else if (ts_sec > 0 && ts_nsec < 0) { - is_negative = false; - ts_sec_abs = ts_sec - 1; - ts_nsec_abs = NSEC_PER_SEC + ts_nsec; - } else if (ts_sec == 0 && ts_nsec < 0) { - is_negative = true; - ts_sec_abs = ts_sec; - ts_nsec_abs = -ts_nsec; - } else if (ts_sec < 0 && ts_nsec > 0) { - is_negative = true; - ts_sec_abs = -(ts_sec + 1); - ts_nsec_abs = NSEC_PER_SEC - ts_nsec; - } else if (ts_sec < 0 && ts_nsec == 0) { - is_negative = true; - ts_sec_abs = -ts_sec; - ts_nsec_abs = ts_nsec; - } else { /* (ts_sec < 0 && ts_nsec < 0) */ - is_negative = true; - ts_sec_abs = -ts_sec; - ts_nsec_abs = -ts_nsec; - } - - ret = asprintf(&s_ret, "%s%" PRId64 ".%09" PRId64, - is_negative ? "-" : "", ts_sec_abs, ts_nsec_abs); - if (ret < 0) { - s_ret = NULL; - } - return s_ret; -} - -static -int cmd_run_ctx_connect_upstream_port_to_downstream_component( - struct cmd_run_ctx *ctx, - const bt_component *upstream_comp, - const bt_port_output *out_upstream_port, - struct bt_config_connection *cfg_conn) -{ - typedef uint64_t (*input_port_count_func_t)(void *); - typedef const bt_port_input *(*borrow_input_port_by_index_func_t)( - const void *, uint64_t); - const bt_port *upstream_port = - bt_port_output_as_port_const(out_upstream_port); - - int ret = 0; - GQuark downstreamp_comp_name_quark; - void *downstream_comp; - uint64_t downstream_port_count; - uint64_t i; - input_port_count_func_t port_count_fn; - borrow_input_port_by_index_func_t port_by_index_fn; - bt_graph_status status = BT_GRAPH_STATUS_ERROR; - bool insert_trimmer = false; - bt_value *trimmer_params = NULL; - char *intersection_begin = NULL; - char *intersection_end = NULL; - const bt_component_filter *trimmer = NULL; - const bt_component_class_filter *trimmer_class = NULL; - const bt_port_input *trimmer_input = NULL; - const bt_port_output *trimmer_output = NULL; - - if (ctx->intersections && - bt_component_get_class_type(upstream_comp) == - BT_COMPONENT_CLASS_TYPE_SOURCE) { - struct trace_range *range; - struct port_id port_id = { - .instance_name = (char *) bt_component_get_name(upstream_comp), - .port_name = (char *) bt_port_get_name(upstream_port) - }; - - if (!port_id.instance_name || !port_id.port_name) { - goto error; - } - - range = (struct trace_range *) g_hash_table_lookup( - ctx->intersections, &port_id); - if (range) { - bt_value_status status; - - intersection_begin = s_from_ns( - range->intersection_range_begin_ns); - intersection_end = s_from_ns( - range->intersection_range_end_ns); - if (!intersection_begin || !intersection_end) { - BT_LOGE_STR("Cannot create trimmer argument timestamp string."); - goto error; - } - - insert_trimmer = true; - trimmer_params = bt_value_map_create(); - if (!trimmer_params) { - goto error; - } - - status = bt_value_map_insert_string_entry( - trimmer_params, "begin", intersection_begin); - if (status != BT_VALUE_STATUS_OK) { - goto error; - } - status = bt_value_map_insert_string_entry( - trimmer_params, - "end", intersection_end); - if (status != BT_VALUE_STATUS_OK) { - goto error; - } - } - - trimmer_class = find_filter_component_class("utils", "trimmer"); - if (!trimmer_class) { - goto error; - } - } - - BT_LOGI("Connecting upstream port to the next available downstream port: " - "upstream-port-addr=%p, upstream-port-name=\"%s\", " - "downstream-comp-name=\"%s\", conn-arg=\"%s\"", - upstream_port, bt_port_get_name(upstream_port), - cfg_conn->downstream_comp_name->str, - cfg_conn->arg->str); - downstreamp_comp_name_quark = g_quark_from_string( - cfg_conn->downstream_comp_name->str); - BT_ASSERT(downstreamp_comp_name_quark > 0); - downstream_comp = g_hash_table_lookup(ctx->flt_components, - GUINT_TO_POINTER(downstreamp_comp_name_quark)); - port_count_fn = (input_port_count_func_t) - bt_component_filter_get_input_port_count; - port_by_index_fn = (borrow_input_port_by_index_func_t) - bt_component_filter_borrow_input_port_by_index_const; - - if (!downstream_comp) { - downstream_comp = g_hash_table_lookup(ctx->sink_components, - GUINT_TO_POINTER(downstreamp_comp_name_quark)); - port_count_fn = (input_port_count_func_t) - bt_component_sink_get_input_port_count; - port_by_index_fn = (borrow_input_port_by_index_func_t) - bt_component_sink_borrow_input_port_by_index_const; - } - - if (!downstream_comp) { - BT_LOGE("Cannot find downstream component: comp-name=\"%s\", " - "conn-arg=\"%s\"", cfg_conn->downstream_comp_name->str, - cfg_conn->arg->str); - fprintf(stderr, "Cannot create connection: cannot find downstream component: %s\n", - cfg_conn->arg->str); - goto error; - } - - downstream_port_count = port_count_fn(downstream_comp); - - for (i = 0; i < downstream_port_count; i++) { - const bt_port_input *in_downstream_port = - port_by_index_fn(downstream_comp, i); - const bt_port *downstream_port = - bt_port_input_as_port_const(in_downstream_port); - const char *upstream_port_name; - const char *downstream_port_name; - - BT_ASSERT(downstream_port); - - /* Skip port if it's already connected. */ - if (bt_port_is_connected(downstream_port)) { - BT_LOGD("Skipping downstream port: already connected: " - "port-addr=%p, port-name=\"%s\"", - downstream_port, - bt_port_get_name(downstream_port)); - continue; - } - - downstream_port_name = bt_port_get_name(downstream_port); - BT_ASSERT(downstream_port_name); - upstream_port_name = bt_port_get_name(upstream_port); - BT_ASSERT(upstream_port_name); - - if (!bt_common_star_glob_match( - cfg_conn->downstream_port_glob->str, SIZE_MAX, - downstream_port_name, SIZE_MAX)) { - continue; - } - - if (insert_trimmer) { - /* - * In order to insert the trimmer between the - * two components that were being connected, we - * create a connection configuration entry which - * describes a connection from the trimmer's - * output to the original input that was being - * connected. - * - * Hence, the creation of the trimmer will cause - * the graph "new port" listener to establish - * all downstream connections as its output port - * is connected. We will then establish the - * connection between the original upstream - * source and the trimmer. - */ - char *trimmer_name = NULL; - bt_graph_status graph_status; - - ret = asprintf(&trimmer_name, - "stream-intersection-trimmer-%s", - upstream_port_name); - if (ret < 0) { - goto error; - } - ret = 0; - - ctx->connect_ports = false; - graph_status = bt_graph_add_filter_component( - ctx->graph, trimmer_class, trimmer_name, - trimmer_params, &trimmer); - free(trimmer_name); - if (graph_status != BT_GRAPH_STATUS_OK) { - goto error; - } - BT_ASSERT(trimmer); - - trimmer_input = - bt_component_filter_borrow_input_port_by_index_const( - trimmer, 0); - if (!trimmer_input) { - goto error; - } - trimmer_output = - bt_component_filter_borrow_output_port_by_index_const( - trimmer, 0); - if (!trimmer_output) { - goto error; - } - - /* - * Replace the current downstream port by the trimmer's - * upstream port. - */ - in_downstream_port = trimmer_input; - downstream_port = - bt_port_input_as_port_const(in_downstream_port); - downstream_port_name = bt_port_get_name( - downstream_port); - BT_ASSERT(downstream_port_name); - } - - /* We have a winner! */ - status = bt_graph_connect_ports(ctx->graph, - out_upstream_port, in_downstream_port, NULL); - downstream_port = NULL; - switch (status) { - case BT_GRAPH_STATUS_OK: - break; - case BT_GRAPH_STATUS_CANCELED: - BT_LOGI_STR("Graph was canceled by user."); - status = BT_GRAPH_STATUS_OK; - break; - case BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: - BT_LOGE("A component refused a connection to one of its ports: " - "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " - "upstream-port-addr=%p, upstream-port-name=\"%s\", " - "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " - "downstream-port-addr=%p, downstream-port-name=\"%s\", " - "conn-arg=\"%s\"", - upstream_comp, bt_component_get_name(upstream_comp), - upstream_port, bt_port_get_name(upstream_port), - downstream_comp, cfg_conn->downstream_comp_name->str, - downstream_port, downstream_port_name, - cfg_conn->arg->str); - fprintf(stderr, - "A component refused a connection to one of its ports (`%s` to `%s`): %s\n", - bt_port_get_name(upstream_port), - downstream_port_name, - cfg_conn->arg->str); - break; - default: - BT_LOGE("Cannot create connection: graph refuses to connect ports: " - "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " - "upstream-port-addr=%p, upstream-port-name=\"%s\", " - "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " - "downstream-port-addr=%p, downstream-port-name=\"%s\", " - "conn-arg=\"%s\"", - upstream_comp, bt_component_get_name(upstream_comp), - upstream_port, bt_port_get_name(upstream_port), - downstream_comp, cfg_conn->downstream_comp_name->str, - downstream_port, downstream_port_name, - cfg_conn->arg->str); - fprintf(stderr, - "Cannot create connection: graph refuses to connect ports (`%s` to `%s`): %s\n", - bt_port_get_name(upstream_port), - downstream_port_name, - cfg_conn->arg->str); - goto error; - } - - BT_LOGI("Connected component ports: " - "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " - "upstream-port-addr=%p, upstream-port-name=\"%s\", " - "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " - "downstream-port-addr=%p, downstream-port-name=\"%s\", " - "conn-arg=\"%s\"", - upstream_comp, bt_component_get_name(upstream_comp), - upstream_port, bt_port_get_name(upstream_port), - downstream_comp, cfg_conn->downstream_comp_name->str, - downstream_port, downstream_port_name, - cfg_conn->arg->str); - - if (insert_trimmer) { - /* - * The first connection, from the source to the trimmer, - * has been done. We now connect the trimmer to the - * original downstream port. - */ - ret = cmd_run_ctx_connect_upstream_port_to_downstream_component( - ctx, - bt_component_filter_as_component_const(trimmer), - trimmer_output, cfg_conn); - if (ret) { - goto error; - } - ctx->connect_ports = true; - } - - /* - * We found a matching downstream port: the search is - * over. - */ - goto end; - } - - /* No downstream port found */ - BT_LOGE("Cannot create connection: cannot find a matching downstream port for upstream port: " - "upstream-port-addr=%p, upstream-port-name=\"%s\", " - "downstream-comp-name=\"%s\", conn-arg=\"%s\"", - upstream_port, bt_port_get_name(upstream_port), - cfg_conn->downstream_comp_name->str, - cfg_conn->arg->str); - fprintf(stderr, - "Cannot create connection: cannot find a matching downstream port for upstream port `%s`: %s\n", - bt_port_get_name(upstream_port), cfg_conn->arg->str); - -error: - ret = -1; - -end: - free(intersection_begin); - free(intersection_end); - BT_VALUE_PUT_REF_AND_RESET(trimmer_params); - BT_COMPONENT_CLASS_FILTER_PUT_REF_AND_RESET(trimmer_class); - BT_COMPONENT_FILTER_PUT_REF_AND_RESET(trimmer); - return ret; -} - -static -int cmd_run_ctx_connect_upstream_port(struct cmd_run_ctx *ctx, - const bt_port_output *upstream_port) -{ - int ret = 0; - const char *upstream_port_name; - const char *upstream_comp_name; - const bt_component *upstream_comp = NULL; - size_t i; - - BT_ASSERT(ctx); - BT_ASSERT(upstream_port); - upstream_port_name = bt_port_get_name( - bt_port_output_as_port_const(upstream_port)); - BT_ASSERT(upstream_port_name); - upstream_comp = bt_port_borrow_component_const( - bt_port_output_as_port_const(upstream_port)); - if (!upstream_comp) { - BT_LOGW("Upstream port to connect is not part of a component: " - "port-addr=%p, port-name=\"%s\"", - upstream_port, upstream_port_name); - ret = -1; - goto end; - } - - upstream_comp_name = bt_component_get_name(upstream_comp); - BT_ASSERT(upstream_comp_name); - BT_LOGI("Connecting upstream port: comp-addr=%p, comp-name=\"%s\", " - "port-addr=%p, port-name=\"%s\"", - upstream_comp, upstream_comp_name, - upstream_port, upstream_port_name); - - for (i = 0; i < ctx->cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *cfg_conn = - g_ptr_array_index( - ctx->cfg->cmd_data.run.connections, i); - - if (strcmp(cfg_conn->upstream_comp_name->str, - upstream_comp_name)) { - continue; - } - - if (!bt_common_star_glob_match( - cfg_conn->upstream_port_glob->str, - SIZE_MAX, upstream_port_name, SIZE_MAX)) { - continue; - } - - ret = cmd_run_ctx_connect_upstream_port_to_downstream_component( - ctx, upstream_comp, upstream_port, cfg_conn); - if (ret) { - BT_LOGE("Cannot connect upstream port: " - "port-addr=%p, port-name=\"%s\"", - upstream_port, - upstream_port_name); - fprintf(stderr, - "Cannot connect port `%s` of component `%s` to a downstream port: %s\n", - upstream_port_name, - upstream_comp_name, - cfg_conn->arg->str); - goto error; - } - goto end; - } - - BT_LOGE("Cannot connect upstream port: port does not match any connection argument: " - "port-addr=%p, port-name=\"%s\"", upstream_port, - upstream_port_name); - fprintf(stderr, - "Cannot create connection: upstream port `%s` does not match any connection\n", - upstream_port_name); - -error: - ret = -1; - -end: - return ret; -} - -static -bt_graph_listener_status -graph_output_port_added_listener(struct cmd_run_ctx *ctx, - const bt_port_output *out_port) -{ - const bt_component *comp; - const bt_port *port = bt_port_output_as_port_const(out_port); - bt_graph_listener_status ret = BT_GRAPH_LISTENER_STATUS_OK; - - comp = bt_port_borrow_component_const(port); - BT_LOGI("Port added to a graph's component: comp-addr=%p, " - "comp-name=\"%s\", port-addr=%p, port-name=\"%s\"", - comp, comp ? bt_component_get_name(comp) : "", - port, bt_port_get_name(port)); - - if (!ctx->connect_ports) { - goto end; - } - - if (!comp) { - BT_LOGW_STR("Port has no component."); - goto end; - } - - if (bt_port_is_connected(port)) { - BT_LOGW_STR("Port is already connected."); - goto end; - } - - if (cmd_run_ctx_connect_upstream_port(ctx, out_port)) { - BT_LOGF_STR("Cannot connect upstream port."); - fprintf(stderr, "Added port could not be connected: aborting\n"); - ret = BT_GRAPH_LISTENER_STATUS_ERROR; - goto end; - } - -end: - return ret; -} - -static -bt_graph_listener_status graph_source_output_port_added_listener( - const bt_component_source *component, - const bt_port_output *port, void *data) -{ - return graph_output_port_added_listener(data, port); -} - -static -bt_graph_listener_status graph_filter_output_port_added_listener( - const bt_component_filter *component, - const bt_port_output *port, void *data) -{ - return graph_output_port_added_listener(data, port); -} - -static -void cmd_run_ctx_destroy(struct cmd_run_ctx *ctx) -{ - if (!ctx) { - return; - } - - if (ctx->src_components) { - g_hash_table_destroy(ctx->src_components); - ctx->src_components = NULL; - } - - if (ctx->flt_components) { - g_hash_table_destroy(ctx->flt_components); - ctx->flt_components = NULL; - } - - if (ctx->sink_components) { - g_hash_table_destroy(ctx->sink_components); - ctx->sink_components = NULL; - } - - if (ctx->intersections) { - g_hash_table_destroy(ctx->intersections); - ctx->intersections = NULL; - } - - BT_GRAPH_PUT_REF_AND_RESET(ctx->graph); - the_graph = NULL; - ctx->cfg = NULL; -} - -static -int cmd_run_ctx_init(struct cmd_run_ctx *ctx, struct bt_config *cfg) -{ - int ret = 0; - bt_graph_status status; - - ctx->cfg = cfg; - ctx->connect_ports = false; - ctx->src_components = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); - if (!ctx->src_components) { - goto error; - } - - ctx->flt_components = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); - if (!ctx->flt_components) { - goto error; - } - - ctx->sink_components = g_hash_table_new_full(g_direct_hash, - g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); - if (!ctx->sink_components) { - goto error; - } - - if (cfg->cmd_data.run.stream_intersection_mode) { - ctx->stream_intersection_mode = true; - ctx->intersections = g_hash_table_new_full(port_id_hash, - port_id_equal, port_id_destroy, trace_range_destroy); - if (!ctx->intersections) { - goto error; - } - } - - ctx->graph = bt_graph_create(); - if (!ctx->graph) { - goto error; - } - - the_graph = ctx->graph; - status = bt_graph_add_source_component_output_port_added_listener( - ctx->graph, graph_source_output_port_added_listener, NULL, ctx, - NULL); - if (status != BT_GRAPH_STATUS_OK) { - BT_LOGE_STR("Cannot add \"port added\" listener to graph."); - goto error; - } - - status = bt_graph_add_filter_component_output_port_added_listener( - ctx->graph, graph_filter_output_port_added_listener, NULL, ctx, - NULL); - if (status != BT_GRAPH_STATUS_OK) { - BT_LOGE_STR("Cannot add \"port added\" listener to graph."); - goto error; - } - - goto end; - -error: - cmd_run_ctx_destroy(ctx); - ret = -1; - -end: - return ret; -} - -static -int set_stream_intersections(struct cmd_run_ctx *ctx, - struct bt_config_component *cfg_comp, - const bt_component_class_source *src_comp_cls) -{ - int ret = 0; - uint64_t trace_idx; - int64_t trace_count; - const char *path = NULL; - const bt_value *query_result = NULL; - const bt_value *trace_info = NULL; - const bt_value *intersection_range = NULL; - const bt_value *intersection_begin = NULL; - const bt_value *intersection_end = NULL; - const bt_value *stream_infos = NULL; - const bt_value *stream_info = NULL; - struct port_id *port_id = NULL; - struct trace_range *trace_range = NULL; - const char *fail_reason = NULL; - const bt_component_class *comp_cls = - bt_component_class_source_as_component_class_const(src_comp_cls); - - ret = query(comp_cls, "trace-info", - cfg_comp->params, &query_result, - &fail_reason); - if (ret) { - BT_LOGD("Component class does not support the `trace-info` query: %s: " - "comp-class-name=\"%s\"", fail_reason, - bt_component_class_get_name(comp_cls)); - ret = -1; - goto error; - } - - BT_ASSERT(query_result); - - if (!bt_value_is_array(query_result)) { - BT_LOGD("Unexpected format of \'trace-info\' query result: " - "component-class-name=%s", - bt_component_class_get_name(comp_cls)); - ret = -1; - goto error; - } - - trace_count = bt_value_array_get_size(query_result); - if (trace_count < 0) { - ret = -1; - goto error; - } - - for (trace_idx = 0; trace_idx < trace_count; trace_idx++) { - int64_t begin, end; - uint64_t stream_idx; - int64_t stream_count; - - trace_info = bt_value_array_borrow_element_by_index_const( - query_result, trace_idx); - if (!trace_info || !bt_value_is_map(trace_info)) { - ret = -1; - BT_LOGD_STR("Cannot retrieve trace from query result."); - goto error; - } - - intersection_range = bt_value_map_borrow_entry_value_const( - trace_info, "intersection-range-ns"); - if (!intersection_range) { - ret = -1; - BT_LOGD_STR("Cannot retrieve \'intersetion-range-ns\' field from query result."); - goto error; - } - - intersection_begin = bt_value_map_borrow_entry_value_const(intersection_range, - "begin"); - if (!intersection_begin) { - ret = -1; - BT_LOGD_STR("Cannot retrieve intersection-range-ns \'begin\' field from query result."); - goto error; - } - - intersection_end = bt_value_map_borrow_entry_value_const(intersection_range, - "end"); - if (!intersection_end) { - ret = -1; - BT_LOGD_STR("Cannot retrieve intersection-range-ns \'end\' field from query result."); - goto error; - } - - begin = bt_value_signed_integer_get(intersection_begin); - end = bt_value_signed_integer_get(intersection_end); - - if (begin < 0 || end < 0 || end < begin) { - BT_LOGW("Invalid trace stream intersection values: " - "intersection-range-ns:begin=%" PRId64 - ", intersection-range-ns:end=%" PRId64, - begin, end); - ret = -1; - goto error; - } - - stream_infos = bt_value_map_borrow_entry_value_const(trace_info, - "streams"); - if (!stream_infos || !bt_value_is_array(stream_infos)) { - ret = -1; - BT_LOGD_STR("Cannot retrieve stream information from trace in query result."); - goto error; - } - - stream_count = bt_value_array_get_size(stream_infos); - if (stream_count < 0) { - ret = -1; - goto error; - } - - for (stream_idx = 0; stream_idx < stream_count; stream_idx++) { - const bt_value *port_name; - - port_id = g_new0(struct port_id, 1); - if (!port_id) { - ret = -1; - BT_LOGE_STR("Cannot allocate memory for port_id structure."); - goto error; - } - port_id->instance_name = strdup(cfg_comp->instance_name->str); - if (!port_id->instance_name) { - ret = -1; - BT_LOGE_STR("Cannot allocate memory for port_id component instance name."); - goto error; - } - - trace_range = g_new0(struct trace_range, 1); - if (!trace_range) { - ret = -1; - BT_LOGE_STR("Cannot allocate memory for trace_range structure."); - goto error; - } - trace_range->intersection_range_begin_ns = begin; - trace_range->intersection_range_end_ns = end; - - stream_info = bt_value_array_borrow_element_by_index_const( - stream_infos, stream_idx); - if (!stream_info || !bt_value_is_map(stream_info)) { - ret = -1; - BT_LOGD_STR("Cannot retrieve stream informations from trace in query result."); - goto error; - } - - port_name = bt_value_map_borrow_entry_value_const(stream_info, "port-name"); - if (!port_name || !bt_value_is_string(port_name)) { - ret = -1; - BT_LOGD_STR("Cannot retrieve port name in query result."); - goto error; - } - - port_id->port_name = g_strdup(bt_value_string_get(port_name)); - if (!port_id->port_name) { - ret = -1; - BT_LOGE_STR("Cannot allocate memory for port_id port_name."); - goto error; - } - - BT_LOGD("Inserting stream intersection "); - - g_hash_table_insert(ctx->intersections, port_id, trace_range); - - port_id = NULL; - trace_range = NULL; - } - } - - goto end; - -error: - fprintf(stderr, "%s%sCannot determine stream intersection of trace at path \'%s\'.%s\n", - bt_common_color_bold(), - bt_common_color_fg_yellow(), - path ? path : "(unknown)", - bt_common_color_reset()); -end: - bt_value_put_ref(query_result); - g_free(port_id); - g_free(trace_range); - return ret; -} - -static -int cmd_run_ctx_create_components_from_config_components( - struct cmd_run_ctx *ctx, GPtrArray *cfg_components) -{ - size_t i; - const void *comp_cls = NULL; - const void *comp = NULL; - int ret = 0; - - for (i = 0; i < cfg_components->len; i++) { - struct bt_config_component *cfg_comp = - g_ptr_array_index(cfg_components, i); - GQuark quark; - - switch (cfg_comp->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - comp_cls = find_source_component_class( - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str); - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - comp_cls = find_filter_component_class( - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str); - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - comp_cls = find_sink_component_class( - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str); - break; - default: - abort(); - } - - if (!comp_cls) { - BT_LOGE("Cannot find component class: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d", - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str, - cfg_comp->type); - fprintf(stderr, "%s%sCannot find component class %s", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - print_plugin_comp_cls_opt(stderr, - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str, - cfg_comp->type); - fprintf(stderr, "\n"); - goto error; - } - - switch (cfg_comp->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - ret = bt_graph_add_source_component(ctx->graph, - comp_cls, cfg_comp->instance_name->str, - cfg_comp->params, - (void *) &comp); - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - ret = bt_graph_add_filter_component(ctx->graph, - comp_cls, cfg_comp->instance_name->str, - cfg_comp->params, - (void *) &comp); - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - ret = bt_graph_add_sink_component(ctx->graph, - comp_cls, cfg_comp->instance_name->str, - cfg_comp->params, - (void *) &comp); - break; - default: - abort(); - } - - if (ret) { - BT_LOGE("Cannot create component: plugin-name=\"%s\", " - "comp-cls-name=\"%s\", comp-cls-type=%d, " - "comp-name=\"%s\"", - cfg_comp->plugin_name->str, - cfg_comp->comp_cls_name->str, - cfg_comp->type, cfg_comp->instance_name->str); - fprintf(stderr, "%s%sCannot create component `%s`%s\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - cfg_comp->instance_name->str, - bt_common_color_reset()); - goto error; - } - - if (ctx->stream_intersection_mode && - cfg_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) { - ret = set_stream_intersections(ctx, cfg_comp, comp_cls); - if (ret) { - goto error; - } - } - - BT_LOGI("Created and inserted component: comp-addr=%p, comp-name=\"%s\"", - comp, cfg_comp->instance_name->str); - quark = g_quark_from_string(cfg_comp->instance_name->str); - BT_ASSERT(quark > 0); - - switch (cfg_comp->type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - g_hash_table_insert(ctx->src_components, - GUINT_TO_POINTER(quark), (void *) comp); - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - g_hash_table_insert(ctx->flt_components, - GUINT_TO_POINTER(quark), (void *) comp); - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - g_hash_table_insert(ctx->sink_components, - GUINT_TO_POINTER(quark), (void *) comp); - break; - default: - abort(); - } - - comp = NULL; - BT_OBJECT_PUT_REF_AND_RESET(comp_cls); - } - - goto end; - -error: - ret = -1; - -end: - bt_object_put_ref(comp); - bt_object_put_ref(comp_cls); - return ret; -} - -static -int cmd_run_ctx_create_components(struct cmd_run_ctx *ctx) -{ - int ret = 0; - - /* - * Make sure that, during this phase, our graph's "port added" - * listener does not connect ports while we are creating the - * components because we have a special, initial phase for - * this. - */ - ctx->connect_ports = false; - - ret = cmd_run_ctx_create_components_from_config_components( - ctx, ctx->cfg->cmd_data.run.sources); - if (ret) { - ret = -1; - goto end; - } - - ret = cmd_run_ctx_create_components_from_config_components( - ctx, ctx->cfg->cmd_data.run.filters); - if (ret) { - ret = -1; - goto end; - } - - ret = cmd_run_ctx_create_components_from_config_components( - ctx, ctx->cfg->cmd_data.run.sinks); - if (ret) { - ret = -1; - goto end; - } - -end: - return ret; -} - -typedef uint64_t (*output_port_count_func_t)(const void *); -typedef const bt_port_output *(*borrow_output_port_by_index_func_t)( - const void *, uint64_t); - -static -int cmd_run_ctx_connect_comp_ports(struct cmd_run_ctx *ctx, - void *comp, output_port_count_func_t port_count_fn, - borrow_output_port_by_index_func_t port_by_index_fn) -{ - int ret = 0; - uint64_t count; - uint64_t i; - - count = port_count_fn(comp); - - for (i = 0; i < count; i++) { - const bt_port_output *upstream_port = port_by_index_fn(comp, i); - - BT_ASSERT(upstream_port); - ret = cmd_run_ctx_connect_upstream_port(ctx, upstream_port); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -static -int cmd_run_ctx_connect_ports(struct cmd_run_ctx *ctx) -{ - int ret = 0; - GHashTableIter iter; - gpointer g_name_quark, g_comp; - - ctx->connect_ports = true; - g_hash_table_iter_init(&iter, ctx->src_components); - - while (g_hash_table_iter_next(&iter, &g_name_quark, &g_comp)) { - ret = cmd_run_ctx_connect_comp_ports(ctx, g_comp, - (output_port_count_func_t) - bt_component_source_get_output_port_count, - (borrow_output_port_by_index_func_t) - bt_component_source_borrow_output_port_by_index_const); - if (ret) { - goto end; - } - } - - g_hash_table_iter_init(&iter, ctx->flt_components); - - while (g_hash_table_iter_next(&iter, &g_name_quark, &g_comp)) { - ret = cmd_run_ctx_connect_comp_ports(ctx, g_comp, - (output_port_count_func_t) - bt_component_filter_get_output_port_count, - (borrow_output_port_by_index_func_t) - bt_component_filter_borrow_output_port_by_index_const); - if (ret) { - goto end; - } - } - -end: - return ret; -} - -static inline -const char *bt_graph_status_str(bt_graph_status status) -{ - switch (status) { - case BT_GRAPH_STATUS_OK: - return "BT_GRAPH_STATUS_OK"; - case BT_GRAPH_STATUS_END: - return "BT_GRAPH_STATUS_END"; - case BT_GRAPH_STATUS_AGAIN: - return "BT_GRAPH_STATUS_AGAIN"; - case BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: - return "BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION"; - case BT_GRAPH_STATUS_CANCELED: - return "BT_GRAPH_STATUS_CANCELED"; - case BT_GRAPH_STATUS_ERROR: - return "BT_GRAPH_STATUS_ERROR"; - case BT_GRAPH_STATUS_NOMEM: - return "BT_GRAPH_STATUS_NOMEM"; - default: - return "(unknown)"; - } -} - -static -int cmd_run(struct bt_config *cfg) -{ - int ret = 0; - struct cmd_run_ctx ctx = { 0 }; - - /* Initialize the command's context and the graph object */ - if (cmd_run_ctx_init(&ctx, cfg)) { - BT_LOGE_STR("Cannot initialize the command's context."); - fprintf(stderr, "Cannot initialize the command's context\n"); - goto error; - } - - if (canceled) { - BT_LOGI_STR("Canceled by user before creating components."); - goto error; - } - - BT_LOGI_STR("Creating components."); - - /* Create the requested component instances */ - if (cmd_run_ctx_create_components(&ctx)) { - BT_LOGE_STR("Cannot create components."); - fprintf(stderr, "Cannot create components\n"); - goto error; - } - - if (canceled) { - BT_LOGI_STR("Canceled by user before connecting components."); - goto error; - } - - BT_LOGI_STR("Connecting components."); - - /* Connect the initially visible component ports */ - if (cmd_run_ctx_connect_ports(&ctx)) { - BT_LOGE_STR("Cannot connect initial component ports."); - fprintf(stderr, "Cannot connect initial component ports\n"); - goto error; - } - - if (canceled) { - BT_LOGI_STR("Canceled by user before running the graph."); - goto error; - } - - BT_LOGI_STR("Running the graph."); - - /* Run the graph */ - while (true) { - bt_graph_status graph_status = bt_graph_run(ctx.graph); - - /* - * Reset console in case something messed with console - * codes during the graph's execution. - */ - printf("%s", bt_common_color_reset()); - fflush(stdout); - fprintf(stderr, "%s", bt_common_color_reset()); - BT_LOGV("bt_graph_run() returned: status=%s", - bt_graph_status_str(graph_status)); - - switch (graph_status) { - case BT_GRAPH_STATUS_OK: - break; - case BT_GRAPH_STATUS_CANCELED: - BT_LOGI_STR("Graph was canceled by user."); - goto error; - case BT_GRAPH_STATUS_AGAIN: - if (bt_graph_is_canceled(ctx.graph)) { - BT_LOGI_STR("Graph was canceled by user."); - goto error; - } - - if (cfg->cmd_data.run.retry_duration_us > 0) { - BT_LOGV("Got BT_GRAPH_STATUS_AGAIN: sleeping: " - "time-us=%" PRIu64, - cfg->cmd_data.run.retry_duration_us); - - if (usleep(cfg->cmd_data.run.retry_duration_us)) { - if (bt_graph_is_canceled(ctx.graph)) { - BT_LOGI_STR("Graph was canceled by user."); - goto error; - } - } - } - break; - case BT_GRAPH_STATUS_END: - goto end; - default: - BT_LOGE_STR("Graph failed to complete successfully"); - fprintf(stderr, "Graph failed to complete successfully\n"); - goto error; - } - } - - goto end; - -error: - if (ret == 0) { - ret = -1; - } - -end: - cmd_run_ctx_destroy(&ctx); - return ret; -} - -static -void warn_command_name_and_directory_clash(struct bt_config *cfg) -{ - const char *env_clash; - - if (!cfg->command_name) { - return; - } - - env_clash = getenv(ENV_BABELTRACE_WARN_COMMAND_NAME_DIRECTORY_CLASH); - if (env_clash && strcmp(env_clash, "0") == 0) { - return; - } - - if (g_file_test(cfg->command_name, - G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { - fprintf(stderr, "\nNOTE: The `%s` command was executed. If you meant to convert a\n", - cfg->command_name); - fprintf(stderr, "trace located in the local `%s` directory, please use:\n", - cfg->command_name); - fprintf(stderr, "\n"); - fprintf(stderr, " babeltrace convert %s [OPTIONS]\n", - cfg->command_name); - } -} - -static -void init_log_level(void) -{ - bt_cli_log_level = bt_log_get_level_from_env(ENV_BABELTRACE_CLI_LOG_LEVEL); -} - -static -void set_auto_log_levels(struct bt_config *cfg) -{ - const char **env_var_name; - - /* - * Override the configuration's default log level if - * BABELTRACE_VERBOSE or BABELTRACE_DEBUG environment variables - * are found for backward compatibility with legacy Babetrace 1. - */ - if (getenv("BABELTRACE_DEBUG") && - strcmp(getenv("BABELTRACE_DEBUG"), "1") == 0) { - cfg->log_level = 'V'; - } else if (getenv("BABELTRACE_VERBOSE") && - strcmp(getenv("BABELTRACE_VERBOSE"), "1") == 0) { - cfg->log_level = 'I'; - } - - /* - * Set log levels according to --debug or --verbose. For - * backward compatibility, --debug is more verbose than - * --verbose. So: - * - * --verbose: INFO log level - * --debug: VERBOSE log level (includes DEBUG, which is - * is less verbose than VERBOSE in the internal - * logging framework) - */ - if (!getenv("BABELTRACE_LOGGING_GLOBAL_LEVEL")) { - if (cfg->verbose) { - bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO); - } else if (cfg->debug) { - bt_logging_set_global_level(BT_LOGGING_LEVEL_VERBOSE); - } else { - /* - * Set library's default log level if not - * explicitly specified. - */ - switch (cfg->log_level) { - case 'N': - bt_logging_set_global_level(BT_LOGGING_LEVEL_NONE); - break; - case 'V': - bt_logging_set_global_level(BT_LOGGING_LEVEL_VERBOSE); - break; - case 'D': - bt_logging_set_global_level(BT_LOGGING_LEVEL_DEBUG); - break; - case 'I': - bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO); - break; - case 'W': - bt_logging_set_global_level(BT_LOGGING_LEVEL_WARN); - break; - case 'E': - bt_logging_set_global_level(BT_LOGGING_LEVEL_ERROR); - break; - case 'F': - bt_logging_set_global_level(BT_LOGGING_LEVEL_FATAL); - break; - default: - abort(); - } - } - } - - if (!getenv(ENV_BABELTRACE_CLI_LOG_LEVEL)) { - if (cfg->verbose) { - bt_cli_log_level = BT_LOG_INFO; - } else if (cfg->debug) { - bt_cli_log_level = BT_LOG_VERBOSE; - } else { - /* - * Set CLI's default log level if not explicitly - * specified. - */ - switch (cfg->log_level) { - case 'N': - bt_cli_log_level = BT_LOG_NONE; - break; - case 'V': - bt_cli_log_level = BT_LOG_VERBOSE; - break; - case 'D': - bt_cli_log_level = BT_LOG_DEBUG; - break; - case 'I': - bt_cli_log_level = BT_LOG_INFO; - break; - case 'W': - bt_cli_log_level = BT_LOG_WARN; - break; - case 'E': - bt_cli_log_level = BT_LOG_ERROR; - break; - case 'F': - bt_cli_log_level = BT_LOG_FATAL; - break; - default: - abort(); - } - } - } - - env_var_name = log_level_env_var_names; - - while (*env_var_name) { - if (!getenv(*env_var_name)) { - if (cfg->verbose) { - g_setenv(*env_var_name, "I", 1); - } else if (cfg->debug) { - g_setenv(*env_var_name, "V", 1); - } else { - char val[2] = { 0 }; - - /* - * Set module's default log level if not - * explicitly specified. - */ - val[0] = cfg->log_level; - g_setenv(*env_var_name, val, 1); - } - } - - env_var_name++; - } -} - -int main(int argc, const char **argv) -{ - int ret; - int retcode; - struct bt_config *cfg; - - init_log_level(); - set_signal_handler(); - init_static_data(); - cfg = bt_config_cli_args_create_with_default(argc, argv, &retcode); - - if (retcode < 0) { - /* Quit without errors; typically usage/version */ - retcode = 0; - BT_LOGI_STR("Quitting without errors."); - goto end; - } - - if (retcode > 0) { - BT_LOGE("Command-line error: retcode=%d", retcode); - goto end; - } - - if (!cfg) { - BT_LOGE_STR("Failed to create a valid Babeltrace configuration."); - fprintf(stderr, "Failed to create Babeltrace configuration\n"); - retcode = 1; - goto end; - } - - set_auto_log_levels(cfg); - print_cfg(cfg); - - if (cfg->command_needs_plugins) { - ret = load_all_plugins(cfg->plugin_paths); - if (ret) { - BT_LOGE("Failed to load plugins: ret=%d", ret); - retcode = 1; - goto end; - } - } - - BT_LOGI("Executing command: cmd=%d, command-name=\"%s\"", - cfg->command, cfg->command_name); - - switch (cfg->command) { - case BT_CONFIG_COMMAND_RUN: - ret = cmd_run(cfg); - break; - case BT_CONFIG_COMMAND_LIST_PLUGINS: - ret = cmd_list_plugins(cfg); - break; - case BT_CONFIG_COMMAND_HELP: - ret = cmd_help(cfg); - break; - case BT_CONFIG_COMMAND_QUERY: - ret = cmd_query(cfg); - break; - case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: - ret = cmd_print_ctf_metadata(cfg); - break; - case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: - ret = cmd_print_lttng_live_sessions(cfg); - break; - default: - BT_LOGF("Invalid/unknown command: cmd=%d", cfg->command); - abort(); - } - - BT_LOGI("Command completed: cmd=%d, command-name=\"%s\", ret=%d", - cfg->command, cfg->command_name, ret); - warn_command_name_and_directory_clash(cfg); - retcode = ret ? 1 : 0; - -end: - BT_OBJECT_PUT_REF_AND_RESET(cfg); - fini_static_data(); - return retcode; -} diff --git a/cli/babeltrace2-cfg-cli-args-connect.c b/cli/babeltrace2-cfg-cli-args-connect.c new file mode 100644 index 00000000..f1d71047 --- /dev/null +++ b/cli/babeltrace2-cfg-cli-args-connect.c @@ -0,0 +1,726 @@ +/* + * Copyright 2017 Philippe Proulx + * + * 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 +#include +#include +#include "babeltrace2-cfg.h" +#include "babeltrace2-cfg-cli-args-connect.h" + +static bool all_named_and_printable_in_array(GPtrArray *comps) +{ + size_t i; + bool all_named_and_printable = 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_and_printable = false; + goto end; + } + + if (!bt_common_string_is_printable(comp->instance_name->str)) { + all_named_and_printable = false; + goto end; + } + } + +end: + return all_named_and_printable; +} + +static bool all_named_and_printable(struct bt_config *cfg) +{ + return all_named_and_printable_in_array(cfg->cmd_data.run.sources) && + all_named_and_printable_in_array(cfg->cmd_data.run.filters) && + all_named_and_printable_in_array(cfg->cmd_data.run.sinks); +} + +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->upstream_comp_name = g_string_new(NULL); + if (!cfg_connection->upstream_comp_name) { + goto error; + } + + cfg_connection->downstream_comp_name = g_string_new(NULL); + if (!cfg_connection->downstream_comp_name) { + goto error; + } + + cfg_connection->upstream_port_glob = g_string_new("*"); + if (!cfg_connection->upstream_port_glob) { + goto error; + } + + cfg_connection->downstream_port_glob = g_string_new("*"); + if (!cfg_connection->downstream_port_glob) { + 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 bool validate_port_glob(const char *port_glob) +{ + bool is_valid = true; + const char *ch = port_glob; + + BT_ASSERT(port_glob); + + while (*ch != '\0') { + switch (*ch) { + case '\\': + switch (ch[1]) { + case '\0': + goto end; + default: + ch += 2; + continue; + } + case '?': + case '[': + /* + * This is reserved for future use, to support + * full globbing patterns. Those characters must + * be escaped with `\`. + */ + is_valid = false; + goto end; + default: + ch++; + break; + } + } + +end: + return is_valid; +} + +static int normalize_glob_pattern(GString *glob_pattern_gs) +{ + int ret = 0; + char *glob_pattern = strdup(glob_pattern_gs->str); + + if (!glob_pattern) { + ret = -1; + goto end; + } + + bt_common_normalize_star_glob_pattern(glob_pattern); + g_string_assign(glob_pattern_gs, glob_pattern); + free(glob_pattern); + +end: + return ret; +} + +static struct bt_config_connection *cfg_connection_from_arg(const char *arg) +{ + const char *at = arg; + size_t end_pos; + struct bt_config_connection *cfg_conn = NULL; + GString *gs = NULL; + enum { + UPSTREAM_NAME, + DOWNSTREAM_NAME, + UPSTREAM_PORT_GLOB, + DOWNSTREAM_PORT_GLOB, + } state = UPSTREAM_NAME; + + if (!bt_common_string_is_printable(arg)) { + goto error; + } + + cfg_conn = bt_config_connection_create(arg); + if (!cfg_conn) { + goto error; + } + + while (true) { + switch (state) { + case UPSTREAM_NAME: + gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + g_string_free(cfg_conn->upstream_comp_name, TRUE); + cfg_conn->upstream_comp_name = gs; + gs = NULL; + + if (at[end_pos] == ':') { + state = DOWNSTREAM_NAME; + } else if (at[end_pos] == '.') { + state = UPSTREAM_PORT_GLOB; + } else { + goto error; + } + + at += end_pos + 1; + break; + case DOWNSTREAM_NAME: + gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + g_string_free(cfg_conn->downstream_comp_name, TRUE); + cfg_conn->downstream_comp_name = gs; + gs = NULL; + + if (at[end_pos] == '.') { + state = DOWNSTREAM_PORT_GLOB; + } else if (at[end_pos] == '\0') { + goto end; + } else { + goto error; + } + + at += end_pos + 1; + break; + case UPSTREAM_PORT_GLOB: + gs = bt_common_string_until(at, ".:", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + if (!validate_port_glob(gs->str)) { + goto error; + } + + if (normalize_glob_pattern(gs)) { + goto error; + } + + g_string_free(cfg_conn->upstream_port_glob, TRUE); + cfg_conn->upstream_port_glob = gs; + gs = NULL; + + if (at[end_pos] == ':') { + state = DOWNSTREAM_NAME; + } else { + goto error; + } + + at += end_pos + 1; + break; + case DOWNSTREAM_PORT_GLOB: + gs = bt_common_string_until(at, ".:", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + if (!validate_port_glob(gs->str)) { + goto error; + } + + if (normalize_glob_pattern(gs)) { + goto error; + } + + g_string_free(cfg_conn->downstream_port_glob, TRUE); + cfg_conn->downstream_port_glob = gs; + gs = NULL; + + if (at[end_pos] == '\0') { + goto end; + } else { + goto error; + } + break; + default: + abort(); + } + } + +error: + bt_config_connection_destroy(cfg_conn); + cfg_conn = NULL; + +end: + if (gs) { + g_string_free(gs, TRUE); + } + + return cfg_conn; +} + +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 = comp; + bt_object_get_ref(found_comp); + goto end; + } + } + +end: + return found_comp; +} + +static struct bt_config_component *find_component(struct bt_config *cfg, + const char *name) +{ + struct bt_config_component *comp; + + comp = find_component_in_array(cfg->cmd_data.run.sources, name); + if (comp) { + goto end; + } + + comp = find_component_in_array(cfg->cmd_data.run.filters, name); + if (comp) { + goto end; + } + + comp = find_component_in_array(cfg->cmd_data.run.sinks, name); + if (comp) { + 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.run.connections->len; i++) { + struct bt_config_connection *connection = + g_ptr_array_index(cfg->cmd_data.run.connections, i); + struct bt_config_component *comp; + + comp = find_component(cfg, connection->upstream_comp_name->str); + bt_object_put_ref(comp); + if (!comp) { + snprintf(error_buf, error_buf_size, + "Invalid connection: cannot find upstream component `%s`:\n %s\n", + connection->upstream_comp_name->str, + connection->arg->str); + ret = -1; + goto end; + } + + comp = find_component(cfg, connection->downstream_comp_name->str); + bt_object_put_ref(comp); + if (!comp) { + snprintf(error_buf, error_buf_size, + "Invalid connection: cannot find downstream component `%s`:\n %s\n", + connection->downstream_comp_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.run.connections->len; i++) { + struct bt_config_connection *connection = + g_ptr_array_index(cfg->cmd_data.run.connections, i); + + src_comp = find_component(cfg, + connection->upstream_comp_name->str); + BT_ASSERT(src_comp); + dst_comp = find_component(cfg, + connection->downstream_comp_name->str); + BT_ASSERT(dst_comp); + + if (src_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) { + if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER && + dst_comp->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->upstream_comp_name->str, + connection->arg->str); + ret = -1; + goto end; + } + } else if (src_comp->type == BT_COMPONENT_CLASS_TYPE_FILTER) { + if (dst_comp->type != BT_COMPONENT_CLASS_TYPE_FILTER && + dst_comp->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->upstream_comp_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->upstream_comp_name->str, + connection->downstream_comp_name->str, + connection->arg->str); + ret = -1; + goto end; + } + + BT_OBJECT_PUT_REF_AND_RESET(src_comp); + BT_OBJECT_PUT_REF_AND_RESET(dst_comp); + } + +end: + bt_object_put_ref(src_comp); + bt_object_put_ref(dst_comp); + return ret; +} + +static int validate_no_cycles_rec(struct bt_config *cfg, GPtrArray *path, + char *error_buf, size_t error_buf_size) +{ + int ret = 0; + size_t conn_i; + const char *src_comp_name; + + BT_ASSERT(path && path->len > 0); + src_comp_name = g_ptr_array_index(path, path->len - 1); + + for (conn_i = 0; conn_i < cfg->cmd_data.run.connections->len; conn_i++) { + struct bt_config_connection *conn = + g_ptr_array_index(cfg->cmd_data.run.connections, conn_i); + + if (strcmp(conn->upstream_comp_name->str, src_comp_name) == 0) { + size_t path_i; + + for (path_i = 0; path_i < path->len; path_i++) { + const char *comp_name = + g_ptr_array_index(path, path_i); + + if (strcmp(comp_name, conn->downstream_comp_name->str) == 0) { + snprintf(error_buf, error_buf_size, + "Invalid connection: connection forms a cycle:\n %s\n", + conn->arg->str); + ret = -1; + goto end; + } + } + + g_ptr_array_add(path, conn->downstream_comp_name->str); + ret = validate_no_cycles_rec(cfg, path, error_buf, + error_buf_size); + if (ret) { + goto end; + } + + g_ptr_array_remove_index(path, path->len - 1); + } + } + +end: + return ret; +} + +static int validate_no_cycles(struct bt_config *cfg, char *error_buf, + size_t error_buf_size) +{ + size_t i; + int ret = 0; + GPtrArray *path; + + path = g_ptr_array_new(); + if (!path) { + ret = -1; + goto end; + } + + g_ptr_array_add(path, NULL); + + for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *conn = + g_ptr_array_index(cfg->cmd_data.run.connections, i); + + g_ptr_array_index(path, 0) = conn->upstream_comp_name->str; + ret = validate_no_cycles_rec(cfg, path, + error_buf, error_buf_size); + if (ret) { + goto end; + } + } + +end: + if (path) { + g_ptr_array_free(path, TRUE); + } + + return ret; +} + +static int validate_all_components_connected_in_array(GPtrArray *comps, + const 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_entry(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; + bt_value *connected_components = bt_value_map_create(); + + if (!connected_components) { + ret = -1; + goto end; + } + + for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *connection = + g_ptr_array_index(cfg->cmd_data.run.connections, i); + + ret = bt_value_map_insert_entry(connected_components, + connection->upstream_comp_name->str, bt_value_null); + if (ret) { + goto end; + } + + ret = bt_value_map_insert_entry(connected_components, + connection->downstream_comp_name->str, bt_value_null); + if (ret) { + goto end; + } + } + + ret = validate_all_components_connected_in_array( + cfg->cmd_data.run.sources, + connected_components, + error_buf, error_buf_size); + if (ret) { + goto end; + } + + ret = validate_all_components_connected_in_array( + cfg->cmd_data.run.filters, + connected_components, + error_buf, error_buf_size); + if (ret) { + goto end; + } + + ret = validate_all_components_connected_in_array( + cfg->cmd_data.run.sinks, + connected_components, + error_buf, error_buf_size); + if (ret) { + goto end; + } + +end: + bt_value_put_ref(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; + 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.run.connections->len; i++) { + struct bt_config_connection *connection = + g_ptr_array_index(cfg->cmd_data.run.connections, i); + + g_string_printf(flat_connection_name, "%s\x01%s\x01%s\x01%s", + connection->upstream_comp_name->str, + connection->upstream_port_glob->str, + connection->downstream_comp_name->str, + connection->downstream_port_glob->str); + + if (bt_value_map_has_entry(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_entry(flat_connection_names, + flat_connection_name->str, bt_value_null); + if (ret) { + goto end; + } + } + +end: + bt_value_put_ref(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_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; + } + + ret = validate_no_cycles(cfg, error_buf, error_buf_size); + if (ret) { + goto end; + } + +end: + return ret; +} + +int bt_config_cli_args_create_connections(struct bt_config *cfg, + const bt_value *connection_args, + char *error_buf, size_t error_buf_size) +{ + int ret; + size_t i; + + if (!all_named_and_printable(cfg)) { + snprintf(error_buf, error_buf_size, + "One or more components are unnamed (use --name) or contain a non-printable character\n"); + goto error; + } + + for (i = 0; i < bt_value_array_get_size(connection_args); i++) { + const bt_value *arg_value = + bt_value_array_borrow_element_by_index_const( + connection_args, i); + const char *arg; + struct bt_config_connection *cfg_connection; + + arg = bt_value_string_get(arg_value); + 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.run.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/cli/babeltrace2-cfg-cli-args-connect.h b/cli/babeltrace2-cfg-cli-args-connect.h new file mode 100644 index 00000000..5130cd91 --- /dev/null +++ b/cli/babeltrace2-cfg-cli-args-connect.h @@ -0,0 +1,36 @@ +#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H +#define CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H + +/* + * Copyright 2017 Philippe Proulx + * + * 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 +#include +#include +#include +#include "babeltrace2-cfg.h" + +int bt_config_cli_args_create_connections(struct bt_config *cfg, + const bt_value *connection_args, + char *error_buf, size_t error_buf_size); + +#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_CONNECT_H */ diff --git a/cli/babeltrace2-cfg-cli-args-default.c b/cli/babeltrace2-cfg-cli-args-default.c new file mode 100644 index 00000000..5cd0bcf8 --- /dev/null +++ b/cli/babeltrace2-cfg-cli-args-default.c @@ -0,0 +1,77 @@ +/* + * Copyright 2016 - Jérémie Galarneau + * Copyright 2017 - Philippe Proulx + * + * 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 +#include "babeltrace2-cfg.h" +#include "babeltrace2-cfg-cli-args.h" +#include "babeltrace2-cfg-cli-args-default.h" + +#ifdef ENABLE_DEBUG_INFO +# define BT_ENABLE_DEBUG_INFO 1 +#else +# define BT_ENABLE_DEBUG_INFO 0 +#endif + +#ifdef BT_SET_DEFAULT_IN_TREE_CONFIGURATION + +struct bt_config *bt_config_cli_args_create_with_default(int argc, + const char *argv[], int *retcode) +{ + bt_value *initial_plugin_paths; + struct bt_config *cfg = NULL; + int ret; + + initial_plugin_paths = bt_value_array_create(); + if (!initial_plugin_paths) { + goto error; + } + + ret = bt_config_append_plugin_paths(initial_plugin_paths, + CONFIG_IN_TREE_PLUGIN_PATH); + if (ret) { + goto error; + } + + cfg = bt_config_cli_args_create(argc, argv, retcode, true, true, + initial_plugin_paths); + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + bt_value_put_ref(initial_plugin_paths); + return cfg; +} + +#else /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ + +struct bt_config *bt_config_cli_args_create_with_default(int argc, + const char *argv[], int *retcode) +{ + return bt_config_cli_args_create(argc, argv, retcode, false, false, + NULL); +} + +#endif /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ diff --git a/cli/babeltrace2-cfg-cli-args-default.h b/cli/babeltrace2-cfg-cli-args-default.h new file mode 100644 index 00000000..839b08c1 --- /dev/null +++ b/cli/babeltrace2-cfg-cli-args-default.h @@ -0,0 +1,33 @@ +#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H +#define CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H + +/* + * Babeltrace Trace Converter - Default Configuration + * + * Copyright 2016 Jérémie Galarneau + * + * 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 "babeltrace2-cfg.h" + +struct bt_config *bt_config_cli_args_create_with_default(int argc, + const char *argv[], int *retcode); + +#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_DEFAULT_H */ diff --git a/cli/babeltrace2-cfg-cli-args.c b/cli/babeltrace2-cfg-cli-args.c new file mode 100644 index 00000000..7a7b855a --- /dev/null +++ b/cli/babeltrace2-cfg-cli-args.c @@ -0,0 +1,5107 @@ +/* + * Babeltrace trace converter - parameter parsing + * + * Copyright 2016 Philippe Proulx + * + * 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-CLI-ARGS" +#include "logging.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "babeltrace2-cfg.h" +#include "babeltrace2-cfg-cli-args.h" +#include "babeltrace2-cfg-cli-args-connect.h" +#include "version.h" + +/* + * Error printf() macro which prepends "Error: " the first time it's + * called. This gives a nicer feel than having a bunch of error prefixes + * (since the following lines usually describe the error and possible + * solutions), or the error prefix just at the end. + */ +#define printf_err(fmt, args...) \ + do { \ + if (is_first_error) { \ + fprintf(stderr, "Command line error: "); \ + is_first_error = false; \ + } \ + fprintf(stderr, fmt, ##args); \ + } while (0) + +static bool is_first_error = true; + +/* INI-style parsing FSM states */ +enum ini_parsing_fsm_state { + /* Expect a map key (identifier) */ + INI_EXPECT_MAP_KEY, + + /* Expect an equal character ('=') */ + INI_EXPECT_EQUAL, + + /* Expect a value */ + INI_EXPECT_VALUE, + + /* Expect a comma character (',') */ + INI_EXPECT_COMMA, +}; + +/* INI-style parsing state variables */ +struct ini_parsing_state { + /* Lexical scanner (owned by this) */ + GScanner *scanner; + + /* Output map value object being filled (owned by this) */ + bt_value *params; + + /* Next expected FSM state */ + enum ini_parsing_fsm_state expecting; + + /* Last decoded map key (owned by this) */ + char *last_map_key; + + /* Complete INI-style string to parse (not owned by this) */ + const char *arg; + + /* Error buffer (not owned by this) */ + GString *ini_error; +}; + +/* Offset option with "is set" boolean */ +struct offset_opt { + int64_t value; + bool is_set; +}; + +/* Legacy "ctf"/"lttng-live" format options */ +struct ctf_legacy_opts { + struct offset_opt offset_s; + struct offset_opt offset_ns; + bool stream_intersection; +}; + +/* Legacy "text" format options */ +struct text_legacy_opts { + /* + * output, dbg_info_dir, dbg_info_target_prefix, names, + * and fields are owned by this. + */ + GString *output; + GString *dbg_info_dir; + GString *dbg_info_target_prefix; + const bt_value *names; + const bt_value *fields; + + /* Flags */ + bool no_delta; + bool clock_cycles; + bool clock_seconds; + bool clock_date; + bool clock_gmt; + bool dbg_info_full_path; + bool verbose; +}; + +/* Legacy input format format */ +enum legacy_input_format { + LEGACY_INPUT_FORMAT_NONE = 0, + LEGACY_INPUT_FORMAT_CTF, + LEGACY_INPUT_FORMAT_LTTNG_LIVE, +}; + +/* Legacy output format format */ +enum legacy_output_format { + LEGACY_OUTPUT_FORMAT_NONE = 0, + LEGACY_OUTPUT_FORMAT_TEXT, + LEGACY_OUTPUT_FORMAT_DUMMY, +}; + +/* + * Prints the "out of memory" error. + */ +static +void print_err_oom(void) +{ + printf_err("Out of memory\n"); +} + +/* + * Appends an "expecting token" error to the INI-style parsing state's + * error buffer. + */ +static +void ini_append_error_expecting(struct ini_parsing_state *state, + GScanner *scanner, const char *expecting) +{ + size_t i; + size_t pos; + + g_string_append_printf(state->ini_error, "Expecting %s:\n", expecting); + + /* Only print error if there's one line */ + if (strchr(state->arg, '\n') != NULL || strlen(state->arg) == 0) { + return; + } + + g_string_append_printf(state->ini_error, "\n %s\n", state->arg); + pos = g_scanner_cur_position(scanner) + 4; + + if (!g_scanner_eof(scanner)) { + pos--; + } + + for (i = 0; i < pos; ++i) { + g_string_append_printf(state->ini_error, " "); + } + + g_string_append_printf(state->ini_error, "^\n\n"); +} + +/* Parse the next token as an unsigned integer. */ +static +bt_value *ini_parse_uint(struct ini_parsing_state *state) +{ + bt_value *value = NULL; + GTokenType token_type = g_scanner_get_next_token(state->scanner); + + if (token_type != G_TOKEN_INT) { + ini_append_error_expecting(state, state->scanner, + "integer value"); + goto end; + } + + value = bt_value_unsigned_integer_create_init( + state->scanner->value.v_int64); + +end: + return value; +} + +/* Parse the next token as a number and return its negation. */ +static +bt_value *ini_parse_neg_number(struct ini_parsing_state *state) +{ + bt_value *value = NULL; + GTokenType token_type = g_scanner_get_next_token(state->scanner); + + switch (token_type) { + case G_TOKEN_INT: + { + /* Negative integer */ + uint64_t int_val = state->scanner->value.v_int64; + + if (int_val > (((uint64_t) INT64_MAX) + 1)) { + g_string_append_printf(state->ini_error, + "Integer value -%" PRIu64 " is outside the range of a 64-bit signed integer\n", + int_val); + } else { + value = bt_value_signed_integer_create_init( + -((int64_t) int_val)); + } + + break; + } + case G_TOKEN_FLOAT: + /* Negative floating point number */ + value = bt_value_real_create_init(-state->scanner->value.v_float); + break; + default: + ini_append_error_expecting(state, state->scanner, "value"); + break; + } + + return value; +} + +static bt_value *ini_parse_value(struct ini_parsing_state *state); + +/* + * Parse the current and following tokens as an array. Arrays are formatted as + * an opening `[`, a list of comma-separated values and a closing `]`. For + * convenience we support an optional trailing comma, after the last value. + * + * The current token of the parser must be the opening square bracket of the + * array. + */ +static +bt_value *ini_parse_array(struct ini_parsing_state *state) +{ + /* The [ character must have already been ingested. */ + BT_ASSERT(g_scanner_cur_token(state->scanner) == G_TOKEN_CHAR); + BT_ASSERT(g_scanner_cur_value(state->scanner).v_char == '['); + + bt_value *array_value; + GTokenType token_type; + + array_value = bt_value_array_create (); + token_type = g_scanner_get_next_token(state->scanner); + + /* While the current token is not a ]... */ + while (!(token_type == G_TOKEN_CHAR && g_scanner_cur_value(state->scanner).v_char == ']')) { + /* Parse the item... */ + bt_value *item_value; + bt_value_status status; + + item_value = ini_parse_value(state); + if (!item_value) { + goto error; + } + + /* ... and add it to the result array. */ + status = bt_value_array_append_element(array_value, item_value); + BT_VALUE_PUT_REF_AND_RESET(item_value); + + if (status != BT_VALUE_STATUS_OK) { + goto error; + } + + /* + * Ingest the token following the value, it should be either a + * comma or closing square brace. + */ + token_type = g_scanner_get_next_token(state->scanner); + + if (token_type == G_TOKEN_CHAR && g_scanner_cur_value(state->scanner).v_char == ',') { + /* + * Ingest the token following the comma. If it happens + * to be a closing square bracket, we'll exit the loop + * and we are done (we allow trailing commas). + * Otherwise, we are ready for the next ini_parse_value call. + */ + token_type = g_scanner_get_next_token(state->scanner); + } else if (token_type != G_TOKEN_CHAR || g_scanner_cur_value(state->scanner).v_char != ']') { + ini_append_error_expecting(state, state->scanner, ", or ]"); + goto error; + } + } + + goto end; + +error: + BT_VALUE_PUT_REF_AND_RESET(array_value); + +end: + return array_value; +} + +/* + * Parse the current token (and the following ones if needed) as a value, return + * it as a bt_value. + */ +static +bt_value *ini_parse_value(struct ini_parsing_state *state) +{ + bt_value *value = NULL; + GTokenType token_type = state->scanner->token; + + switch (token_type) { + case G_TOKEN_CHAR: + if (state->scanner->value.v_char == '-') { + /* Negative number */ + value = ini_parse_neg_number(state); + } else if (state->scanner->value.v_char == '+') { + /* Unsigned integer */ + value = ini_parse_uint(state); + } else if (state->scanner->value.v_char == '[') { + /* Array */ + value = ini_parse_array(state); + } else { + ini_append_error_expecting(state, state->scanner, "value"); + } + break; + case G_TOKEN_INT: + { + /* Positive, signed integer */ + uint64_t int_val = state->scanner->value.v_int64; + + if (int_val > INT64_MAX) { + g_string_append_printf(state->ini_error, + "Integer value %" PRIu64 " is outside the range of a 64-bit signed integer\n", + int_val); + } else { + value = bt_value_signed_integer_create_init( + (int64_t) int_val); + } + break; + } + case G_TOKEN_FLOAT: + /* Positive floating point number */ + value = bt_value_real_create_init(state->scanner->value.v_float); + break; + case G_TOKEN_STRING: + /* Quoted string */ + value = bt_value_string_create_init(state->scanner->value.v_string); + break; + case G_TOKEN_IDENTIFIER: + { + /* + * Using symbols would be appropriate here, + * but said symbols are allowed as map key, + * so it's easier to consider everything an + * identifier. + * + * If one of the known symbols is not + * recognized here, then fall back to creating + * a string value. + */ + const char *id = state->scanner->value.v_identifier; + + if (!strcmp(id, "null") || !strcmp(id, "NULL") || + !strcmp(id, "nul")) { + value = bt_value_null; + } else if (!strcmp(id, "true") || !strcmp(id, "TRUE") || + !strcmp(id, "yes") || + !strcmp(id, "YES")) { + value = bt_value_bool_create_init(true); + } else if (!strcmp(id, "false") || + !strcmp(id, "FALSE") || + !strcmp(id, "no") || + !strcmp(id, "NO")) { + value = bt_value_bool_create_init(false); + } else { + value = bt_value_string_create_init(id); + } + break; + } + default: + /* Unset value variable will trigger the error */ + ini_append_error_expecting(state, state->scanner, "value"); + break; + } + + return value; +} + +static +int ini_handle_state(struct ini_parsing_state *state) +{ + int ret = 0; + GTokenType token_type; + bt_value *value = NULL; + + token_type = g_scanner_get_next_token(state->scanner); + if (token_type == G_TOKEN_EOF) { + if (state->expecting != INI_EXPECT_COMMA) { + switch (state->expecting) { + case INI_EXPECT_EQUAL: + ini_append_error_expecting(state, + state->scanner, "'='"); + break; + case INI_EXPECT_VALUE: + ini_append_error_expecting(state, + state->scanner, "value"); + break; + case INI_EXPECT_MAP_KEY: + ini_append_error_expecting(state, + state->scanner, "unquoted map key"); + break; + default: + break; + } + goto error; + } + + /* We're done! */ + ret = 1; + goto success; + } + + switch (state->expecting) { + case INI_EXPECT_MAP_KEY: + if (token_type != G_TOKEN_IDENTIFIER) { + ini_append_error_expecting(state, state->scanner, + "unquoted map key"); + goto error; + } + + free(state->last_map_key); + state->last_map_key = + strdup(state->scanner->value.v_identifier); + if (!state->last_map_key) { + g_string_append(state->ini_error, + "Out of memory\n"); + goto error; + } + + if (bt_value_map_has_entry(state->params, + state->last_map_key)) { + g_string_append_printf(state->ini_error, + "Duplicate parameter key: `%s`\n", + state->last_map_key); + goto error; + } + + state->expecting = INI_EXPECT_EQUAL; + goto success; + case INI_EXPECT_EQUAL: + if (token_type != G_TOKEN_CHAR) { + ini_append_error_expecting(state, + state->scanner, "'='"); + goto error; + } + + if (state->scanner->value.v_char != '=') { + ini_append_error_expecting(state, + state->scanner, "'='"); + goto error; + } + + state->expecting = INI_EXPECT_VALUE; + goto success; + case INI_EXPECT_VALUE: + { + value = ini_parse_value(state); + if (!value) { + goto error; + } + + state->expecting = INI_EXPECT_COMMA; + goto success; + } + case INI_EXPECT_COMMA: + if (token_type != G_TOKEN_CHAR) { + ini_append_error_expecting(state, + state->scanner, "','"); + goto error; + } + + if (state->scanner->value.v_char != ',') { + ini_append_error_expecting(state, + state->scanner, "','"); + goto error; + } + + state->expecting = INI_EXPECT_MAP_KEY; + goto success; + default: + abort(); + } + +error: + ret = -1; + goto end; + +success: + if (value) { + if (bt_value_map_insert_entry(state->params, + state->last_map_key, value)) { + /* Only override return value on error */ + ret = -1; + } + } + +end: + BT_VALUE_PUT_REF_AND_RESET(value); + return ret; +} + +/* + * Converts an INI-style argument to an equivalent map value object. + * + * Return value is owned by the caller. + */ +static +bt_value *bt_value_from_ini(const char *arg, + GString *ini_error) +{ + /* Lexical scanner configuration */ + GScannerConfig scanner_config = { + /* Skip whitespaces */ + .cset_skip_characters = " \t\n", + + /* Identifier syntax is: [a-zA-Z_][a-zA-Z0-9_.:-]* */ + .cset_identifier_first = + G_CSET_a_2_z + "_" + G_CSET_A_2_Z, + .cset_identifier_nth = + G_CSET_a_2_z + "_0123456789-.:" + G_CSET_A_2_Z, + + /* "hello" and "Hello" two different keys */ + .case_sensitive = TRUE, + + /* No comments */ + .cpair_comment_single = NULL, + .skip_comment_multi = TRUE, + .skip_comment_single = TRUE, + .scan_comment_multi = FALSE, + + /* + * Do scan identifiers, including 1-char identifiers, + * but NULL is a normal identifier. + */ + .scan_identifier = TRUE, + .scan_identifier_1char = TRUE, + .scan_identifier_NULL = FALSE, + + /* + * No specific symbols: null and boolean "symbols" are + * scanned as plain identifiers. + */ + .scan_symbols = FALSE, + .symbol_2_token = FALSE, + .scope_0_fallback = FALSE, + + /* + * Scan "0b"-, "0"-, and "0x"-prefixed integers, but not + * integers prefixed with "$". + */ + .scan_binary = TRUE, + .scan_octal = TRUE, + .scan_float = TRUE, + .scan_hex = TRUE, + .scan_hex_dollar = FALSE, + + /* Convert scanned numbers to integer tokens */ + .numbers_2_int = TRUE, + + /* Support both integers and floating-point numbers */ + .int_2_float = FALSE, + + /* Scan integers as 64-bit signed integers */ + .store_int64 = TRUE, + + /* Only scan double-quoted strings */ + .scan_string_sq = FALSE, + .scan_string_dq = TRUE, + + /* Do not converter identifiers to string tokens */ + .identifier_2_string = FALSE, + + /* Scan characters as G_TOKEN_CHAR token */ + .char_2_token = FALSE, + }; + struct ini_parsing_state state = { + .scanner = NULL, + .params = NULL, + .expecting = INI_EXPECT_MAP_KEY, + .arg = arg, + .ini_error = ini_error, + }; + + state.params = bt_value_map_create(); + if (!state.params) { + goto error; + } + + state.scanner = g_scanner_new(&scanner_config); + if (!state.scanner) { + goto error; + } + + /* Let the scan begin */ + g_scanner_input_text(state.scanner, arg, strlen(arg)); + + while (true) { + int ret = ini_handle_state(&state); + + if (ret < 0) { + /* Error */ + goto error; + } else if (ret > 0) { + /* Done */ + break; + } + } + + goto end; + +error: + BT_VALUE_PUT_REF_AND_RESET(state.params); + +end: + if (state.scanner) { + g_scanner_destroy(state.scanner); + } + + free(state.last_map_key); + return state.params; +} + +/* + * Returns the parameters map value object from a command-line + * parameter option's argument. + * + * Return value is owned by the caller. + */ +static +bt_value *bt_value_from_arg(const char *arg) +{ + bt_value *params = NULL; + GString *ini_error = NULL; + + ini_error = g_string_new(NULL); + if (!ini_error) { + print_err_oom(); + goto end; + } + + /* Try INI-style parsing */ + params = bt_value_from_ini(arg, ini_error); + if (!params) { + printf_err("%s", ini_error->str); + goto end; + } + +end: + if (ini_error) { + g_string_free(ini_error, TRUE); + } + + return params; +} + +/* + * Returns the plugin name, component class name, component class type, + * and component name from a command-line --component option's argument. + * arg must have the following format: + * + * [NAME:]TYPE.PLUGIN.CLS + * + * where NAME is the optional component name, TYPE is either `source`, + * `filter`, or `sink`, PLUGIN is the plugin name, and CLS is the + * component class name. + * + * On success, both *plugin and *component are not NULL. *plugin + * and *comp_cls are owned by the caller. On success, *name can be NULL + * if no component class name was found, and *comp_cls_type is set. + */ +static +void plugin_comp_cls_names(const char *arg, char **name, char **plugin, + char **comp_cls, bt_component_class_type *comp_cls_type) +{ + const char *at = arg; + GString *gs_name = NULL; + GString *gs_comp_cls_type = NULL; + GString *gs_plugin = NULL; + GString *gs_comp_cls = NULL; + size_t end_pos; + + BT_ASSERT(arg); + BT_ASSERT(plugin); + BT_ASSERT(comp_cls); + BT_ASSERT(comp_cls_type); + + if (!bt_common_string_is_printable(arg)) { + printf_err("Argument contains a non-printable character\n"); + goto error; + } + + /* Parse the component name */ + gs_name = bt_common_string_until(at, ".:\\", ":", &end_pos); + if (!gs_name) { + goto error; + } + + if (arg[end_pos] == ':') { + at += end_pos + 1; + } else { + /* No name */ + g_string_assign(gs_name, ""); + } + + /* Parse the component class type */ + gs_comp_cls_type = bt_common_string_until(at, ".:\\", ".", &end_pos); + if (!gs_comp_cls_type || at[end_pos] == '\0') { + printf_err("Missing component class type (`source`, `filter`, or `sink`)\n"); + goto error; + } + + if (strcmp(gs_comp_cls_type->str, "source") == 0 || + strcmp(gs_comp_cls_type->str, "src") == 0) { + *comp_cls_type = BT_COMPONENT_CLASS_TYPE_SOURCE; + } else if (strcmp(gs_comp_cls_type->str, "filter") == 0 || + strcmp(gs_comp_cls_type->str, "flt") == 0) { + *comp_cls_type = BT_COMPONENT_CLASS_TYPE_FILTER; + } else if (strcmp(gs_comp_cls_type->str, "sink") == 0) { + *comp_cls_type = BT_COMPONENT_CLASS_TYPE_SINK; + } else { + printf_err("Unknown component class type: `%s`\n", + gs_comp_cls_type->str); + goto error; + } + + at += end_pos + 1; + + /* Parse the plugin name */ + gs_plugin = bt_common_string_until(at, ".:\\", ".", &end_pos); + if (!gs_plugin || gs_plugin->len == 0 || at[end_pos] == '\0') { + printf_err("Missing plugin or component class name\n"); + goto error; + } + + at += end_pos + 1; + + /* Parse the component class name */ + gs_comp_cls = bt_common_string_until(at, ".:\\", ".", &end_pos); + if (!gs_comp_cls || gs_comp_cls->len == 0) { + printf_err("Missing component class name\n"); + goto error; + } + + if (at[end_pos] != '\0') { + /* Found a non-escaped `.` */ + goto error; + } + + if (name) { + if (gs_name->len == 0) { + *name = NULL; + g_string_free(gs_name, TRUE); + } else { + *name = gs_name->str; + g_string_free(gs_name, FALSE); + } + } else { + g_string_free(gs_name, TRUE); + } + + *plugin = gs_plugin->str; + *comp_cls = gs_comp_cls->str; + g_string_free(gs_plugin, FALSE); + g_string_free(gs_comp_cls, FALSE); + gs_name = NULL; + gs_plugin = NULL; + gs_comp_cls = NULL; + goto end; + +error: + if (name) { + *name = NULL; + } + + *plugin = NULL; + *comp_cls = NULL; + +end: + if (gs_name) { + g_string_free(gs_name, TRUE); + } + + if (gs_plugin) { + g_string_free(gs_plugin, TRUE); + } + + if (gs_comp_cls) { + g_string_free(gs_comp_cls, TRUE); + } + + if (gs_comp_cls_type) { + g_string_free(gs_comp_cls_type, TRUE); + } + + return; +} + +/* + * Prints the Babeltrace version. + */ +static +void print_version(void) +{ + if (GIT_VERSION[0] == '\0') { + puts("Babeltrace " VERSION); + } else { + puts("Babeltrace " VERSION " - " GIT_VERSION); + } +} + +/* + * Destroys a component configuration. + */ +static +void bt_config_component_destroy(bt_object *obj) +{ + struct bt_config_component *bt_config_component = + container_of(obj, struct bt_config_component, base); + + if (!obj) { + goto end; + } + + if (bt_config_component->plugin_name) { + g_string_free(bt_config_component->plugin_name, TRUE); + } + + if (bt_config_component->comp_cls_name) { + g_string_free(bt_config_component->comp_cls_name, TRUE); + } + + if (bt_config_component->instance_name) { + g_string_free(bt_config_component->instance_name, TRUE); + } + + BT_VALUE_PUT_REF_AND_RESET(bt_config_component->params); + g_free(bt_config_component); + +end: + return; +} + +/* + * Creates a component configuration using the given plugin name and + * component name. `plugin_name` and `comp_cls_name` are copied (belong + * to the return value). + * + * Return value is owned by the caller. + */ +static +struct bt_config_component *bt_config_component_create( + bt_component_class_type type, + const char *plugin_name, const char *comp_cls_name) +{ + struct bt_config_component *cfg_component = NULL; + + cfg_component = g_new0(struct bt_config_component, 1); + if (!cfg_component) { + print_err_oom(); + goto error; + } + + bt_object_init_shared(&cfg_component->base, + bt_config_component_destroy); + cfg_component->type = type; + cfg_component->plugin_name = g_string_new(plugin_name); + if (!cfg_component->plugin_name) { + print_err_oom(); + goto error; + } + + cfg_component->comp_cls_name = g_string_new(comp_cls_name); + if (!cfg_component->comp_cls_name) { + print_err_oom(); + goto error; + } + + cfg_component->instance_name = g_string_new(NULL); + if (!cfg_component->instance_name) { + print_err_oom(); + goto error; + } + + /* Start with empty parameters */ + cfg_component->params = bt_value_map_create(); + if (!cfg_component->params) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg_component); + +end: + return cfg_component; +} + +/* + * Creates a component configuration from a command-line --component + * option's argument. + */ +static +struct bt_config_component *bt_config_component_from_arg(const char *arg) +{ + struct bt_config_component *cfg_comp = NULL; + char *name = NULL; + char *plugin_name = NULL; + char *comp_cls_name = NULL; + bt_component_class_type type; + + plugin_comp_cls_names(arg, &name, &plugin_name, &comp_cls_name, &type); + if (!plugin_name || !comp_cls_name) { + goto error; + } + + cfg_comp = bt_config_component_create(type, plugin_name, comp_cls_name); + if (!cfg_comp) { + goto error; + } + + if (name) { + g_string_assign(cfg_comp->instance_name, name); + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg_comp); + +end: + g_free(name); + g_free(plugin_name); + g_free(comp_cls_name); + return cfg_comp; +} + +/* + * Destroys a configuration. + */ +static +void bt_config_destroy(bt_object *obj) +{ + struct bt_config *cfg = + container_of(obj, struct bt_config, base); + + if (!obj) { + goto end; + } + + BT_VALUE_PUT_REF_AND_RESET(cfg->plugin_paths); + + switch (cfg->command) { + case BT_CONFIG_COMMAND_RUN: + if (cfg->cmd_data.run.sources) { + g_ptr_array_free(cfg->cmd_data.run.sources, TRUE); + } + + if (cfg->cmd_data.run.filters) { + g_ptr_array_free(cfg->cmd_data.run.filters, TRUE); + } + + if (cfg->cmd_data.run.sinks) { + g_ptr_array_free(cfg->cmd_data.run.sinks, TRUE); + } + + if (cfg->cmd_data.run.connections) { + g_ptr_array_free(cfg->cmd_data.run.connections, + TRUE); + } + break; + case BT_CONFIG_COMMAND_LIST_PLUGINS: + break; + case BT_CONFIG_COMMAND_HELP: + BT_OBJECT_PUT_REF_AND_RESET(cfg->cmd_data.help.cfg_component); + break; + case BT_CONFIG_COMMAND_QUERY: + BT_OBJECT_PUT_REF_AND_RESET(cfg->cmd_data.query.cfg_component); + + if (cfg->cmd_data.query.object) { + g_string_free(cfg->cmd_data.query.object, TRUE); + } + break; + case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: + if (cfg->cmd_data.print_ctf_metadata.path) { + g_string_free(cfg->cmd_data.print_ctf_metadata.path, + TRUE); + g_string_free( + cfg->cmd_data.print_ctf_metadata.output_path, + TRUE); + } + break; + case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: + if (cfg->cmd_data.print_lttng_live_sessions.url) { + g_string_free( + cfg->cmd_data.print_lttng_live_sessions.url, + TRUE); + g_string_free( + cfg->cmd_data.print_lttng_live_sessions.output_path, + TRUE); + } + break; + default: + abort(); + } + + g_free(cfg); + +end: + return; +} + +static +void destroy_glist_of_gstring(GList *list) +{ + GList *at; + + if (!list) { + return; + } + + for (at = list; at != NULL; at = g_list_next(at)) { + g_string_free(at->data, TRUE); + } + + g_list_free(list); +} + +/* + * Creates a simple lexical scanner for parsing comma-delimited names + * and fields. + * + * Return value is owned by the caller. + */ +static +GScanner *create_csv_identifiers_scanner(void) +{ + GScanner *scanner; + GScannerConfig scanner_config = { + .cset_skip_characters = " \t\n", + .cset_identifier_first = G_CSET_a_2_z G_CSET_A_2_Z "_", + .cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z ":_-", + .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, + }; + + scanner = g_scanner_new(&scanner_config); + if (!scanner) { + print_err_oom(); + } + + return scanner; +} + +/* + * Converts a comma-delimited list of known names (--names option) to + * an array value object containing those names as string value objects. + * + * Return value is owned by the caller. + */ +static +bt_value *names_from_arg(const char *arg) +{ + GScanner *scanner = NULL; + bt_value *names = NULL; + bool found_all = false, found_none = false, found_item = false; + + names = bt_value_array_create(); + if (!names) { + print_err_oom(); + goto error; + } + + scanner = create_csv_identifiers_scanner(); + if (!scanner) { + goto error; + } + + g_scanner_input_text(scanner, arg, strlen(arg)); + + while (true) { + GTokenType token_type = g_scanner_get_next_token(scanner); + + switch (token_type) { + case G_TOKEN_IDENTIFIER: + { + const char *identifier = scanner->value.v_identifier; + + if (!strcmp(identifier, "payload") || + !strcmp(identifier, "args") || + !strcmp(identifier, "arg")) { + found_item = true; + if (bt_value_array_append_string_element(names, + "payload")) { + goto error; + } + } else if (!strcmp(identifier, "context") || + !strcmp(identifier, "ctx")) { + found_item = true; + if (bt_value_array_append_string_element(names, + "context")) { + goto error; + } + } else if (!strcmp(identifier, "scope") || + !strcmp(identifier, "header")) { + found_item = true; + if (bt_value_array_append_string_element(names, + identifier)) { + goto error; + } + } else if (!strcmp(identifier, "all")) { + found_all = true; + if (bt_value_array_append_string_element(names, + identifier)) { + goto error; + } + } else if (!strcmp(identifier, "none")) { + found_none = true; + if (bt_value_array_append_string_element(names, + identifier)) { + goto error; + } + } else { + printf_err("Unknown name: `%s`\n", + identifier); + goto error; + } + break; + } + case G_TOKEN_COMMA: + continue; + case G_TOKEN_EOF: + goto end; + default: + goto error; + } + } + +end: + if (found_none && found_all) { + printf_err("Only either `all` or `none` can be specified in the list given to the --names option, but not both.\n"); + goto error; + } + /* + * Legacy behavior is to clear the defaults (show none) when at + * least one item is specified. + */ + if (found_item && !found_none && !found_all) { + if (bt_value_array_append_string_element(names, "none")) { + goto error; + } + } + if (scanner) { + g_scanner_destroy(scanner); + } + return names; + +error: + BT_VALUE_PUT_REF_AND_RESET(names); + if (scanner) { + g_scanner_destroy(scanner); + } + return names; +} + +/* + * Converts a comma-delimited list of known fields (--fields option) to + * an array value object containing those fields as string + * value objects. + * + * Return value is owned by the caller. + */ +static +bt_value *fields_from_arg(const char *arg) +{ + GScanner *scanner = NULL; + bt_value *fields; + + fields = bt_value_array_create(); + if (!fields) { + print_err_oom(); + goto error; + } + + scanner = create_csv_identifiers_scanner(); + if (!scanner) { + goto error; + } + + g_scanner_input_text(scanner, arg, strlen(arg)); + + while (true) { + GTokenType token_type = g_scanner_get_next_token(scanner); + + switch (token_type) { + case G_TOKEN_IDENTIFIER: + { + const char *identifier = scanner->value.v_identifier; + + if (!strcmp(identifier, "trace") || + !strcmp(identifier, "trace:hostname") || + !strcmp(identifier, "trace:domain") || + !strcmp(identifier, "trace:procname") || + !strcmp(identifier, "trace:vpid") || + !strcmp(identifier, "loglevel") || + !strcmp(identifier, "emf") || + !strcmp(identifier, "callsite") || + !strcmp(identifier, "all")) { + if (bt_value_array_append_string_element(fields, + identifier)) { + goto error; + } + } else { + printf_err("Unknown field: `%s`\n", + identifier); + goto error; + } + break; + } + case G_TOKEN_COMMA: + continue; + case G_TOKEN_EOF: + goto end; + default: + goto error; + } + } + + goto end; + +error: + BT_VALUE_PUT_REF_AND_RESET(fields); + +end: + if (scanner) { + g_scanner_destroy(scanner); + } + return fields; +} + +static +void append_param_arg(GString *params_arg, const char *key, const char *value) +{ + BT_ASSERT(params_arg); + BT_ASSERT(key); + BT_ASSERT(value); + + if (params_arg->len != 0) { + g_string_append_c(params_arg, ','); + } + + g_string_append(params_arg, key); + g_string_append_c(params_arg, '='); + g_string_append(params_arg, value); +} + +/* + * Inserts the equivalent "prefix-NAME=yes" strings into params_arg + * where the names are in names_array. + */ +static +int insert_flat_params_from_array(GString *params_arg, + const bt_value *names_array, const char *prefix) +{ + int ret = 0; + int i; + GString *tmpstr = NULL, *default_value = NULL; + bool default_set = false, non_default_set = false; + + /* + * names_array may be NULL if no CLI options were specified to + * trigger its creation. + */ + if (!names_array) { + goto end; + } + + tmpstr = g_string_new(NULL); + if (!tmpstr) { + print_err_oom(); + ret = -1; + goto end; + } + + default_value = g_string_new(NULL); + if (!default_value) { + print_err_oom(); + ret = -1; + goto end; + } + + for (i = 0; i < bt_value_array_get_size(names_array); i++) { + const bt_value *str_obj = + bt_value_array_borrow_element_by_index_const(names_array, + i); + const char *suffix; + bool is_default = false; + + if (!str_obj) { + printf_err("Unexpected error\n"); + ret = -1; + goto end; + } + + suffix = bt_value_string_get(str_obj); + + g_string_assign(tmpstr, prefix); + g_string_append(tmpstr, "-"); + + /* Special-case for "all" and "none". */ + if (!strcmp(suffix, "all")) { + is_default = true; + g_string_assign(default_value, "show"); + } else if (!strcmp(suffix, "none")) { + is_default = true; + g_string_assign(default_value, "hide"); + } + if (is_default) { + default_set = true; + g_string_append(tmpstr, "default"); + append_param_arg(params_arg, tmpstr->str, + default_value->str); + } else { + non_default_set = true; + g_string_append(tmpstr, suffix); + append_param_arg(params_arg, tmpstr->str, "yes"); + } + } + + /* Implicit field-default=hide if any non-default option is set. */ + if (non_default_set && !default_set) { + g_string_assign(tmpstr, prefix); + g_string_append(tmpstr, "-default"); + g_string_assign(default_value, "hide"); + append_param_arg(params_arg, tmpstr->str, default_value->str); + } + +end: + if (default_value) { + g_string_free(default_value, TRUE); + } + + if (tmpstr) { + g_string_free(tmpstr, TRUE); + } + + return ret; +} + +/* popt options */ +enum { + OPT_NONE = 0, + OPT_BASE_PARAMS, + OPT_BEGIN, + OPT_CLOCK_CYCLES, + OPT_CLOCK_DATE, + OPT_CLOCK_FORCE_CORRELATE, + OPT_CLOCK_GMT, + OPT_CLOCK_OFFSET, + OPT_CLOCK_OFFSET_NS, + OPT_CLOCK_SECONDS, + OPT_COLOR, + OPT_COMPONENT, + OPT_CONNECT, + OPT_DEBUG, + OPT_DEBUG_INFO, + OPT_DEBUG_INFO_DIR, + OPT_DEBUG_INFO_FULL_PATH, + OPT_DEBUG_INFO_TARGET_PREFIX, + OPT_END, + OPT_FIELDS, + OPT_HELP, + OPT_INPUT_FORMAT, + OPT_LIST, + OPT_NAME, + OPT_NAMES, + OPT_NO_DELTA, + OPT_OMIT_HOME_PLUGIN_PATH, + OPT_OMIT_SYSTEM_PLUGIN_PATH, + OPT_OUTPUT, + OPT_OUTPUT_FORMAT, + OPT_PARAMS, + OPT_PATH, + OPT_PLUGIN_PATH, + OPT_RESET_BASE_PARAMS, + OPT_RETRY_DURATION, + OPT_RUN_ARGS, + OPT_RUN_ARGS_0, + OPT_STREAM_INTERSECTION, + OPT_TIMERANGE, + OPT_URL, + OPT_VERBOSE, +}; + +enum bt_config_component_dest { + BT_CONFIG_COMPONENT_DEST_UNKNOWN = -1, + BT_CONFIG_COMPONENT_DEST_SOURCE, + BT_CONFIG_COMPONENT_DEST_FILTER, + BT_CONFIG_COMPONENT_DEST_SINK, +}; + +/* + * Adds a configuration component to the appropriate configuration + * array depending on the destination. + */ +static +void add_run_cfg_comp(struct bt_config *cfg, + struct bt_config_component *cfg_comp, + enum bt_config_component_dest dest) +{ + bt_object_get_ref(cfg_comp); + + switch (dest) { + case BT_CONFIG_COMPONENT_DEST_SOURCE: + g_ptr_array_add(cfg->cmd_data.run.sources, cfg_comp); + break; + case BT_CONFIG_COMPONENT_DEST_FILTER: + g_ptr_array_add(cfg->cmd_data.run.filters, cfg_comp); + break; + case BT_CONFIG_COMPONENT_DEST_SINK: + g_ptr_array_add(cfg->cmd_data.run.sinks, cfg_comp); + break; + default: + abort(); + } +} + +static +int add_run_cfg_comp_check_name(struct bt_config *cfg, + struct bt_config_component *cfg_comp, + enum bt_config_component_dest dest, + bt_value *instance_names) +{ + int ret = 0; + + if (cfg_comp->instance_name->len == 0) { + printf_err("Found an unnamed component\n"); + ret = -1; + goto end; + } + + if (bt_value_map_has_entry(instance_names, + cfg_comp->instance_name->str)) { + printf_err("Duplicate component instance name:\n %s\n", + cfg_comp->instance_name->str); + ret = -1; + goto end; + } + + if (bt_value_map_insert_entry(instance_names, + cfg_comp->instance_name->str, bt_value_null)) { + print_err_oom(); + ret = -1; + goto end; + } + + add_run_cfg_comp(cfg, cfg_comp, dest); + +end: + return ret; +} + +static +int append_env_var_plugin_paths(bt_value *plugin_paths) +{ + int ret = 0; + const char *envvar; + + if (bt_common_is_setuid_setgid()) { + BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); + goto end; + } + + envvar = getenv("BABELTRACE_PLUGIN_PATH"); + if (!envvar) { + goto end; + } + + ret = bt_config_append_plugin_paths(plugin_paths, envvar); + +end: + if (ret) { + printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); + } + + return ret; +} + +static +int append_home_and_system_plugin_paths(bt_value *plugin_paths, + bool omit_system_plugin_path, bool omit_home_plugin_path) +{ + int ret; + + if (!omit_home_plugin_path) { + if (bt_common_is_setuid_setgid()) { + BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); + } else { + char *home_plugin_dir = + bt_common_get_home_plugin_path(); + + if (home_plugin_dir) { + ret = bt_config_append_plugin_paths( + plugin_paths, home_plugin_dir); + free(home_plugin_dir); + + if (ret) { + printf_err("Invalid home plugin path\n"); + goto error; + } + } + } + } + + if (!omit_system_plugin_path) { + if (bt_config_append_plugin_paths(plugin_paths, + bt_common_get_system_plugin_path())) { + printf_err("Invalid system plugin path\n"); + goto error; + } + } + return 0; +error: + printf_err("Cannot append home and system plugin paths\n"); + return -1; +} + +static +int append_home_and_system_plugin_paths_cfg(struct bt_config *cfg) +{ + return append_home_and_system_plugin_paths(cfg->plugin_paths, + cfg->omit_system_plugin_path, cfg->omit_home_plugin_path); +} + +static +struct bt_config *bt_config_base_create(enum bt_config_command command, + const bt_value *initial_plugin_paths, + bool needs_plugins) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = g_new0(struct bt_config, 1); + if (!cfg) { + print_err_oom(); + goto error; + } + + bt_object_init_shared(&cfg->base, bt_config_destroy); + cfg->command = command; + cfg->command_needs_plugins = needs_plugins; + + if (initial_plugin_paths) { + bt_value *initial_plugin_paths_copy; + + (void) bt_value_copy(initial_plugin_paths, + &initial_plugin_paths_copy); + cfg->plugin_paths = initial_plugin_paths_copy; + } else { + cfg->plugin_paths = bt_value_array_create(); + if (!cfg->plugin_paths) { + print_err_oom(); + goto error; + } + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_run_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_RUN, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + cfg->cmd_data.run.sources = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); + if (!cfg->cmd_data.run.sources) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.run.filters = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); + if (!cfg->cmd_data.run.filters) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.run.sinks = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); + if (!cfg->cmd_data.run.sinks) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.run.connections = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_config_connection_destroy); + if (!cfg->cmd_data.run.connections) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_list_plugins_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_LIST_PLUGINS, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_help_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_HELP, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + cfg->cmd_data.help.cfg_component = + bt_config_component_create(-1, NULL, NULL); + if (!cfg->cmd_data.help.cfg_component) { + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_query_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_QUERY, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + cfg->cmd_data.query.object = g_string_new(NULL); + if (!cfg->cmd_data.query.object) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_print_ctf_metadata_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_CTF_METADATA, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + cfg->cmd_data.print_ctf_metadata.path = g_string_new(NULL); + if (!cfg->cmd_data.print_ctf_metadata.path) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.print_ctf_metadata.output_path = g_string_new(NULL); + if (!cfg->cmd_data.print_ctf_metadata.output_path) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +struct bt_config *bt_config_print_lttng_live_sessions_create( + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg; + + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS, + initial_plugin_paths, true); + if (!cfg) { + goto error; + } + + cfg->cmd_data.print_lttng_live_sessions.url = g_string_new(NULL); + if (!cfg->cmd_data.print_lttng_live_sessions.url) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.print_lttng_live_sessions.output_path = + g_string_new(NULL); + if (!cfg->cmd_data.print_lttng_live_sessions.output_path) { + print_err_oom(); + goto error; + } + + goto end; + +error: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + return cfg; +} + +static +int bt_config_append_plugin_paths_check_setuid_setgid( + bt_value *plugin_paths, const char *arg) +{ + int ret = 0; + + if (bt_common_is_setuid_setgid()) { + BT_LOGI_STR("Skipping non-system plugin paths for setuid/setgid binary."); + goto end; + } + + if (bt_config_append_plugin_paths(plugin_paths, arg)) { + printf_err("Invalid --plugin-path option's argument:\n %s\n", + arg); + ret = -1; + goto end; + } + +end: + return ret; +} + +/* + * Prints the expected format for a --params option. + */ +static +void print_expected_params_format(FILE *fp) +{ + fprintf(fp, "Expected format of PARAMS\n"); + fprintf(fp, "-------------------------\n"); + fprintf(fp, "\n"); + fprintf(fp, " PARAM=VALUE[,PARAM=VALUE]...\n"); + fprintf(fp, "\n"); + fprintf(fp, "The parameter string is a comma-separated list of PARAM=VALUE assignments,\n"); + fprintf(fp, "where PARAM is the parameter name (C identifier plus the [:.-] characters),\n"); + fprintf(fp, "and VALUE can be one of:\n"); + fprintf(fp, "\n"); + fprintf(fp, "* `null`, `nul`, `NULL`: null value (no backticks).\n"); + fprintf(fp, "* `true`, `TRUE`, `yes`, `YES`: true boolean value (no backticks).\n"); + fprintf(fp, "* `false`, `FALSE`, `no`, `NO`: false boolean value (no backticks).\n"); + fprintf(fp, "* Binary (`0b` prefix), octal (`0` prefix), decimal, or hexadecimal\n"); + fprintf(fp, " (`0x` prefix) unsigned (with `+` prefix) or signed 64-bit integer.\n"); + fprintf(fp, "* Double precision floating point number (scientific notation is accepted).\n"); + fprintf(fp, "* Unquoted string with no special characters, and not matching any of\n"); + fprintf(fp, " the null and boolean value symbols above.\n"); + fprintf(fp, "* Double-quoted string (accepts escape characters).\n"); + fprintf(fp, "* Array, formatted as an opening `[`, a list of comma-separated values\n"); + fprintf(fp, " (as described by the current list) and a closing `]`.\n"); + fprintf(fp, "\n"); + fprintf(fp, "You can put whitespaces allowed around individual `=` and `,` symbols.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Example:\n"); + fprintf(fp, "\n"); + fprintf(fp, " many=null, fresh=yes, condition=false, squirrel=-782329,\n"); + fprintf(fp, " play=+23, observe=3.14, simple=beef, needs-quotes=\"some string\",\n"); + fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\",\n"); + fprintf(fp, " things=[1, \"2\", 3]\n"); + fprintf(fp, "\n"); + fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n"); + fprintf(fp, "babeltrace2 from a shell.\n"); +} + + +/* + * Prints the help command usage. + */ +static +void print_help_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] help [OPTIONS] PLUGIN\n"); + fprintf(fp, " babeltrace2 [GENERAL OPTIONS] help [OPTIONS] TYPE.PLUGIN.CLS\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); + fprintf(fp, " dynamic plugins can be loaded\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Use `babeltrace2 list-plugins` to show the list of available plugins.\n"); +} + +static +struct poptOption help_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, + { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, + { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, + { NULL, 0, '\0', NULL, 0, NULL, NULL }, +}; + +/* + * Creates a Babeltrace config object from the arguments of a help + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +static +struct bt_config *bt_config_help_from_args(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + int opt; + int ret; + struct bt_config *cfg = NULL; + const char *leftover; + char *plugin_name = NULL, *comp_cls_name = NULL; + + *retcode = 0; + cfg = bt_config_help_create(initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->omit_system_plugin_path = force_omit_system_plugin_path; + cfg->omit_home_plugin_path = force_omit_home_plugin_path; + ret = append_env_var_plugin_paths(cfg->plugin_paths); + if (ret) { + goto error; + } + + /* Parse options */ + pc = poptGetContext(NULL, argc, (const char **) argv, + help_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + cfg->plugin_paths, arg)) { + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->omit_home_plugin_path = true; + break; + case OPT_HELP: + print_help_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + leftover = poptGetArg(pc); + if (leftover) { + plugin_comp_cls_names(leftover, NULL, + &plugin_name, &comp_cls_name, + &cfg->cmd_data.help.cfg_component->type); + if (plugin_name && comp_cls_name) { + /* Component class help */ + g_string_assign( + cfg->cmd_data.help.cfg_component->plugin_name, + plugin_name); + g_string_assign( + cfg->cmd_data.help.cfg_component->comp_cls_name, + comp_cls_name); + } else { + /* Fall back to plugin help */ + g_string_assign( + cfg->cmd_data.help.cfg_component->plugin_name, + leftover); + } + } else { + print_help_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + } + + if (append_home_and_system_plugin_paths_cfg(cfg)) { + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + g_free(plugin_name); + g_free(comp_cls_name); + + if (pc) { + poptFreeContext(pc); + } + + free(arg); + return cfg; +} + +/* + * Prints the help command usage. + */ +static +void print_query_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GEN OPTS] query [OPTS] TYPE.PLUGIN.CLS OBJECT\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " -p, --params=PARAMS Set the query parameters to PARAMS\n"); + fprintf(fp, " (see the expected format of PARAMS below)\n"); + fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); + fprintf(fp, " dynamic plugins can be loaded\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, "\n\n"); + print_expected_params_format(fp); +} + +static +struct poptOption query_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, + { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, + { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, + { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, + { NULL, 0, '\0', NULL, 0, NULL, NULL }, +}; + +/* + * Creates a Babeltrace config object from the arguments of a query + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +static +struct bt_config *bt_config_query_from_args(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + int opt; + int ret; + struct bt_config *cfg = NULL; + const char *leftover; + bt_value *params; + + params = bt_value_null; + bt_value_get_ref(bt_value_null); + + *retcode = 0; + cfg = bt_config_query_create(initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->omit_system_plugin_path = force_omit_system_plugin_path; + cfg->omit_home_plugin_path = force_omit_home_plugin_path; + ret = append_env_var_plugin_paths(cfg->plugin_paths); + if (ret) { + goto error; + } + + /* Parse options */ + pc = poptGetContext(NULL, argc, (const char **) argv, + query_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + cfg->plugin_paths, arg)) { + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->omit_home_plugin_path = true; + break; + case OPT_PARAMS: + { + bt_value_put_ref(params); + params = bt_value_from_arg(arg); + if (!params) { + printf_err("Invalid format for --params option's argument:\n %s\n", + arg); + goto error; + } + break; + } + case OPT_HELP: + print_query_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + /* + * We need exactly two leftover arguments which are the + * mandatory component class specification and query object. + */ + leftover = poptGetArg(pc); + if (leftover) { + cfg->cmd_data.query.cfg_component = + bt_config_component_from_arg(leftover); + if (!cfg->cmd_data.query.cfg_component) { + printf_err("Invalid format for component class specification:\n %s\n", + leftover); + goto error; + } + + BT_ASSERT(params); + BT_OBJECT_MOVE_REF(cfg->cmd_data.query.cfg_component->params, + params); + } else { + print_query_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + } + + leftover = poptGetArg(pc); + if (leftover) { + if (strlen(leftover) == 0) { + printf_err("Invalid empty object\n"); + goto error; + } + + g_string_assign(cfg->cmd_data.query.object, leftover); + } else { + print_query_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + } + + leftover = poptGetArg(pc); + if (leftover) { + printf_err("Unexpected argument: %s\n", leftover); + goto error; + } + + if (append_home_and_system_plugin_paths_cfg(cfg)) { + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + bt_value_put_ref(params); + free(arg); + return cfg; +} + +/* + * Prints the list-plugins command usage. + */ +static +void print_list_plugins_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] list-plugins [OPTIONS]\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); + fprintf(fp, " dynamic plugins can be loaded\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Use `babeltrace2 help` to get help for a specific plugin or component class.\n"); +} + +static +struct poptOption list_plugins_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, + { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, + { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, + { NULL, 0, '\0', NULL, 0, NULL, NULL }, +}; + +/* + * Creates a Babeltrace config object from the arguments of a + * list-plugins command. + * + * *retcode is set to the appropriate exit code to use. + */ +static +struct bt_config *bt_config_list_plugins_from_args(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + int opt; + int ret; + struct bt_config *cfg = NULL; + const char *leftover; + + *retcode = 0; + cfg = bt_config_list_plugins_create(initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->omit_system_plugin_path = force_omit_system_plugin_path; + cfg->omit_home_plugin_path = force_omit_home_plugin_path; + ret = append_env_var_plugin_paths(cfg->plugin_paths); + if (ret) { + goto error; + } + + /* Parse options */ + pc = poptGetContext(NULL, argc, (const char **) argv, + list_plugins_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + cfg->plugin_paths, arg)) { + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->omit_home_plugin_path = true; + break; + case OPT_HELP: + print_list_plugins_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + leftover = poptGetArg(pc); + if (leftover) { + printf_err("Unexpected argument: %s\n", leftover); + goto error; + } + + if (append_home_and_system_plugin_paths_cfg(cfg)) { + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + free(arg); + return cfg; +} + +/* + * Prints the run command usage. + */ +static +void print_run_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] run [OPTIONS]\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n"); + fprintf(fp, " for all the following components until\n"); + fprintf(fp, " --reset-base-params is encountered\n"); + fprintf(fp, " (see the expected format of PARAMS below)\n"); + fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n"); + fprintf(fp, " Instantiate the component class CLS of type\n"); + fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n"); + fprintf(fp, " in the plugin PLUGIN, add it to the graph,\n"); + fprintf(fp, " and optionally name it NAME (you can also\n"); + fprintf(fp, " specify the name with --name)\n"); + fprintf(fp, " -x, --connect=CONNECTION Connect two created components (see the\n"); + fprintf(fp, " expected format of CONNECTION below)\n"); + fprintf(fp, " -n, --name=NAME Set the name of the current component\n"); + fprintf(fp, " to NAME (must be unique amongst all the\n"); + fprintf(fp, " names of the created components)\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n"); + fprintf(fp, " current component (see the expected format\n"); + fprintf(fp, " of PARAMS below)\n"); + fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); + fprintf(fp, " dynamic plugins can be loaded\n"); + fprintf(fp, " -r, --reset-base-params Reset the current base parameters to an\n"); + fprintf(fp, " empty map\n"); + fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n"); + fprintf(fp, " the graph later, retry in DUR µs\n"); + fprintf(fp, " (default: 100000)\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); + fprintf(fp, "\n\n"); + fprintf(fp, "Expected format of CONNECTION\n"); + fprintf(fp, "-----------------------------\n"); + fprintf(fp, "\n"); + fprintf(fp, " UPSTREAM[.UPSTREAM-PORT]:DOWNSTREAM[.DOWNSTREAM-PORT]\n"); + fprintf(fp, "\n"); + fprintf(fp, "UPSTREAM and DOWNSTREAM are names of the upstream and downstream\n"); + fprintf(fp, "components to connect together. You must escape the following characters\n\n"); + fprintf(fp, "with `\\`: `\\`, `.`, and `:`. You can set the name of the current\n"); + fprintf(fp, "component with the --name option.\n"); + fprintf(fp, "\n"); + fprintf(fp, "UPSTREAM-PORT and DOWNSTREAM-PORT are optional globbing patterns to\n"); + fprintf(fp, "identify the upstream and downstream ports to use for the connection.\n"); + fprintf(fp, "When the port is not specified, `*` is used.\n"); + fprintf(fp, "\n"); + fprintf(fp, "When a component named UPSTREAM has an available port which matches the\n"); + fprintf(fp, "UPSTREAM-PORT globbing pattern, it is connected to the first port which\n"); + fprintf(fp, "matches the DOWNSTREAM-PORT globbing pattern of the component named\n"); + fprintf(fp, "DOWNSTREAM.\n"); + fprintf(fp, "\n"); + fprintf(fp, "The only special character in UPSTREAM-PORT and DOWNSTREAM-PORT is `*`\n"); + fprintf(fp, "which matches anything. You must escape the following characters\n"); + fprintf(fp, "with `\\`: `\\`, `*`, `?`, `[`, `.`, and `:`.\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, "Examples:\n"); + fprintf(fp, "\n"); + fprintf(fp, " my-src:my-sink\n"); + fprintf(fp, " ctf-fs.*stream*:utils-muxer:*\n"); + fprintf(fp, "\n"); + fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run\n"); + fprintf(fp, "babeltrace2 from a shell.\n"); + fprintf(fp, "\n\n"); + print_expected_params_format(fp); +} + +/* + * Creates a Babeltrace config object from the arguments of a run + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +static +struct bt_config *bt_config_run_from_args(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + struct bt_config_component *cur_cfg_comp = NULL; + enum bt_config_component_dest cur_cfg_comp_dest = + BT_CONFIG_COMPONENT_DEST_UNKNOWN; + bt_value *cur_base_params = NULL; + int opt, ret = 0; + struct bt_config *cfg = NULL; + bt_value *instance_names = NULL; + bt_value *connection_args = NULL; + char error_buf[256] = { 0 }; + long retry_duration = -1; + bt_value_status status; + struct poptOption run_long_options[] = { + { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL }, + { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, NULL, NULL }, + { "connect", 'x', POPT_ARG_STRING, NULL, OPT_CONNECT, NULL, NULL }, + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "name", 'n', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL }, + { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, + { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, + { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, + { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, + { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, NULL, NULL }, + { "retry-duration", '\0', POPT_ARG_LONG, &retry_duration, OPT_RETRY_DURATION, NULL, NULL }, + { NULL, 0, '\0', NULL, 0, NULL, NULL }, + }; + + *retcode = 0; + + if (argc <= 1) { + print_run_usage(stdout); + *retcode = -1; + goto end; + } + + cfg = bt_config_run_create(initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->cmd_data.run.retry_duration_us = 100000; + cfg->omit_system_plugin_path = force_omit_system_plugin_path; + cfg->omit_home_plugin_path = force_omit_home_plugin_path; + cur_base_params = bt_value_map_create(); + if (!cur_base_params) { + print_err_oom(); + goto error; + } + + instance_names = bt_value_map_create(); + if (!instance_names) { + print_err_oom(); + goto error; + } + + connection_args = bt_value_array_create(); + if (!connection_args) { + print_err_oom(); + goto error; + } + + ret = append_env_var_plugin_paths(cfg->plugin_paths); + if (ret) { + goto error; + } + + /* Parse options */ + pc = poptGetContext(NULL, argc, (const char **) argv, + run_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + cfg->plugin_paths, arg)) { + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->omit_home_plugin_path = true; + break; + case OPT_COMPONENT: + { + enum bt_config_component_dest new_dest; + + if (cur_cfg_comp) { + ret = add_run_cfg_comp_check_name(cfg, + cur_cfg_comp, cur_cfg_comp_dest, + instance_names); + BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); + if (ret) { + goto error; + } + } + + cur_cfg_comp = bt_config_component_from_arg(arg); + if (!cur_cfg_comp) { + printf_err("Invalid format for --component option's argument:\n %s\n", + arg); + goto error; + } + + switch (cur_cfg_comp->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + new_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + new_dest = BT_CONFIG_COMPONENT_DEST_FILTER; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + new_dest = BT_CONFIG_COMPONENT_DEST_SINK; + break; + default: + abort(); + } + + BT_ASSERT(cur_base_params); + bt_value_put_ref(cur_cfg_comp->params); + status = bt_value_copy(cur_base_params, + &cur_cfg_comp->params); + if (status != BT_VALUE_STATUS_OK) { + print_err_oom(); + goto error; + } + + cur_cfg_comp_dest = new_dest; + break; + } + case OPT_PARAMS: + { + bt_value *params; + bt_value *params_to_set; + + if (!cur_cfg_comp) { + printf_err("Cannot add parameters to unavailable component:\n %s\n", + arg); + goto error; + } + + params = bt_value_from_arg(arg); + if (!params) { + printf_err("Invalid format for --params option's argument:\n %s\n", + arg); + goto error; + } + + status = bt_value_map_extend(cur_cfg_comp->params, + params, ¶ms_to_set); + BT_VALUE_PUT_REF_AND_RESET(params); + if (status != BT_VALUE_STATUS_OK) { + printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n", + arg); + goto error; + } + + BT_OBJECT_MOVE_REF(cur_cfg_comp->params, params_to_set); + break; + } + case OPT_NAME: + if (!cur_cfg_comp) { + printf_err("Cannot set the name of unavailable component:\n %s\n", + arg); + goto error; + } + + g_string_assign(cur_cfg_comp->instance_name, arg); + break; + case OPT_BASE_PARAMS: + { + bt_value *params = + bt_value_from_arg(arg); + + if (!params) { + printf_err("Invalid format for --base-params option's argument:\n %s\n", + arg); + goto error; + } + + BT_OBJECT_MOVE_REF(cur_base_params, params); + break; + } + case OPT_RESET_BASE_PARAMS: + BT_VALUE_PUT_REF_AND_RESET(cur_base_params); + cur_base_params = bt_value_map_create(); + if (!cur_base_params) { + print_err_oom(); + goto error; + } + break; + case OPT_CONNECT: + if (bt_value_array_append_string_element( + connection_args, arg)) { + print_err_oom(); + goto error; + } + break; + case OPT_RETRY_DURATION: + if (retry_duration < 0) { + printf_err("--retry-duration option's argument must be positive or 0: %ld\n", + retry_duration); + goto error; + } + + cfg->cmd_data.run.retry_duration_us = + (uint64_t) retry_duration; + break; + case OPT_HELP: + print_run_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + /* This command does not accept leftover arguments */ + if (poptPeekArg(pc)) { + printf_err("Unexpected argument: %s\n", poptPeekArg(pc)); + goto error; + } + + /* Add current component */ + if (cur_cfg_comp) { + ret = add_run_cfg_comp_check_name(cfg, cur_cfg_comp, + cur_cfg_comp_dest, instance_names); + BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); + if (ret) { + goto error; + } + } + + if (cfg->cmd_data.run.sources->len == 0) { + printf_err("Incomplete graph: no source component\n"); + goto error; + } + + if (cfg->cmd_data.run.sinks->len == 0) { + printf_err("Incomplete graph: no sink component\n"); + goto error; + } + + if (append_home_and_system_plugin_paths_cfg(cfg)) { + goto error; + } + + ret = bt_config_cli_args_create_connections(cfg, + connection_args, + error_buf, 256); + if (ret) { + printf_err("Cannot creation connections:\n%s", error_buf); + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + free(arg); + BT_OBJECT_PUT_REF_AND_RESET(cur_cfg_comp); + BT_VALUE_PUT_REF_AND_RESET(cur_base_params); + BT_VALUE_PUT_REF_AND_RESET(instance_names); + BT_VALUE_PUT_REF_AND_RESET(connection_args); + return cfg; +} + +static +struct bt_config *bt_config_run_from_args_array(const bt_value *run_args, + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + struct bt_config *cfg = NULL; + const char **argv; + int64_t i, len; + const size_t argc = bt_value_array_get_size(run_args) + 1; + + argv = calloc(argc, sizeof(*argv)); + if (!argv) { + print_err_oom(); + goto end; + } + + argv[0] = "run"; + + len = bt_value_array_get_size(run_args); + if (len < 0) { + printf_err("Invalid executable arguments\n"); + goto end; + } + for (i = 0; i < len; i++) { + const bt_value *arg_value = + bt_value_array_borrow_element_by_index_const(run_args, + i); + const char *arg; + + BT_ASSERT(arg_value); + arg = bt_value_string_get(arg_value); + BT_ASSERT(arg); + argv[i + 1] = arg; + } + + cfg = bt_config_run_from_args(argc, argv, retcode, + force_omit_system_plugin_path, force_omit_home_plugin_path, + initial_plugin_paths); + +end: + free(argv); + return cfg; +} + +/* + * Prints the convert command usage. + */ +static +void print_convert_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [convert] [OPTIONS] [PATH/URL]\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -c, --component=[NAME:]TYPE.PLUGIN.CLS\n"); + fprintf(fp, " Instantiate the component class CLS of type\n"); + fprintf(fp, " TYPE (`source`, `filter`, or `sink`) found\n"); + fprintf(fp, " in the plugin PLUGIN, add it to the\n"); + fprintf(fp, " conversion graph, and optionally name it\n"); + fprintf(fp, " NAME (you can also specify the name with\n"); + fprintf(fp, " --name)\n"); + fprintf(fp, " --name=NAME Set the name of the current component\n"); + fprintf(fp, " to NAME (must be unique amongst all the\n"); + fprintf(fp, " names of the created components)\n"); + fprintf(fp, " --omit-home-plugin-path Omit home plugins from plugin search path\n"); + fprintf(fp, " (~/.local/lib/babeltrace/plugins)\n"); + fprintf(fp, " --omit-system-plugin-path Omit system plugins from plugin search path\n"); + fprintf(fp, " -p, --params=PARAMS Add initialization parameters PARAMS to the\n"); + fprintf(fp, " current component (see the expected format\n"); + fprintf(fp, " of PARAMS below)\n"); + fprintf(fp, " -P, --path=PATH Set the `path` string parameter of the\n"); + fprintf(fp, " current component to PATH\n"); + fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which\n"); + fprintf(fp, " --retry-duration=DUR When babeltrace2(1) needs to retry to run\n"); + fprintf(fp, " the graph later, retry in DUR µs\n"); + fprintf(fp, " (default: 100000)\n"); + fprintf(fp, " dynamic plugins can be loaded\n"); + fprintf(fp, " --run-args Print the equivalent arguments for the\n"); + fprintf(fp, " `run` command to the standard output,\n"); + fprintf(fp, " formatted for a shell, and quit\n"); + fprintf(fp, " --run-args-0 Print the equivalent arguments for the\n"); + fprintf(fp, " `run` command to the standard output,\n"); + fprintf(fp, " formatted for `xargs -0`, and quit\n"); + fprintf(fp, " --stream-intersection Only process events when all streams\n"); + fprintf(fp, " are active\n"); + fprintf(fp, " -u, --url=URL Set the `url` string parameter of the\n"); + fprintf(fp, " current component to URL\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "Implicit `source.ctf.fs` component options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n"); + fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS ns\n"); + fprintf(fp, "\n"); + fprintf(fp, "Implicit `sink.text.pretty` component options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --clock-cycles Print timestamps in clock cycles\n"); + fprintf(fp, " --clock-date Print timestamp dates\n"); + fprintf(fp, " --clock-gmt Print and parse timestamps in the GMT\n"); + fprintf(fp, " time zone instead of the local time zone\n"); + fprintf(fp, " --clock-seconds Print the timestamps as `SEC.NS` instead\n"); + fprintf(fp, " of `hh:mm:ss.nnnnnnnnn`\n"); + fprintf(fp, " --color=(never | auto | always)\n"); + fprintf(fp, " Never, automatically, or always emit\n"); + fprintf(fp, " console color codes\n"); + fprintf(fp, " -f, --fields=FIELD[,FIELD]... Print additional fields; FIELD can be:\n"); + fprintf(fp, " `all`, `trace`, `trace:hostname`,\n"); + fprintf(fp, " `trace:domain`, `trace:procname`,\n"); + fprintf(fp, " `trace:vpid`, `loglevel`, `emf`\n"); + fprintf(fp, " -n, --names=NAME[,NAME]... Print field names; NAME can be:\n"); + fprintf(fp, " `payload` (or `arg` or `args`), `none`,\n"); + fprintf(fp, " `all`, `scope`, `header`, `context`\n"); + fprintf(fp, " (or `ctx`)\n"); + fprintf(fp, " --no-delta Do not print time delta between\n"); + fprintf(fp, " consecutive events\n"); + fprintf(fp, " -w, --output=PATH Write output text to PATH instead of\n"); + fprintf(fp, " the standard output\n"); + fprintf(fp, "\n"); + fprintf(fp, "Implicit `filter.utils.muxer` component options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --clock-force-correlate Assume that clocks are inherently\n"); + fprintf(fp, " correlated across traces\n"); + fprintf(fp, "\n"); + fprintf(fp, "Implicit `filter.utils.trimmer` component options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -b, --begin=BEGIN Set the beginning time of the conversion\n"); + fprintf(fp, " time range to BEGIN (see the format of\n"); + fprintf(fp, " BEGIN below)\n"); + fprintf(fp, " -e, --end=END Set the end time of the conversion time\n"); + fprintf(fp, " range to END (see the format of END below)\n"); + fprintf(fp, " -t, --timerange=TIMERANGE Set conversion time range to TIMERANGE:\n"); + fprintf(fp, " BEGIN,END or [BEGIN,END] (literally `[` and\n"); + fprintf(fp, " `]`) (see the format of BEGIN/END below)\n"); + fprintf(fp, "\n"); + fprintf(fp, "Implicit `filter.lttng-utils.debug-info` component options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --debug-info Create an implicit\n"); + fprintf(fp, " `filter.lttng-utils.debug-info` component\n"); + fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n"); + fprintf(fp, " instead of `/usr/lib/debug`\n"); + fprintf(fp, " --debug-info-full-path Show full debug info source and\n"); + fprintf(fp, " binary paths instead of just names\n"); + fprintf(fp, " --debug-info-target-prefix=DIR\n"); + fprintf(fp, " Use directory DIR as a prefix when\n"); + fprintf(fp, " looking up executables during debug\n"); + fprintf(fp, " info analysis\n"); + fprintf(fp, "\n"); + fprintf(fp, "Legacy options that still work:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -i, --input-format=(ctf | lttng-live)\n"); + fprintf(fp, " `ctf`:\n"); + fprintf(fp, " Create an implicit `source.ctf.fs`\n"); + fprintf(fp, " component\n"); + fprintf(fp, " `lttng-live`:\n"); + fprintf(fp, " Create an implicit `source.ctf.lttng-live`\n"); + fprintf(fp, " component\n"); + fprintf(fp, " -o, --output-format=(text | ctf | dummy | ctf-metadata)\n"); + fprintf(fp, " `text`:\n"); + fprintf(fp, " Create an implicit `sink.text.pretty`\n"); + fprintf(fp, " component\n"); + fprintf(fp, " `ctf`:\n"); + fprintf(fp, " Create an implicit `sink.ctf.fs`\n"); + fprintf(fp, " component\n"); + fprintf(fp, " `dummy`:\n"); + fprintf(fp, " Create an implicit `sink.utils.dummy`\n"); + fprintf(fp, " component\n"); + fprintf(fp, " `ctf-metadata`:\n"); + fprintf(fp, " Query the `source.ctf.fs` component class\n"); + fprintf(fp, " for metadata text and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "See `babeltrace2 --help` for the list of general options.\n"); + fprintf(fp, "\n\n"); + fprintf(fp, "Format of BEGIN and END\n"); + fprintf(fp, "-----------------------\n"); + fprintf(fp, "\n"); + fprintf(fp, " [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn]\n"); + fprintf(fp, "\n\n"); + print_expected_params_format(fp); +} + +static +struct poptOption convert_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "begin", 'b', POPT_ARG_STRING, NULL, OPT_BEGIN, NULL, NULL }, + { "clock-cycles", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_CYCLES, NULL, NULL }, + { "clock-date", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_DATE, NULL, NULL }, + { "clock-force-correlate", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_FORCE_CORRELATE, NULL, NULL }, + { "clock-gmt", '\0', POPT_ARG_NONE, NULL, OPT_CLOCK_GMT, NULL, NULL }, + { "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 }, + { "color", '\0', POPT_ARG_STRING, NULL, OPT_COLOR, NULL, NULL }, + { "component", 'c', POPT_ARG_STRING, NULL, OPT_COMPONENT, 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 }, + { "debug-info-target-prefix", 0, POPT_ARG_STRING, NULL, OPT_DEBUG_INFO_TARGET_PREFIX, NULL, NULL }, + { "end", 'e', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL }, + { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL }, + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL }, + { "name", '\0', POPT_ARG_STRING, NULL, OPT_NAME, NULL, NULL }, + { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL }, + { "debug-info", '\0', POPT_ARG_NONE, NULL, OPT_DEBUG_INFO, NULL, NULL }, + { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL }, + { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, + { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, + { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT, NULL, NULL }, + { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL }, + { "params", 'p', POPT_ARG_STRING, NULL, OPT_PARAMS, NULL, NULL }, + { "path", 'P', POPT_ARG_STRING, NULL, OPT_PATH, NULL, NULL }, + { "plugin-path", '\0', POPT_ARG_STRING, NULL, OPT_PLUGIN_PATH, NULL, NULL }, + { "retry-duration", '\0', POPT_ARG_STRING, NULL, OPT_RETRY_DURATION, NULL, NULL }, + { "run-args", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS, NULL, NULL }, + { "run-args-0", '\0', POPT_ARG_NONE, NULL, OPT_RUN_ARGS_0, NULL, NULL }, + { "stream-intersection", '\0', POPT_ARG_NONE, NULL, OPT_STREAM_INTERSECTION, NULL, NULL }, + { "timerange", '\0', POPT_ARG_STRING, NULL, OPT_TIMERANGE, NULL, NULL }, + { "url", 'u', POPT_ARG_STRING, NULL, OPT_URL, NULL, NULL }, + { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL }, + { NULL, 0, '\0', NULL, 0, NULL, NULL }, +}; + +static +GString *get_component_auto_name(const char *prefix, + const bt_value *existing_names) +{ + unsigned int i = 0; + GString *auto_name = g_string_new(NULL); + + if (!auto_name) { + print_err_oom(); + goto end; + } + + if (!bt_value_map_has_entry(existing_names, prefix)) { + g_string_assign(auto_name, prefix); + goto end; + } + + do { + g_string_printf(auto_name, "%s-%d", prefix, i); + i++; + } while (bt_value_map_has_entry(existing_names, auto_name->str)); + +end: + return auto_name; +} + +struct implicit_component_args { + bool exists; + GString *comp_arg; + GString *name_arg; + GString *params_arg; + bt_value *extra_params; +}; + +static +int assign_name_to_implicit_component(struct implicit_component_args *args, + const char *prefix, bt_value *existing_names, + GList **comp_names, bool append_to_comp_names) +{ + int ret = 0; + GString *name = NULL; + + if (!args->exists) { + goto end; + } + + name = get_component_auto_name(prefix, + existing_names); + + if (!name) { + ret = -1; + goto end; + } + + g_string_assign(args->name_arg, name->str); + + if (bt_value_map_insert_entry(existing_names, name->str, + bt_value_null)) { + print_err_oom(); + ret = -1; + goto end; + } + + if (append_to_comp_names) { + *comp_names = g_list_append(*comp_names, name); + name = NULL; + } + +end: + if (name) { + g_string_free(name, TRUE); + } + + return ret; +} + +static +int append_run_args_for_implicit_component( + struct implicit_component_args *impl_args, + bt_value *run_args) +{ + int ret = 0; + size_t i; + + if (!impl_args->exists) { + goto end; + } + + if (bt_value_array_append_string_element(run_args, "--component")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, impl_args->comp_arg->str)) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, "--name")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, impl_args->name_arg->str)) { + print_err_oom(); + goto error; + } + + if (impl_args->params_arg->len > 0) { + if (bt_value_array_append_string_element(run_args, "--params")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, + impl_args->params_arg->str)) { + print_err_oom(); + goto error; + } + } + + for (i = 0; i < bt_value_array_get_size(impl_args->extra_params); + i++) { + const bt_value *elem; + const char *arg; + + elem = bt_value_array_borrow_element_by_index(impl_args->extra_params, + i); + if (!elem) { + goto error; + } + + BT_ASSERT(bt_value_is_string(elem)); + arg = bt_value_string_get(elem); + ret = bt_value_array_append_string_element(run_args, arg); + if (ret) { + print_err_oom(); + goto error; + } + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +void finalize_implicit_component_args(struct implicit_component_args *args) +{ + BT_ASSERT(args); + + if (args->comp_arg) { + g_string_free(args->comp_arg, TRUE); + } + + if (args->name_arg) { + g_string_free(args->name_arg, TRUE); + } + + if (args->params_arg) { + g_string_free(args->params_arg, TRUE); + } + + bt_value_put_ref(args->extra_params); +} + +static +int init_implicit_component_args(struct implicit_component_args *args, + const char *comp_arg, bool exists) +{ + int ret = 0; + + args->exists = exists; + args->comp_arg = g_string_new(comp_arg); + args->name_arg = g_string_new(NULL); + args->params_arg = g_string_new(NULL); + args->extra_params = bt_value_array_create(); + + if (!args->comp_arg || !args->name_arg || + !args->params_arg || !args->extra_params) { + ret = -1; + finalize_implicit_component_args(args); + print_err_oom(); + goto end; + } + +end: + return ret; +} + +static +void append_implicit_component_param(struct implicit_component_args *args, + const char *key, const char *value) +{ + BT_ASSERT(args); + BT_ASSERT(key); + BT_ASSERT(value); + append_param_arg(args->params_arg, key, value); +} + +/* Escape value to make it suitable to use as a string parameter value. */ +static +gchar *escape_string_value(const char *value) +{ + GString *ret; + const char *in; + + ret = g_string_new(NULL); + if (!ret) { + print_err_oom(); + goto end; + } + + in = value; + while (*in) { + switch (*in) { + case '"': + case '\\': + g_string_append_c(ret, '\\'); + break; + } + + g_string_append_c(ret, *in); + + in++; + } + +end: + return g_string_free(ret, FALSE); +} + +static +int bt_value_to_cli_param_value_append(const bt_value *value, GString *buf) +{ + BT_ASSERT(buf); + + int ret = -1; + + switch (bt_value_get_type(value)) { + case BT_VALUE_TYPE_STRING: + { + const char *str_value = bt_value_string_get(value); + gchar *escaped_str_value; + + escaped_str_value = escape_string_value(str_value); + if (!escaped_str_value) { + goto end; + } + + g_string_append_printf(buf, "\"%s\"", escaped_str_value); + + g_free(escaped_str_value); + break; + } + case BT_VALUE_TYPE_ARRAY: { + g_string_append_c(buf, '['); + uint64_t sz = bt_value_array_get_size(value); + for (uint64_t i = 0; i < sz; i++) { + const bt_value *item; + int ret; + + if (i > 0) { + g_string_append(buf, ", "); + } + + item = bt_value_array_borrow_element_by_index_const( + value, i); + ret = bt_value_to_cli_param_value_append(item, buf); + + if (ret) { + goto end; + } + } + g_string_append_c(buf, ']'); + break; + } + default: + abort(); + } + + ret = 0; + +end: + return ret; +} + +/* + * Convert `value` to its equivalent representation as a command line parameter + * value. + */ + +static +gchar *bt_value_to_cli_param_value(bt_value *value) +{ + GString *buf; + gchar *result = NULL; + + buf = g_string_new(NULL); + if (!buf) { + print_err_oom(); + goto error; + } + + if (bt_value_to_cli_param_value_append(value, buf)) { + goto error; + } + + result = g_string_free(buf, FALSE); + buf = NULL; + + goto end; + +error: + if (buf) { + g_string_free(buf, TRUE); + } + +end: + return result; +} + +static +int append_parameter_to_args(bt_value *args, const char *key, bt_value *value) +{ + BT_ASSERT(args); + BT_ASSERT(bt_value_get_type(args) == BT_VALUE_TYPE_ARRAY); + BT_ASSERT(key); + BT_ASSERT(value); + + int ret = 0; + gchar *str_value = NULL; + GString *parameter = NULL; + + if (bt_value_array_append_string_element(args, "--params")) { + print_err_oom(); + ret = -1; + goto end; + } + + str_value = bt_value_to_cli_param_value(value); + if (!str_value) { + ret = -1; + goto end; + } + + parameter = g_string_new(NULL); + if (!parameter) { + print_err_oom(); + ret = -1; + goto end; + } + + g_string_printf(parameter, "%s=%s", key, str_value); + + if (bt_value_array_append_string_element(args, parameter->str)) { + print_err_oom(); + ret = -1; + goto end; + } + +end: + if (parameter) { + g_string_free(parameter, TRUE); + parameter = NULL; + } + + if (str_value) { + g_free(str_value); + str_value = NULL; + } + + return ret; +} + +static +int append_string_parameter_to_args(bt_value *args, const char *key, const char *value) +{ + bt_value *str_value; + int ret; + + str_value = bt_value_string_create_init(value); + + if (!str_value) { + print_err_oom(); + ret = -1; + goto end; + } + + ret = append_parameter_to_args(args, key, str_value); + +end: + BT_VALUE_PUT_REF_AND_RESET(str_value); + return ret; +} + +static +int append_implicit_component_extra_param(struct implicit_component_args *args, + const char *key, const char *value) +{ + return append_string_parameter_to_args(args->extra_params, key, value); +} + +static +int convert_append_name_param(enum bt_config_component_dest dest, + GString *cur_name, GString *cur_name_prefix, + bt_value *run_args, + bt_value *all_names, + GList **source_names, GList **filter_names, + GList **sink_names) +{ + int ret = 0; + + if (cur_name_prefix->len > 0) { + /* We're after a --component option */ + GString *name = NULL; + bool append_name_opt = false; + + if (cur_name->len == 0) { + /* + * No explicit name was provided for the user + * component. + */ + name = get_component_auto_name(cur_name_prefix->str, + all_names); + append_name_opt = true; + } else { + /* + * An explicit name was provided for the user + * component. + */ + if (bt_value_map_has_entry(all_names, + cur_name->str)) { + printf_err("Duplicate component instance name:\n %s\n", + cur_name->str); + goto error; + } + + name = g_string_new(cur_name->str); + } + + if (!name) { + print_err_oom(); + goto error; + } + + /* + * Remember this name globally, for the uniqueness of + * all component names. + */ + if (bt_value_map_insert_entry(all_names, name->str, bt_value_null)) { + print_err_oom(); + goto error; + } + + /* + * Append the --name option if necessary. + */ + if (append_name_opt) { + if (bt_value_array_append_string_element(run_args, "--name")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, name->str)) { + print_err_oom(); + goto error; + } + } + + /* + * Remember this name specifically for the type of the + * component. This is to create connection arguments. + */ + switch (dest) { + case BT_CONFIG_COMPONENT_DEST_SOURCE: + *source_names = g_list_append(*source_names, name); + break; + case BT_CONFIG_COMPONENT_DEST_FILTER: + *filter_names = g_list_append(*filter_names, name); + break; + case BT_CONFIG_COMPONENT_DEST_SINK: + *sink_names = g_list_append(*sink_names, name); + break; + default: + abort(); + } + + g_string_assign(cur_name_prefix, ""); + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +/* + * Escapes `.`, `:`, and `\` of `input` with `\`. + */ +static +GString *escape_dot_colon(const char *input) +{ + GString *output = g_string_new(NULL); + const char *ch; + + if (!output) { + print_err_oom(); + goto end; + } + + for (ch = input; *ch != '\0'; ch++) { + if (*ch == '\\' || *ch == '.' || *ch == ':') { + g_string_append_c(output, '\\'); + } + + g_string_append_c(output, *ch); + } + +end: + return output; +} + +/* + * Appends a --connect option to a list of arguments. `upstream_name` + * and `downstream_name` are escaped with escape_dot_colon() in this + * function. + */ +static +int append_connect_arg(bt_value *run_args, + const char *upstream_name, const char *downstream_name) +{ + int ret = 0; + GString *e_upstream_name = escape_dot_colon(upstream_name); + GString *e_downstream_name = escape_dot_colon(downstream_name); + GString *arg = g_string_new(NULL); + + if (!e_upstream_name || !e_downstream_name || !arg) { + print_err_oom(); + ret = -1; + goto end; + } + + ret = bt_value_array_append_string_element(run_args, "--connect"); + if (ret) { + print_err_oom(); + ret = -1; + goto end; + } + + g_string_append(arg, e_upstream_name->str); + g_string_append_c(arg, ':'); + g_string_append(arg, e_downstream_name->str); + ret = bt_value_array_append_string_element(run_args, arg->str); + if (ret) { + print_err_oom(); + ret = -1; + goto end; + } + +end: + if (arg) { + g_string_free(arg, TRUE); + } + + if (e_upstream_name) { + g_string_free(e_upstream_name, TRUE); + } + + if (e_downstream_name) { + g_string_free(e_downstream_name, TRUE); + } + + return ret; +} + +/* + * Appends the run command's --connect options for the convert command. + */ +static +int convert_auto_connect(bt_value *run_args, + GList *source_names, GList *filter_names, + GList *sink_names) +{ + int ret = 0; + GList *source_at = source_names; + GList *filter_at = filter_names; + GList *filter_prev; + GList *sink_at = sink_names; + + BT_ASSERT(source_names); + BT_ASSERT(filter_names); + BT_ASSERT(sink_names); + + /* Connect all sources to the first filter */ + for (source_at = source_names; source_at != NULL; source_at = g_list_next(source_at)) { + GString *source_name = source_at->data; + GString *filter_name = filter_at->data; + + ret = append_connect_arg(run_args, source_name->str, + filter_name->str); + if (ret) { + goto error; + } + } + + filter_prev = filter_at; + filter_at = g_list_next(filter_at); + + /* Connect remaining filters */ + for (; filter_at != NULL; filter_prev = filter_at, filter_at = g_list_next(filter_at)) { + GString *filter_name = filter_at->data; + GString *filter_prev_name = filter_prev->data; + + ret = append_connect_arg(run_args, filter_prev_name->str, + filter_name->str); + if (ret) { + goto error; + } + } + + /* Connect last filter to all sinks */ + for (sink_at = sink_names; sink_at != NULL; sink_at = g_list_next(sink_at)) { + GString *filter_name = filter_prev->data; + GString *sink_name = sink_at->data; + + ret = append_connect_arg(run_args, filter_name->str, + sink_name->str); + if (ret) { + goto error; + } + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +int split_timerange(const char *arg, char **begin, char **end) +{ + int ret = 0; + const char *ch = arg; + size_t end_pos; + GString *g_begin = NULL; + GString *g_end = NULL; + + BT_ASSERT(arg); + + if (*ch == '[') { + ch++; + } + + g_begin = bt_common_string_until(ch, "", ",", &end_pos); + if (!g_begin || ch[end_pos] != ',' || g_begin->len == 0) { + goto error; + } + + ch += end_pos + 1; + + g_end = bt_common_string_until(ch, "", "]", &end_pos); + if (!g_end || g_end->len == 0) { + goto error; + } + + BT_ASSERT(begin); + BT_ASSERT(end); + *begin = g_begin->str; + *end = g_end->str; + g_string_free(g_begin, FALSE); + g_string_free(g_end, FALSE); + g_begin = NULL; + g_end = NULL; + goto end; + +error: + ret = -1; + +end: + if (g_begin) { + g_string_free(g_begin, TRUE); + } + + if (g_end) { + g_string_free(g_end, TRUE); + } + + return ret; +} + +static +int g_list_prepend_gstring(GList **list, const char *string) +{ + int ret = 0; + GString *gs = g_string_new(string); + + BT_ASSERT(list); + + if (!gs) { + print_err_oom(); + goto end; + } + + *list = g_list_prepend(*list, gs); + +end: + return ret; +} + +/* + * Creates a Babeltrace config object from the arguments of a convert + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +static +struct bt_config *bt_config_convert_from_args(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths, char *log_level) +{ + poptContext pc = NULL; + char *arg = NULL; + enum bt_config_component_dest cur_comp_dest = + BT_CONFIG_COMPONENT_DEST_UNKNOWN; + int opt, ret = 0; + struct bt_config *cfg = NULL; + bool got_input_format_opt = false; + bool got_output_format_opt = false; + bool trimmer_has_begin = false; + bool trimmer_has_end = false; + bool stream_intersection_mode = false; + GString *cur_name = NULL; + GString *cur_name_prefix = NULL; + const char *leftover = NULL; + bool print_run_args = false; + bool print_run_args_0 = false; + bool print_ctf_metadata = false; + bt_value *run_args = NULL; + bt_value *all_names = NULL; + GList *source_names = NULL; + GList *filter_names = NULL; + GList *sink_names = NULL; + bt_value *leftovers = NULL; + struct implicit_component_args implicit_ctf_input_args = { 0 }; + struct implicit_component_args implicit_ctf_output_args = { 0 }; + struct implicit_component_args implicit_lttng_live_args = { 0 }; + struct implicit_component_args implicit_dummy_args = { 0 }; + struct implicit_component_args implicit_text_args = { 0 }; + struct implicit_component_args implicit_debug_info_args = { 0 }; + struct implicit_component_args implicit_muxer_args = { 0 }; + struct implicit_component_args implicit_trimmer_args = { 0 }; + bt_value *plugin_paths; + char error_buf[256] = { 0 }; + size_t i; + struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 }; + char *output = NULL; + + (void) bt_value_copy(initial_plugin_paths, &plugin_paths); + + *retcode = 0; + + if (argc <= 1) { + print_convert_usage(stdout); + *retcode = -1; + goto end; + } + + if (init_implicit_component_args(&implicit_ctf_input_args, + "source.ctf.fs", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_ctf_output_args, + "sink.ctf.fs", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_lttng_live_args, + "source.ctf.lttng-live", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_text_args, + "sink.text.pretty", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_dummy_args, + "sink.utils.dummy", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_debug_info_args, + "filter.lttng-utils.debug-info", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_muxer_args, + "filter.utils.muxer", true)) { + goto error; + } + + if (init_implicit_component_args(&implicit_trimmer_args, + "filter.utils.trimmer", false)) { + goto error; + } + + all_names = bt_value_map_create(); + if (!all_names) { + print_err_oom(); + goto error; + } + + run_args = bt_value_array_create(); + if (!run_args) { + print_err_oom(); + goto error; + } + + cur_name = g_string_new(NULL); + if (!cur_name) { + print_err_oom(); + goto error; + } + + cur_name_prefix = g_string_new(NULL); + if (!cur_name_prefix) { + print_err_oom(); + goto error; + } + + ret = append_env_var_plugin_paths(plugin_paths); + if (ret) { + goto error; + } + + leftovers = bt_value_array_create(); + if (!leftovers) { + print_err_oom(); + goto error; + } + + /* + * First pass: collect all arguments which need to be passed + * as is to the run command. This pass can also add --name + * arguments if needed to automatically name unnamed component + * instances. Also it does the following transformations: + * + * --path=PATH -> --params=path="PATH" + * --url=URL -> --params=url="URL" + * + * Also it appends the plugin paths of --plugin-path to + * `plugin_paths`. + */ + pc = poptGetContext(NULL, argc, (const char **) argv, + convert_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + char *name = NULL; + char *plugin_name = NULL; + char *comp_cls_name = NULL; + + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_COMPONENT: + { + bt_component_class_type type; + const char *type_prefix; + + /* Append current component's name if needed */ + ret = convert_append_name_param(cur_comp_dest, cur_name, + cur_name_prefix, run_args, all_names, + &source_names, &filter_names, &sink_names); + if (ret) { + goto error; + } + + /* Parse the argument */ + plugin_comp_cls_names(arg, &name, &plugin_name, + &comp_cls_name, &type); + if (!plugin_name || !comp_cls_name) { + printf_err("Invalid format for --component option's argument:\n %s\n", + arg); + goto error; + } + + if (name) { + g_string_assign(cur_name, name); + } else { + g_string_assign(cur_name, ""); + } + + switch (type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; + type_prefix = "source"; + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + cur_comp_dest = BT_CONFIG_COMPONENT_DEST_FILTER; + type_prefix = "filter"; + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + cur_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK; + type_prefix = "sink"; + break; + default: + abort(); + } + + if (bt_value_array_append_string_element(run_args, + "--component")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + print_err_oom(); + goto error; + } + + g_string_assign(cur_name_prefix, ""); + g_string_append_printf(cur_name_prefix, "%s.%s.%s", + type_prefix, plugin_name, comp_cls_name); + free(name); + free(plugin_name); + free(comp_cls_name); + name = NULL; + plugin_name = NULL; + comp_cls_name = NULL; + break; + } + case OPT_PARAMS: + if (cur_name_prefix->len == 0) { + printf_err("No current component of which to set parameters:\n %s\n", + arg); + goto error; + } + + if (bt_value_array_append_string_element(run_args, + "--params")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + print_err_oom(); + goto error; + } + break; + case OPT_PATH: + if (cur_name_prefix->len == 0) { + printf_err("No current component of which to set `path` parameter:\n %s\n", + arg); + goto error; + } + + if (append_string_parameter_to_args(run_args, "path", arg)) { + goto error; + } + break; + case OPT_URL: + if (cur_name_prefix->len == 0) { + printf_err("No current component of which to set `url` parameter:\n %s\n", + arg); + goto error; + } + + + if (append_string_parameter_to_args(run_args, "url", arg)) { + goto error; + } + break; + case OPT_NAME: + if (cur_name_prefix->len == 0) { + printf_err("No current component to name:\n %s\n", + arg); + goto error; + } + + if (bt_value_array_append_string_element(run_args, "--name")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + print_err_oom(); + goto error; + } + + g_string_assign(cur_name, arg); + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + force_omit_home_plugin_path = true; + + if (bt_value_array_append_string_element(run_args, + "--omit-home-plugin-path")) { + print_err_oom(); + goto error; + } + break; + case OPT_RETRY_DURATION: + if (bt_value_array_append_string_element(run_args, + "--retry-duration")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + print_err_oom(); + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + force_omit_system_plugin_path = true; + + if (bt_value_array_append_string_element(run_args, + "--omit-system-plugin-path")) { + print_err_oom(); + goto error; + } + break; + case OPT_PLUGIN_PATH: + if (bt_config_append_plugin_paths_check_setuid_setgid( + plugin_paths, arg)) { + goto error; + } + + if (bt_value_array_append_string_element(run_args, + "--plugin-path")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string_element(run_args, arg)) { + print_err_oom(); + goto error; + } + break; + case OPT_HELP: + print_convert_usage(stdout); + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + case OPT_BEGIN: + case OPT_CLOCK_CYCLES: + case OPT_CLOCK_DATE: + case OPT_CLOCK_FORCE_CORRELATE: + case OPT_CLOCK_GMT: + case OPT_CLOCK_OFFSET: + case OPT_CLOCK_OFFSET_NS: + case OPT_CLOCK_SECONDS: + case OPT_COLOR: + case OPT_DEBUG: + case OPT_DEBUG_INFO: + case OPT_DEBUG_INFO_DIR: + case OPT_DEBUG_INFO_FULL_PATH: + case OPT_DEBUG_INFO_TARGET_PREFIX: + case OPT_END: + case OPT_FIELDS: + case OPT_INPUT_FORMAT: + case OPT_NAMES: + case OPT_NO_DELTA: + case OPT_OUTPUT_FORMAT: + case OPT_OUTPUT: + case OPT_RUN_ARGS: + case OPT_RUN_ARGS_0: + case OPT_STREAM_INTERSECTION: + case OPT_TIMERANGE: + case OPT_VERBOSE: + /* Ignore in this pass */ + break; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + /* Append current component's name if needed */ + ret = convert_append_name_param(cur_comp_dest, cur_name, + cur_name_prefix, run_args, all_names, &source_names, + &filter_names, &sink_names); + if (ret) { + goto error; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + poptFreeContext(pc); + free(arg); + arg = NULL; + + /* + * Second pass: transform the convert-specific options and + * arguments into implicit component instances for the run + * command. + */ + pc = poptGetContext(NULL, argc, (const char **) argv, + convert_long_options, 0); + if (!pc) { + printf_err("Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + arg = poptGetOptArg(pc); + + switch (opt) { + case OPT_BEGIN: + if (trimmer_has_begin) { + printf("At --begin option: --begin or --timerange option already specified\n %s\n", + arg); + goto error; + } + + trimmer_has_begin = true; + ret = append_implicit_component_extra_param( + &implicit_trimmer_args, "begin", arg); + implicit_trimmer_args.exists = true; + if (ret) { + goto error; + } + break; + case OPT_END: + if (trimmer_has_end) { + printf("At --end option: --end or --timerange option already specified\n %s\n", + arg); + goto error; + } + + trimmer_has_end = true; + ret = append_implicit_component_extra_param( + &implicit_trimmer_args, "end", arg); + implicit_trimmer_args.exists = true; + if (ret) { + goto error; + } + break; + case OPT_TIMERANGE: + { + char *begin; + char *end; + + if (trimmer_has_begin || trimmer_has_end) { + printf("At --timerange option: --begin, --end, or --timerange option already specified\n %s\n", + arg); + goto error; + } + + ret = split_timerange(arg, &begin, &end); + if (ret) { + printf_err("Invalid --timerange option's argument: expecting BEGIN,END or [BEGIN,END]:\n %s\n", + arg); + goto error; + } + + ret = append_implicit_component_extra_param( + &implicit_trimmer_args, "begin", begin); + ret |= append_implicit_component_extra_param( + &implicit_trimmer_args, "end", end); + implicit_trimmer_args.exists = true; + free(begin); + free(end); + if (ret) { + goto error; + } + break; + } + case OPT_CLOCK_CYCLES: + append_implicit_component_param( + &implicit_text_args, "clock-cycles", "yes"); + implicit_text_args.exists = true; + break; + case OPT_CLOCK_DATE: + append_implicit_component_param( + &implicit_text_args, "clock-date", "yes"); + implicit_text_args.exists = true; + break; + case OPT_CLOCK_FORCE_CORRELATE: + append_implicit_component_param( + &implicit_muxer_args, + "assume-absolute-clock-classes", "yes"); + break; + case OPT_CLOCK_GMT: + append_implicit_component_param( + &implicit_text_args, "clock-gmt", "yes"); + append_implicit_component_param( + &implicit_trimmer_args, "gmt", "yes"); + implicit_text_args.exists = true; + break; + case OPT_CLOCK_OFFSET: + implicit_ctf_input_args.exists = true; + append_implicit_component_param( + &implicit_ctf_input_args, + "clock-class-offset-s", arg); + break; + case OPT_CLOCK_OFFSET_NS: + implicit_ctf_input_args.exists = true; + append_implicit_component_param( + &implicit_ctf_input_args, + "clock-class-offset-ns", arg); + break; + case OPT_CLOCK_SECONDS: + append_implicit_component_param( + &implicit_text_args, "clock-seconds", "yes"); + implicit_text_args.exists = true; + break; + case OPT_COLOR: + implicit_text_args.exists = true; + ret = append_implicit_component_extra_param( + &implicit_text_args, "color", arg); + if (ret) { + goto error; + } + break; + case OPT_DEBUG_INFO: + implicit_debug_info_args.exists = true; + break; + case OPT_DEBUG_INFO_DIR: + implicit_debug_info_args.exists = true; + ret = append_implicit_component_extra_param( + &implicit_debug_info_args, "debug-info-dir", arg); + if (ret) { + goto error; + } + break; + case OPT_DEBUG_INFO_FULL_PATH: + implicit_debug_info_args.exists = true; + append_implicit_component_param( + &implicit_debug_info_args, "full-path", "yes"); + break; + case OPT_DEBUG_INFO_TARGET_PREFIX: + implicit_debug_info_args.exists = true; + ret = append_implicit_component_extra_param( + &implicit_debug_info_args, + "target-prefix", arg); + if (ret) { + goto error; + } + break; + case OPT_FIELDS: + { + bt_value *fields = fields_from_arg(arg); + + if (!fields) { + goto error; + } + + implicit_text_args.exists = true; + ret = insert_flat_params_from_array( + implicit_text_args.params_arg, + fields, "field"); + bt_value_put_ref(fields); + if (ret) { + goto error; + } + break; + } + case OPT_NAMES: + { + bt_value *names = names_from_arg(arg); + + if (!names) { + goto error; + } + + implicit_text_args.exists = true; + ret = insert_flat_params_from_array( + implicit_text_args.params_arg, + names, "name"); + bt_value_put_ref(names); + if (ret) { + goto error; + } + break; + } + case OPT_NO_DELTA: + append_implicit_component_param( + &implicit_text_args, "no-delta", "yes"); + implicit_text_args.exists = true; + break; + case OPT_INPUT_FORMAT: + if (got_input_format_opt) { + printf_err("Duplicate --input-format option\n"); + goto error; + } + + got_input_format_opt = true; + + if (strcmp(arg, "ctf") == 0) { + implicit_ctf_input_args.exists = true; + } else if (strcmp(arg, "lttng-live") == 0) { + implicit_lttng_live_args.exists = true; + } else { + printf_err("Unknown legacy input format:\n %s\n", + arg); + goto error; + } + break; + case OPT_OUTPUT_FORMAT: + if (got_output_format_opt) { + printf_err("Duplicate --output-format option\n"); + goto error; + } + + got_output_format_opt = true; + + if (strcmp(arg, "text") == 0) { + implicit_text_args.exists = true; + } else if (strcmp(arg, "ctf") == 0) { + implicit_ctf_output_args.exists = true; + } else if (strcmp(arg, "dummy") == 0) { + implicit_dummy_args.exists = true; + } else if (strcmp(arg, "ctf-metadata") == 0) { + print_ctf_metadata = true; + } else { + printf_err("Unknown legacy output format:\n %s\n", + arg); + goto error; + } + break; + case OPT_OUTPUT: + if (output) { + printf_err("Duplicate --output option\n"); + goto error; + } + + output = strdup(arg); + if (!output) { + print_err_oom(); + goto error; + } + break; + case OPT_RUN_ARGS: + if (print_run_args_0) { + printf_err("Cannot specify --run-args and --run-args-0\n"); + goto error; + } + + print_run_args = true; + break; + case OPT_RUN_ARGS_0: + if (print_run_args) { + printf_err("Cannot specify --run-args and --run-args-0\n"); + goto error; + } + + print_run_args_0 = true; + break; + case OPT_STREAM_INTERSECTION: + /* + * Applies to all traces implementing the trace-info + * query. + */ + stream_intersection_mode = true; + break; + case OPT_VERBOSE: + if (*log_level != 'V' && *log_level != 'D') { + *log_level = 'I'; + } + break; + case OPT_DEBUG: + *log_level = 'V'; + break; + } + + free(arg); + arg = NULL; + } + + /* Check for option parsing error */ + if (opt < -1) { + printf_err("While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + /* + * Legacy behaviour: --verbose used to make the `text` output + * format print more information. --verbose is now equivalent to + * the INFO log level, which is why we compare to 'I' here. + */ + if (*log_level == 'I') { + append_implicit_component_param(&implicit_text_args, + "verbose", "yes"); + } + + /* + * Append home and system plugin paths now that we possibly got + * --plugin-path. + */ + if (append_home_and_system_plugin_paths(plugin_paths, + force_omit_system_plugin_path, + force_omit_home_plugin_path)) { + goto error; + } + + /* Consume and keep leftover arguments */ + while ((leftover = poptGetArg(pc))) { + bt_value_status status = bt_value_array_append_string_element(leftovers, leftover); + if (status != BT_VALUE_STATUS_OK) { + print_err_oom(); + goto error; + } + } + + /* Print CTF metadata or print LTTng live sessions */ + if (print_ctf_metadata) { + const bt_value *bt_val_leftover; + + if (bt_value_array_is_empty(leftovers)) { + printf_err("--output-format=ctf-metadata specified without a path\n"); + goto error; + } + + if (bt_value_array_get_size(leftovers) > 1) { + printf_err("Too many paths specified for --output-format=ctf-metadata\n"); + goto error; + } + + cfg = bt_config_print_ctf_metadata_create(plugin_paths); + if (!cfg) { + goto error; + } + + bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0); + g_string_assign(cfg->cmd_data.print_ctf_metadata.path, + bt_value_string_get(bt_val_leftover)); + + if (output) { + g_string_assign( + cfg->cmd_data.print_ctf_metadata.output_path, + output); + } + + goto end; + } + + /* + * If -o ctf was specified, make sure an output path (--output) + * was also specified. --output does not imply -o ctf because + * it's also used for the default, implicit -o text if -o ctf + * is not specified. + */ + if (implicit_ctf_output_args.exists) { + if (!output) { + printf_err("--output-format=ctf specified without --output (trace output path)\n"); + goto error; + } + + /* + * At this point we know that -o ctf AND --output were + * specified. Make sure that no options were specified + * which would imply -o text because --output would be + * ambiguous in this case. For example, this is wrong: + * + * babeltrace2 --names=all -o ctf --output=/tmp/path my-trace + * + * because --names=all implies -o text, and --output + * could apply to both the sink.text.pretty and + * sink.ctf.fs implicit components. + */ + if (implicit_text_args.exists) { + printf_err("Ambiguous --output option: --output-format=ctf specified but another option implies --output-format=text\n"); + goto error; + } + } + + /* + * If -o dummy and -o ctf were not specified, and if there are + * no explicit sink components, then use an implicit + * `sink.text.pretty` component. + */ + if (!implicit_dummy_args.exists && !implicit_ctf_output_args.exists && + !sink_names) { + implicit_text_args.exists = true; + } + + /* + * Set implicit `sink.text.pretty` or `sink.ctf.fs` component's + * `path` parameter if --output was specified. + */ + if (output) { + if (implicit_text_args.exists) { + append_implicit_component_extra_param(&implicit_text_args, + "path", output); + } else if (implicit_ctf_output_args.exists) { + append_implicit_component_extra_param(&implicit_ctf_output_args, + "path", output); + } + } + + /* Decide where the leftover argument(s) go */ + if (bt_value_array_get_size(leftovers) > 0) { + if (implicit_lttng_live_args.exists) { + const bt_value *bt_val_leftover; + + if (bt_value_array_get_size(leftovers) > 1) { + printf_err("Too many URLs specified for --output-format=lttng-live\n"); + goto error; + } + + bt_val_leftover = bt_value_array_borrow_element_by_index_const(leftovers, 0); + lttng_live_url_parts = + bt_common_parse_lttng_live_url(bt_value_string_get(bt_val_leftover), + error_buf, sizeof(error_buf)); + if (!lttng_live_url_parts.proto) { + printf_err("Invalid LTTng live URL format: %s\n", + error_buf); + goto error; + } + + if (!lttng_live_url_parts.session_name) { + /* Print LTTng live sessions */ + cfg = bt_config_print_lttng_live_sessions_create( + plugin_paths); + if (!cfg) { + goto error; + } + + g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url, + bt_value_string_get(bt_val_leftover)); + + if (output) { + g_string_assign( + cfg->cmd_data.print_lttng_live_sessions.output_path, + output); + } + + goto end; + } + + ret = append_implicit_component_extra_param( + &implicit_lttng_live_args, "url", + bt_value_string_get(bt_val_leftover)); + if (ret) { + goto error; + } + + ret = append_implicit_component_extra_param( + &implicit_lttng_live_args, + "session-not-found-action", "end"); + if (ret) { + goto error; + } + } else { + /* + * Create one source.ctf.fs component, pass it an array + * with the leftovers. + * Note that it still has to be named later. + */ + implicit_ctf_input_args.exists = true; + ret = append_parameter_to_args(implicit_ctf_input_args.extra_params, + "paths", leftovers); + if (ret) { + goto error; + } + } + } + + /* + * Ensure mutual exclusion between implicit `source.ctf.fs` and + * `source.ctf.lttng-live` components. + */ + if (implicit_ctf_input_args.exists && implicit_lttng_live_args.exists) { + printf_err("Cannot create both implicit `%s` and `%s` components\n", + implicit_ctf_input_args.comp_arg->str, + implicit_lttng_live_args.comp_arg->str); + goto error; + } + + /* + * If the implicit `source.ctf.fs` or `source.ctf.lttng-live` + * components exists, make sure there's at least one leftover + * (which is the path or URL). + */ + if (implicit_ctf_input_args.exists && bt_value_array_is_empty(leftovers)) { + printf_err("Missing path for implicit `%s` component\n", + implicit_ctf_input_args.comp_arg->str); + goto error; + } + + if (implicit_lttng_live_args.exists && bt_value_array_is_empty(leftovers)) { + printf_err("Missing URL for implicit `%s` component\n", + implicit_lttng_live_args.comp_arg->str); + goto error; + } + + /* Assign names to implicit components */ + ret = assign_name_to_implicit_component(&implicit_ctf_input_args, + "source-ctf-fs", all_names, &source_names, true); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_lttng_live_args, + "lttng-live", all_names, &source_names, true); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_text_args, + "pretty", all_names, &sink_names, true); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_ctf_output_args, + "sink-ctf-fs", all_names, &sink_names, true); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_dummy_args, + "dummy", all_names, &sink_names, true); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_muxer_args, + "muxer", all_names, NULL, false); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_trimmer_args, + "trimmer", all_names, NULL, false); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_debug_info_args, + "debug-info", all_names, NULL, false); + if (ret) { + goto error; + } + + /* Make sure there's at least one source and one sink */ + if (!source_names) { + printf_err("No source component\n"); + goto error; + } + + if (!sink_names) { + printf_err("No sink component\n"); + goto error; + } + + /* + * Prepend the muxer, the trimmer, and the debug info to the + * filter chain so that we have: + * + * sources -> muxer -> [trimmer] -> [debug info] -> + * [user filters] -> sinks + */ + if (implicit_debug_info_args.exists) { + if (g_list_prepend_gstring(&filter_names, + implicit_debug_info_args.name_arg->str)) { + goto error; + } + } + + if (implicit_trimmer_args.exists) { + if (g_list_prepend_gstring(&filter_names, + implicit_trimmer_args.name_arg->str)) { + goto error; + } + } + + if (g_list_prepend_gstring(&filter_names, + implicit_muxer_args.name_arg->str)) { + goto error; + } + + /* + * Append the equivalent run arguments for the implicit + * components. + */ + ret = append_run_args_for_implicit_component(&implicit_ctf_input_args, run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_lttng_live_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_text_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_ctf_output_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_dummy_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_muxer_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_trimmer_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component(&implicit_debug_info_args, + run_args); + if (ret) { + goto error; + } + + /* Auto-connect components */ + ret = convert_auto_connect(run_args, source_names, filter_names, + sink_names); + if (ret) { + printf_err("Cannot auto-connect components\n"); + goto error; + } + + /* + * We have all the run command arguments now. Depending on + * --run-args, we pass this to the run command or print them + * here. + */ + if (print_run_args || print_run_args_0) { + if (stream_intersection_mode) { + printf_err("Cannot specify --stream-intersection with --run-args or --run-args-0\n"); + goto error; + } + + for (i = 0; i < bt_value_array_get_size(run_args); i++) { + const bt_value *arg_value = + bt_value_array_borrow_element_by_index(run_args, + i); + const char *arg; + GString *quoted = NULL; + const char *arg_to_print; + + BT_ASSERT(arg_value); + arg = bt_value_string_get(arg_value); + + if (print_run_args) { + quoted = bt_common_shell_quote(arg, true); + if (!quoted) { + goto error; + } + + arg_to_print = quoted->str; + } else { + arg_to_print = arg; + } + + printf("%s", arg_to_print); + + if (quoted) { + g_string_free(quoted, TRUE); + } + + if (i < bt_value_array_get_size(run_args) - 1) { + if (print_run_args) { + putchar(' '); + } else { + putchar('\0'); + } + } + } + + *retcode = -1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + goto end; + } + + cfg = bt_config_run_from_args_array(run_args, retcode, + force_omit_system_plugin_path, + force_omit_home_plugin_path, + initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->cmd_data.run.stream_intersection_mode = stream_intersection_mode; + goto end; + +error: + *retcode = 1; + BT_OBJECT_PUT_REF_AND_RESET(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + free(arg); + free(output); + + if (cur_name) { + g_string_free(cur_name, TRUE); + } + + if (cur_name_prefix) { + g_string_free(cur_name_prefix, TRUE); + } + + bt_value_put_ref(run_args); + bt_value_put_ref(all_names); + destroy_glist_of_gstring(source_names); + destroy_glist_of_gstring(filter_names); + destroy_glist_of_gstring(sink_names); + bt_value_put_ref(leftovers); + finalize_implicit_component_args(&implicit_ctf_input_args); + finalize_implicit_component_args(&implicit_ctf_output_args); + finalize_implicit_component_args(&implicit_lttng_live_args); + finalize_implicit_component_args(&implicit_dummy_args); + finalize_implicit_component_args(&implicit_text_args); + finalize_implicit_component_args(&implicit_debug_info_args); + finalize_implicit_component_args(&implicit_muxer_args); + finalize_implicit_component_args(&implicit_trimmer_args); + bt_value_put_ref(plugin_paths); + bt_common_destroy_lttng_live_url_parts(<tng_live_url_parts); + return cfg; +} + +/* + * Prints the Babeltrace 2.x general usage. + */ +static +void print_gen_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace2 [GENERAL OPTIONS] [COMMAND] [COMMAND ARGUMENTS]\n"); + fprintf(fp, "\n"); + fprintf(fp, "General options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -d, --debug Enable debug mode (same as --log-level=V)\n"); + fprintf(fp, " -h, --help Show this help and quit\n"); + fprintf(fp, " -l, --log-level=LVL Set all log levels to LVL (`N`, `V`, `D`,\n"); + fprintf(fp, " `I`, `W` (default), `E`, or `F`)\n"); + fprintf(fp, " -v, --verbose Enable verbose mode (same as --log-level=I)\n"); + fprintf(fp, " -V, --version Show version and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "Available commands:\n"); + fprintf(fp, "\n"); + fprintf(fp, " convert Convert and trim traces (default)\n"); + fprintf(fp, " help Get help for a plugin or a component class\n"); + fprintf(fp, " list-plugins List available plugins and their content\n"); + fprintf(fp, " query Query objects from a component class\n"); + fprintf(fp, " run Build a processing graph and run it\n"); + fprintf(fp, "\n"); + fprintf(fp, "Use `babeltrace2 COMMAND --help` to show the help of COMMAND.\n"); +} + +static +char log_level_from_arg(const char *arg) +{ + char level = 'U'; + + if (strcmp(arg, "VERBOSE") == 0 || + strcmp(arg, "V") == 0) { + level = 'V'; + } else if (strcmp(arg, "DEBUG") == 0 || + strcmp(arg, "D") == 0) { + level = 'D'; + } else if (strcmp(arg, "INFO") == 0 || + strcmp(arg, "I") == 0) { + level = 'I'; + } else if (strcmp(arg, "WARN") == 0 || + strcmp(arg, "WARNING") == 0 || + strcmp(arg, "W") == 0) { + level = 'W'; + } else if (strcmp(arg, "ERROR") == 0 || + strcmp(arg, "E") == 0) { + level = 'E'; + } else if (strcmp(arg, "FATAL") == 0 || + strcmp(arg, "F") == 0) { + level = 'F'; + } else if (strcmp(arg, "NONE") == 0 || + strcmp(arg, "N") == 0) { + level = 'N'; + } + + return level; +} + +struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths) +{ + struct bt_config *config = NULL; + int i; + const char **command_argv = NULL; + int command_argc = -1; + const char *command_name = NULL; + char log_level = 'U'; + + enum command_type { + COMMAND_TYPE_NONE = -1, + COMMAND_TYPE_RUN = 0, + COMMAND_TYPE_CONVERT, + COMMAND_TYPE_LIST_PLUGINS, + COMMAND_TYPE_HELP, + COMMAND_TYPE_QUERY, + } command_type = COMMAND_TYPE_NONE; + + *retcode = -1; + + if (!initial_plugin_paths) { + initial_plugin_paths = bt_value_array_create(); + if (!initial_plugin_paths) { + *retcode = 1; + goto end; + } + } else { + bt_value_get_ref(initial_plugin_paths); + } + + if (argc <= 1) { + print_version(); + puts(""); + print_gen_usage(stdout); + goto end; + } + + for (i = 1; i < argc; i++) { + const char *cur_arg = argv[i]; + const char *next_arg = i == (argc - 1) ? NULL : argv[i + 1]; + + if (strcmp(cur_arg, "-d") == 0 || + strcmp(cur_arg, "--debug") == 0) { + log_level = 'V'; + } else if (strcmp(cur_arg, "-v") == 0 || + strcmp(cur_arg, "--verbose") == 0) { + if (log_level != 'V' && log_level != 'D') { + /* + * Legacy: do not override a previous + * --debug because --verbose and --debug + * can be specified together (in this + * case we want the lowest log level to + * apply, VERBOSE). + */ + log_level = 'I'; + } + } else if (strcmp(cur_arg, "--log-level") == 0 || + strcmp(cur_arg, "-l") == 0) { + if (!next_arg) { + printf_err("Missing log level value for --log-level option\n"); + *retcode = 1; + goto end; + } + + log_level = log_level_from_arg(next_arg); + if (log_level == 'U') { + printf_err("Invalid argument for --log-level option:\n %s\n", + next_arg); + *retcode = 1; + goto end; + } + + i++; + } else if (strncmp(cur_arg, "--log-level=", 12) == 0) { + const char *arg = &cur_arg[12]; + + log_level = log_level_from_arg(arg); + if (log_level == 'U') { + printf_err("Invalid argument for --log-level option:\n %s\n", + arg); + *retcode = 1; + goto end; + } + } else if (strncmp(cur_arg, "-l", 2) == 0) { + const char *arg = &cur_arg[2]; + + log_level = log_level_from_arg(arg); + if (log_level == 'U') { + printf_err("Invalid argument for --log-level option:\n %s\n", + arg); + *retcode = 1; + goto end; + } + } else if (strcmp(cur_arg, "-V") == 0 || + strcmp(cur_arg, "--version") == 0) { + print_version(); + goto end; + } else if (strcmp(cur_arg, "-h") == 0 || + strcmp(cur_arg, "--help") == 0) { + print_gen_usage(stdout); + goto end; + } else { + /* + * First unknown argument: is it a known command + * name? + */ + command_argv = &argv[i]; + command_argc = argc - i; + + if (strcmp(cur_arg, "convert") == 0) { + command_type = COMMAND_TYPE_CONVERT; + } else if (strcmp(cur_arg, "list-plugins") == 0) { + command_type = COMMAND_TYPE_LIST_PLUGINS; + } else if (strcmp(cur_arg, "help") == 0) { + command_type = COMMAND_TYPE_HELP; + } else if (strcmp(cur_arg, "query") == 0) { + command_type = COMMAND_TYPE_QUERY; + } else if (strcmp(cur_arg, "run") == 0) { + command_type = COMMAND_TYPE_RUN; + } else { + /* + * Unknown argument, but not a known + * command name: assume the default + * `convert` command. + */ + command_type = COMMAND_TYPE_CONVERT; + command_name = "convert"; + command_argv = &argv[i - 1]; + command_argc = argc - i + 1; + } + break; + } + } + + if (command_type == COMMAND_TYPE_NONE) { + /* + * We only got non-help, non-version general options + * like --verbose and --debug, without any other + * arguments, so we can't do anything useful: print the + * usage and quit. + */ + print_gen_usage(stdout); + goto end; + } + + BT_ASSERT(command_argv); + BT_ASSERT(command_argc >= 0); + + switch (command_type) { + case COMMAND_TYPE_RUN: + config = bt_config_run_from_args(command_argc, command_argv, + retcode, force_omit_system_plugin_path, + force_omit_home_plugin_path, initial_plugin_paths); + break; + case COMMAND_TYPE_CONVERT: + config = bt_config_convert_from_args(command_argc, command_argv, + retcode, force_omit_system_plugin_path, + force_omit_home_plugin_path, + initial_plugin_paths, &log_level); + break; + case COMMAND_TYPE_LIST_PLUGINS: + config = bt_config_list_plugins_from_args(command_argc, + command_argv, retcode, force_omit_system_plugin_path, + force_omit_home_plugin_path, initial_plugin_paths); + break; + case COMMAND_TYPE_HELP: + config = bt_config_help_from_args(command_argc, + command_argv, retcode, force_omit_system_plugin_path, + force_omit_home_plugin_path, initial_plugin_paths); + break; + case COMMAND_TYPE_QUERY: + config = bt_config_query_from_args(command_argc, + command_argv, retcode, force_omit_system_plugin_path, + force_omit_home_plugin_path, initial_plugin_paths); + break; + default: + abort(); + } + + if (config) { + if (log_level == 'U') { + log_level = 'W'; + } + + config->log_level = log_level; + config->command_name = command_name; + } + +end: + bt_value_put_ref(initial_plugin_paths); + return config; +} diff --git a/cli/babeltrace2-cfg-cli-args.h b/cli/babeltrace2-cfg-cli-args.h new file mode 100644 index 00000000..66d29841 --- /dev/null +++ b/cli/babeltrace2-cfg-cli-args.h @@ -0,0 +1,41 @@ +#ifndef CLI_BABELTRACE_CFG_CLI_ARGS_H +#define CLI_BABELTRACE_CFG_CLI_ARGS_H + +/* + * Copyright 2016-2017 Philippe Proulx + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "babeltrace2-cfg.h" + +struct bt_config *bt_config_cli_args_create(int argc, const char *argv[], + int *retcode, bool force_omit_system_plugin_path, + bool force_omit_home_plugin_path, + const bt_value *initial_plugin_paths); + +#endif /* CLI_BABELTRACE_CFG_CLI_ARGS_H */ diff --git a/cli/babeltrace2-cfg.c b/cli/babeltrace2-cfg.c new file mode 100644 index 00000000..923afbd2 --- /dev/null +++ b/cli/babeltrace2-cfg.c @@ -0,0 +1,101 @@ +/* + * Babeltrace trace converter - parameter parsing + * + * Copyright 2016 Philippe Proulx + * + * 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 +#include +#include +#include "babeltrace2-cfg.h" + +static +void destroy_gstring(void *data) +{ + g_string_free(data, TRUE); +} + +/* + * Extracts the various paths from the string arg, delimited by ':', + * and appends them to the array value object plugin_paths. + */ +int bt_config_append_plugin_paths( + bt_value *plugin_paths, const char *arg) +{ + int ret = 0; + GPtrArray *dirs = g_ptr_array_new_with_free_func(destroy_gstring); + size_t i; + + if (!dirs) { + ret = -1; + goto end; + } + + ret = bt_common_append_plugin_path_dirs(arg, dirs); + if (ret) { + ret = -1; + goto end; + } + + for (i = 0; i < dirs->len; i++) { + GString *dir = g_ptr_array_index(dirs, i); + + ret = bt_value_array_append_string_element( + plugin_paths, dir->str); + if (ret != BT_VALUE_STATUS_OK) { + ret = -1; + goto end; + } + } + +end: + g_ptr_array_free(dirs, TRUE); + return ret; +} + +void bt_config_connection_destroy(struct bt_config_connection *connection) +{ + if (!connection) { + return; + } + + if (connection->upstream_comp_name) { + g_string_free(connection->upstream_comp_name, TRUE); + } + + if (connection->downstream_comp_name) { + g_string_free(connection->downstream_comp_name, TRUE); + } + + if (connection->upstream_port_glob) { + g_string_free(connection->upstream_port_glob, TRUE); + } + + if (connection->downstream_port_glob) { + g_string_free(connection->downstream_port_glob, TRUE); + } + + if (connection->arg) { + g_string_free(connection->arg, TRUE); + } + + g_free(connection); +} diff --git a/cli/babeltrace2-cfg.h b/cli/babeltrace2-cfg.h new file mode 100644 index 00000000..6d98cd33 --- /dev/null +++ b/cli/babeltrace2-cfg.h @@ -0,0 +1,142 @@ +#ifndef CLI_BABELTRACE_CFG_H +#define CLI_BABELTRACE_CFG_H + +/* + * Babeltrace trace converter - CLI tool's configuration + * + * Copyright 2016-2017 Philippe Proulx + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +enum bt_config_command { + BT_CONFIG_COMMAND_RUN, + BT_CONFIG_COMMAND_PRINT_CTF_METADATA, + BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS, + BT_CONFIG_COMMAND_LIST_PLUGINS, + BT_CONFIG_COMMAND_HELP, + BT_CONFIG_COMMAND_QUERY, +}; + +struct bt_config_component { + bt_object base; + bt_component_class_type type; + GString *plugin_name; + GString *comp_cls_name; + bt_value *params; + GString *instance_name; +}; + +struct bt_config_connection { + GString *upstream_comp_name; + GString *downstream_comp_name; + GString *upstream_port_glob; + GString *downstream_port_glob; + GString *arg; +}; + +struct bt_config { + bt_object base; + bool debug; + bool verbose; + bt_value *plugin_paths; + bool omit_system_plugin_path; + bool omit_home_plugin_path; + bool command_needs_plugins; + const char *command_name; + char log_level; + enum bt_config_command command; + union { + /* BT_CONFIG_COMMAND_RUN */ + struct { + /* 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; + + /* + * Number of microseconds to sleep when we need + * to retry to run the graph. + */ + uint64_t retry_duration_us; + + /* + * Whether or not to trim the source trace to the + * intersection of its streams. + */ + bool stream_intersection_mode; + } run; + + /* BT_CONFIG_COMMAND_HELP */ + struct { + struct bt_config_component *cfg_component; + } help; + + /* BT_CONFIG_COMMAND_QUERY */ + struct { + GString *object; + struct bt_config_component *cfg_component; + } query; + + /* BT_CONFIG_COMMAND_PRINT_CTF_METADATA */ + struct { + GString *path; + GString *output_path; + } print_ctf_metadata; + + /* BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS */ + struct { + GString *url; + GString *output_path; + } print_lttng_live_sessions; + } cmd_data; +}; + +static inline +struct bt_config_component *bt_config_get_component(GPtrArray *array, + size_t index) +{ + struct bt_config_component *comp = g_ptr_array_index(array, index); + + bt_object_get_ref(comp); + return comp; +} + +int bt_config_append_plugin_paths(bt_value *plugin_paths, + const char *arg); + +void bt_config_connection_destroy(struct bt_config_connection *connection); + +#endif /* CLI_BABELTRACE_CFG_H */ diff --git a/cli/babeltrace2-log.c b/cli/babeltrace2-log.c new file mode 100644 index 00000000..4d97b90b --- /dev/null +++ b/cli/babeltrace2-log.c @@ -0,0 +1,168 @@ +/* + * Copyright 2017 Philippe Proulx + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +static +void print_usage(FILE *fp) +{ + fprintf(stderr, "Usage: babeltrace2-log [OPTIONS] OUTPUT-PATH\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, "\n"); + fprintf(stderr, " -t, --with-timestamps Extract timestamps from lines and map them to\n"); + fprintf(stderr, " a CTF clock class\n"); +} + +static +int parse_params(int argc, char *argv[], char **output_path, + bool *no_extract_ts) +{ + enum { + OPT_WITH_TIMESTAMPS = 1, + OPT_HELP = 2, + }; + static struct poptOption opts[] = { + { "with-timestamps", 't', POPT_ARG_NONE, NULL, OPT_WITH_TIMESTAMPS, NULL, NULL }, + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { NULL, '\0', 0, NULL, 0, NULL, NULL }, + }; + poptContext pc = NULL; + int opt; + int ret = 0; + const char *leftover; + + *no_extract_ts = true; + pc = poptGetContext(NULL, argc, (const char **) argv, opts, 0); + if (!pc) { + fprintf(stderr, "Cannot get popt context\n"); + goto error; + } + + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) > 0) { + switch (opt) { + case OPT_HELP: + print_usage(stdout); + goto end; + case OPT_WITH_TIMESTAMPS: + *no_extract_ts = false; + break; + default: + fprintf(stderr, "Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + } + + if (opt < -1) { + fprintf(stderr, "While parsing command-line options, at option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto error; + } + + leftover = poptGetArg(pc); + if (!leftover) { + fprintf(stderr, "Command line error: Missing output path\n"); + print_usage(stderr); + goto error; + } + + *output_path = strdup(leftover); + BT_ASSERT(*output_path); + goto end; + +error: + ret = 1; + +end: + if (pc) { + poptFreeContext(pc); + } + + return ret; +} + +int main(int argc, char *argv[]) +{ + char *output_path = NULL; + bool no_extract_ts; + int retcode; + GError *error = NULL; + gchar *bt_argv[] = { + BT_CLI_PATH, + "run", + "--component", + "dmesg:src.text.dmesg", + "--params", + NULL, /* no-extract-timestamp=? placeholder */ + "--component", + "ctf:sink.ctf.fs", + "--key", + "path", + "--value", + NULL, /* output path placeholder */ + "--params", + "single-trace=yes", + "--connect", + "dmesg:ctf", + NULL, /* sentinel */ + }; + + retcode = parse_params(argc, argv, &output_path, &no_extract_ts); + if (retcode) { + goto end; + } + + if (no_extract_ts) { + bt_argv[5] = "no-extract-timestamp=yes"; + } else { + bt_argv[5] = "no-extract-timestamp=no"; + } + + bt_argv[11] = output_path; + (void) g_spawn_sync(NULL, bt_argv, NULL, + G_SPAWN_CHILD_INHERITS_STDIN, NULL, NULL, + NULL, NULL, &retcode, &error); + + if (error) { + fprintf(stderr, "Failed to execute \"%s\": %s (%d)\n", + bt_argv[0], error->message, error->code); + } + +end: + free(output_path); + + if (error) { + g_error_free(error); + } + + return retcode; +} diff --git a/cli/babeltrace2.c b/cli/babeltrace2.c new file mode 100644 index 00000000..a8a0dc00 --- /dev/null +++ b/cli/babeltrace2.c @@ -0,0 +1,2950 @@ +/* + * Copyright 2010-2011 EfficiOS Inc. and Linux Foundation + * + * Author: Mathieu Desnoyers + * + * 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" +#include "logging.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "babeltrace2-cfg.h" +#include "babeltrace2-cfg-cli-args.h" +#include "babeltrace2-cfg-cli-args-default.h" + +#define ENV_BABELTRACE_WARN_COMMAND_NAME_DIRECTORY_CLASH "BABELTRACE_CLI_WARN_COMMAND_NAME_DIRECTORY_CLASH" +#define ENV_BABELTRACE_CLI_LOG_LEVEL "BABELTRACE_CLI_LOG_LEVEL" +#define NSEC_PER_SEC 1000000000LL + +/* + * Known environment variable names for the log levels of the project's + * modules. + */ +static const char* log_level_env_var_names[] = { + "BABELTRACE_COMMON_LOG_LEVEL", + "BABELTRACE_COMPAT_LOG_LEVEL", + "BABELTRACE_CTFSER_LOG_LEVEL", + "BABELTRACE_FD_CACHE_LOG_LEVEL", + "BABELTRACE_FLT_LTTNG_UTILS_DEBUG_INFO_LOG_LEVEL", + "BABELTRACE_FLT_UTILS_COUNTER_LOG_LEVEL", + "BABELTRACE_FLT_UTILS_MUXER_LOG_LEVEL", + "BABELTRACE_FLT_UTILS_TRIMMER_LOG_LEVEL", + "BABELTRACE_PLUGIN_CTF_BFCR_LOG_LEVEL", + "BABELTRACE_PLUGIN_CTF_METADATA_LOG_LEVEL", + "BABELTRACE_PLUGIN_CTF_MSG_ITER_LOG_LEVEL", + "BABELTRACE_PLUGIN_CTF_UTILS_LOG_LEVEL", + "BABELTRACE_PYTHON_BT2_LOG_LEVEL", + "BABELTRACE_SINK_CTF_FS_LOG_LEVEL", + "BABELTRACE_SINK_TEXT_PRETTY_LOG_LEVEL", + "BABELTRACE_SRC_CTF_FS_LOG_LEVEL", + "BABELTRACE_SRC_CTF_LTTNG_LIVE_LOG_LEVEL", + "BABELTRACE_SRC_TEXT_DMESG_LOG_LEVEL", + NULL, +}; + +/* Application's processing graph (weak) */ +static bt_graph *the_graph; +static bt_query_executor *the_query_executor; +static bool canceled = false; + +GPtrArray *loaded_plugins; + +#ifdef __MINGW32__ + +#include + +static +BOOL WINAPI signal_handler(DWORD signal) { + if (the_graph) { + bt_graph_cancel(the_graph); + } + + canceled = true; + + return TRUE; +} + +static +void set_signal_handler(void) +{ + if (!SetConsoleCtrlHandler(signal_handler, TRUE)) { + BT_LOGE("Failed to set the ctrl+c handler."); + } +} + +#else /* __MINGW32__ */ + +static +void signal_handler(int signum) +{ + if (signum != SIGINT) { + return; + } + + if (the_graph) { + bt_graph_cancel(the_graph); + } + + if (the_query_executor) { + bt_query_executor_cancel(the_query_executor); + } + + canceled = true; +} + +static +void set_signal_handler(void) +{ + struct sigaction new_action, old_action; + + new_action.sa_handler = signal_handler; + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = 0; + sigaction(SIGINT, NULL, &old_action); + + if (old_action.sa_handler != SIG_IGN) { + sigaction(SIGINT, &new_action, NULL); + } +} + +#endif /* __MINGW32__ */ + +static +void init_static_data(void) +{ + loaded_plugins = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_object_put_ref); +} + +static +void fini_static_data(void) +{ + g_ptr_array_free(loaded_plugins, TRUE); +} + +static +int create_the_query_executor(void) +{ + int ret = 0; + + the_query_executor = bt_query_executor_create(); + if (!the_query_executor) { + BT_LOGE_STR("Cannot create a query executor."); + ret = -1; + } + + return ret; +} + +static +void destroy_the_query_executor(void) +{ + BT_QUERY_EXECUTOR_PUT_REF_AND_RESET(the_query_executor); +} + +static +int query(const bt_component_class *comp_cls, const char *obj, + const bt_value *params, const bt_value **user_result, + const char **fail_reason) +{ + const bt_value *result = NULL; + bt_query_executor_status status; + *fail_reason = "unknown error"; + int ret = 0; + + BT_ASSERT(fail_reason); + BT_ASSERT(user_result); + ret = create_the_query_executor(); + if (ret) { + /* create_the_query_executor() logs errors */ + goto end; + } + + if (canceled) { + BT_LOGI("Canceled by user before executing the query: " + "comp-cls-addr=%p, comp-cls-name=\"%s\", " + "query-obj=\"%s\"", comp_cls, + bt_component_class_get_name(comp_cls), obj); + *fail_reason = "canceled by user"; + goto error; + } + + while (true) { + status = bt_query_executor_query(the_query_executor, + comp_cls, obj, params, &result); + switch (status) { + case BT_QUERY_EXECUTOR_STATUS_OK: + goto ok; + case BT_QUERY_EXECUTOR_STATUS_AGAIN: + { + const uint64_t sleep_time_us = 100000; + + /* Wait 100 ms and retry */ + BT_LOGV("Got BT_QUERY_EXECUTOR_STATUS_AGAIN: sleeping: " + "time-us=%" PRIu64, sleep_time_us); + + if (usleep(sleep_time_us)) { + if (bt_query_executor_is_canceled(the_query_executor)) { + BT_LOGI("Query was canceled by user: " + "comp-cls-addr=%p, comp-cls-name=\"%s\", " + "query-obj=\"%s\"", comp_cls, + bt_component_class_get_name(comp_cls), + obj); + *fail_reason = "canceled by user"; + goto error; + } + } + + continue; + } + case BT_QUERY_EXECUTOR_STATUS_CANCELED: + *fail_reason = "canceled by user"; + goto error; + case BT_QUERY_EXECUTOR_STATUS_ERROR: + goto error; + case BT_QUERY_EXECUTOR_STATUS_INVALID_OBJECT: + *fail_reason = "invalid or unknown query object"; + goto error; + case BT_QUERY_EXECUTOR_STATUS_INVALID_PARAMS: + *fail_reason = "invalid query parameters"; + goto error; + case BT_QUERY_EXECUTOR_STATUS_UNSUPPORTED: + *fail_reason = "unsupported action"; + goto error; + case BT_QUERY_EXECUTOR_STATUS_NOMEM: + *fail_reason = "not enough memory"; + goto error; + default: + BT_LOGF("Unknown query status: status=%d", status); + abort(); + } + } + +ok: + *user_result = result; + result = NULL; + goto end; + +error: + ret = -1; + +end: + destroy_the_query_executor(); + bt_value_put_ref(result); + return ret; +} + +static +const bt_plugin *find_plugin(const char *name) +{ + int i; + const bt_plugin *plugin = NULL; + + BT_ASSERT(name); + BT_LOGD("Finding plugin: name=\"%s\"", name); + + for (i = 0; i < loaded_plugins->len; i++) { + plugin = g_ptr_array_index(loaded_plugins, i); + + if (strcmp(name, bt_plugin_get_name(plugin)) == 0) { + break; + } + + plugin = NULL; + } + + if (BT_LOG_ON_DEBUG) { + if (plugin) { + BT_LOGD("Found plugin: plugin-addr=%p", plugin); + } else { + BT_LOGD("Cannot find plugin."); + } + } + + bt_plugin_get_ref(plugin); + return plugin; +} + +typedef const void *(*plugin_borrow_comp_cls_func_t)( + const bt_plugin *, const char *); + +static +const void *find_component_class_from_plugin(const char *plugin_name, + const char *comp_class_name, + plugin_borrow_comp_cls_func_t plugin_borrow_comp_cls_func) +{ + const void *comp_class = NULL; + const bt_plugin *plugin; + + BT_LOGD("Finding component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\"", plugin_name, comp_class_name); + + plugin = find_plugin(plugin_name); + if (!plugin) { + goto end; + } + + comp_class = plugin_borrow_comp_cls_func(plugin, comp_class_name); + bt_object_get_ref(comp_class); + BT_PLUGIN_PUT_REF_AND_RESET(plugin); + +end: + if (BT_LOG_ON_DEBUG) { + if (comp_class) { + BT_LOGD("Found component class: comp-cls-addr=%p", + comp_class); + } else { + BT_LOGD("Cannot find source component class."); + } + } + + return comp_class; +} + +static +const bt_component_class_source *find_source_component_class( + const char *plugin_name, const char *comp_class_name) +{ + return (const void *) find_component_class_from_plugin( + plugin_name, comp_class_name, + (plugin_borrow_comp_cls_func_t) + bt_plugin_borrow_source_component_class_by_name_const); +} + +static +const bt_component_class_filter *find_filter_component_class( + const char *plugin_name, const char *comp_class_name) +{ + return (const void *) find_component_class_from_plugin( + plugin_name, comp_class_name, + (plugin_borrow_comp_cls_func_t) + bt_plugin_borrow_filter_component_class_by_name_const); +} + +static +const bt_component_class_sink *find_sink_component_class( + const char *plugin_name, const char *comp_class_name) +{ + return (const void *) find_component_class_from_plugin(plugin_name, + comp_class_name, + (plugin_borrow_comp_cls_func_t) + bt_plugin_borrow_sink_component_class_by_name_const); +} + +static +const bt_component_class *find_component_class(const char *plugin_name, + const char *comp_class_name, + bt_component_class_type comp_class_type) +{ + const bt_component_class *comp_cls = NULL; + + switch (comp_class_type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + comp_cls = bt_component_class_source_as_component_class_const(find_source_component_class(plugin_name, comp_class_name)); + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + comp_cls = bt_component_class_filter_as_component_class_const(find_filter_component_class(plugin_name, comp_class_name)); + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + comp_cls = bt_component_class_sink_as_component_class_const(find_sink_component_class(plugin_name, comp_class_name)); + break; + default: + abort(); + } + + return comp_cls; +} + +static +void print_indent(FILE *fp, size_t indent) +{ + size_t i; + + for (i = 0; i < indent; i++) { + fprintf(fp, " "); + } +} + +static +const char *component_type_str(bt_component_class_type type) +{ + switch (type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + return "source"; + case BT_COMPONENT_CLASS_TYPE_SINK: + return "sink"; + case BT_COMPONENT_CLASS_TYPE_FILTER: + return "filter"; + default: + return "(unknown)"; + } +} + +static +void print_plugin_comp_cls_opt(FILE *fh, const char *plugin_name, + const char *comp_cls_name, bt_component_class_type type) +{ + GString *shell_plugin_name = NULL; + GString *shell_comp_cls_name = NULL; + + shell_plugin_name = bt_common_shell_quote(plugin_name, false); + if (!shell_plugin_name) { + goto end; + } + + shell_comp_cls_name = bt_common_shell_quote(comp_cls_name, false); + if (!shell_comp_cls_name) { + goto end; + } + + fprintf(fh, "'%s%s%s%s.%s%s%s.%s%s%s'", + bt_common_color_bold(), + bt_common_color_fg_cyan(), + component_type_str(type), + bt_common_color_fg_default(), + bt_common_color_fg_blue(), + shell_plugin_name->str, + bt_common_color_fg_default(), + bt_common_color_fg_yellow(), + shell_comp_cls_name->str, + bt_common_color_reset()); + +end: + if (shell_plugin_name) { + g_string_free(shell_plugin_name, TRUE); + } + + if (shell_comp_cls_name) { + g_string_free(shell_comp_cls_name, TRUE); + } +} + +static +void print_value(FILE *, const bt_value *, size_t); + +static +void print_value_rec(FILE *, const bt_value *, size_t); + +struct print_map_value_data { + size_t indent; + FILE *fp; +}; + +static +bt_bool print_map_value(const char *key, const bt_value *object, + void *data) +{ + struct print_map_value_data *print_map_value_data = data; + + print_indent(print_map_value_data->fp, print_map_value_data->indent); + fprintf(print_map_value_data->fp, "%s: ", key); + BT_ASSERT(object); + + if (bt_value_is_array(object) && + bt_value_array_is_empty(object)) { + fprintf(print_map_value_data->fp, "[ ]\n"); + return true; + } + + if (bt_value_is_map(object) && + bt_value_map_is_empty(object)) { + fprintf(print_map_value_data->fp, "{ }\n"); + return true; + } + + if (bt_value_is_array(object) || + bt_value_is_map(object)) { + fprintf(print_map_value_data->fp, "\n"); + } + + print_value_rec(print_map_value_data->fp, object, + print_map_value_data->indent + 2); + return BT_TRUE; +} + +static +void print_value_rec(FILE *fp, const bt_value *value, size_t indent) +{ + bt_bool bool_val; + int64_t int_val; + uint64_t uint_val; + double dbl_val; + const char *str_val; + int size; + int i; + + if (!value) { + return; + } + + switch (bt_value_get_type(value)) { + case BT_VALUE_TYPE_NULL: + fprintf(fp, "%snull%s\n", bt_common_color_bold(), + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_BOOL: + bool_val = bt_value_bool_get(value); + fprintf(fp, "%s%s%s%s\n", bt_common_color_bold(), + bt_common_color_fg_cyan(), bool_val ? "yes" : "no", + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_UNSIGNED_INTEGER: + uint_val = bt_value_unsigned_integer_get(value); + fprintf(fp, "%s%s%" PRIu64 "%s\n", bt_common_color_bold(), + bt_common_color_fg_red(), uint_val, + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_SIGNED_INTEGER: + int_val = bt_value_signed_integer_get(value); + fprintf(fp, "%s%s%" PRId64 "%s\n", bt_common_color_bold(), + bt_common_color_fg_red(), int_val, + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_REAL: + dbl_val = bt_value_real_get(value); + fprintf(fp, "%s%s%lf%s\n", bt_common_color_bold(), + bt_common_color_fg_red(), dbl_val, + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_STRING: + str_val = bt_value_string_get(value); + fprintf(fp, "%s%s%s%s\n", bt_common_color_bold(), + bt_common_color_fg_green(), str_val, + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_ARRAY: + size = bt_value_array_get_size(value); + if (size < 0) { + goto error; + } + + if (size == 0) { + print_indent(fp, indent); + fprintf(fp, "[ ]\n"); + break; + } + + for (i = 0; i < size; i++) { + const bt_value *element = + bt_value_array_borrow_element_by_index_const( + value, i); + + if (!element) { + goto error; + } + print_indent(fp, indent); + fprintf(fp, "- "); + + if (bt_value_is_array(element) && + bt_value_array_is_empty(element)) { + fprintf(fp, "[ ]\n"); + continue; + } + + if (bt_value_is_map(element) && + bt_value_map_is_empty(element)) { + fprintf(fp, "{ }\n"); + continue; + } + + if (bt_value_is_array(element) || + bt_value_is_map(element)) { + fprintf(fp, "\n"); + } + + print_value_rec(fp, element, indent + 2); + } + break; + case BT_VALUE_TYPE_MAP: + { + struct print_map_value_data data = { + .indent = indent, + .fp = fp, + }; + + if (bt_value_map_is_empty(value)) { + print_indent(fp, indent); + fprintf(fp, "{ }\n"); + break; + } + + bt_value_map_foreach_entry_const(value, print_map_value, &data); + break; + } + default: + abort(); + } + return; + +error: + BT_LOGE("Error printing value of type %s.", + bt_common_value_type_string(bt_value_get_type(value))); +} + +static +void print_value(FILE *fp, const bt_value *value, size_t indent) +{ + if (!bt_value_is_array(value) && !bt_value_is_map(value)) { + print_indent(fp, indent); + } + + print_value_rec(fp, value, indent); +} + +static +void print_bt_config_component(struct bt_config_component *bt_config_component) +{ + fprintf(stderr, " "); + print_plugin_comp_cls_opt(stderr, bt_config_component->plugin_name->str, + bt_config_component->comp_cls_name->str, + bt_config_component->type); + fprintf(stderr, ":\n"); + + if (bt_config_component->instance_name->len > 0) { + fprintf(stderr, " Name: %s\n", + bt_config_component->instance_name->str); + } + + fprintf(stderr, " Parameters:\n"); + print_value(stderr, bt_config_component->params, 8); +} + +static +void print_bt_config_components(GPtrArray *array) +{ + size_t i; + + for (i = 0; i < array->len; i++) { + struct bt_config_component *cfg_component = + bt_config_get_component(array, i); + print_bt_config_component(cfg_component); + BT_OBJECT_PUT_REF_AND_RESET(cfg_component); + } +} + +static +void print_plugin_paths(const bt_value *plugin_paths) +{ + fprintf(stderr, " Plugin paths:\n"); + print_value(stderr, plugin_paths, 4); +} + +static +void print_cfg_run(struct bt_config *cfg) +{ + size_t i; + + print_plugin_paths(cfg->plugin_paths); + fprintf(stderr, " Source component instances:\n"); + print_bt_config_components(cfg->cmd_data.run.sources); + + if (cfg->cmd_data.run.filters->len > 0) { + fprintf(stderr, " Filter component instances:\n"); + print_bt_config_components(cfg->cmd_data.run.filters); + } + + fprintf(stderr, " Sink component instances:\n"); + print_bt_config_components(cfg->cmd_data.run.sinks); + fprintf(stderr, " Connections:\n"); + + for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *cfg_connection = + g_ptr_array_index(cfg->cmd_data.run.connections, + i); + + fprintf(stderr, " %s%s%s -> %s%s%s\n", + cfg_connection->upstream_comp_name->str, + cfg_connection->upstream_port_glob->len > 0 ? "." : "", + cfg_connection->upstream_port_glob->str, + cfg_connection->downstream_comp_name->str, + cfg_connection->downstream_port_glob->len > 0 ? "." : "", + cfg_connection->downstream_port_glob->str); + } +} + +static +void print_cfg_list_plugins(struct bt_config *cfg) +{ + print_plugin_paths(cfg->plugin_paths); +} + +static +void print_cfg_help(struct bt_config *cfg) +{ + print_plugin_paths(cfg->plugin_paths); +} + +static +void print_cfg_print_ctf_metadata(struct bt_config *cfg) +{ + print_plugin_paths(cfg->plugin_paths); + fprintf(stderr, " Path: %s\n", + cfg->cmd_data.print_ctf_metadata.path->str); +} + +static +void print_cfg_print_lttng_live_sessions(struct bt_config *cfg) +{ + print_plugin_paths(cfg->plugin_paths); + fprintf(stderr, " URL: %s\n", + cfg->cmd_data.print_lttng_live_sessions.url->str); +} + +static +void print_cfg_query(struct bt_config *cfg) +{ + print_plugin_paths(cfg->plugin_paths); + fprintf(stderr, " Object: `%s`\n", cfg->cmd_data.query.object->str); + fprintf(stderr, " Component class:\n"); + print_bt_config_component(cfg->cmd_data.query.cfg_component); +} + +static +void print_cfg(struct bt_config *cfg) +{ + if (!BT_LOG_ON_INFO) { + return; + } + + BT_LOGI_STR("Configuration:"); + fprintf(stderr, " Debug mode: %s\n", cfg->debug ? "yes" : "no"); + fprintf(stderr, " Verbose mode: %s\n", cfg->verbose ? "yes" : "no"); + + switch (cfg->command) { + case BT_CONFIG_COMMAND_RUN: + print_cfg_run(cfg); + break; + case BT_CONFIG_COMMAND_LIST_PLUGINS: + print_cfg_list_plugins(cfg); + break; + case BT_CONFIG_COMMAND_HELP: + print_cfg_help(cfg); + break; + case BT_CONFIG_COMMAND_QUERY: + print_cfg_query(cfg); + break; + case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: + print_cfg_print_ctf_metadata(cfg); + break; + case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: + print_cfg_print_lttng_live_sessions(cfg); + break; + default: + abort(); + } +} + +static +void add_to_loaded_plugins(const bt_plugin_set *plugin_set) +{ + int64_t i; + int64_t count; + + count = bt_plugin_set_get_plugin_count(plugin_set); + BT_ASSERT(count >= 0); + + for (i = 0; i < count; i++) { + const bt_plugin *plugin = + bt_plugin_set_borrow_plugin_by_index_const(plugin_set, i); + const bt_plugin *loaded_plugin = + find_plugin(bt_plugin_get_name(plugin)); + + BT_ASSERT(plugin); + + if (loaded_plugin) { + BT_LOGI("Not using plugin: another one already exists with the same name: " + "plugin-name=\"%s\", plugin-path=\"%s\", " + "existing-plugin-path=\"%s\"", + bt_plugin_get_name(plugin), + bt_plugin_get_path(plugin), + bt_plugin_get_path(loaded_plugin)); + bt_plugin_put_ref(loaded_plugin); + } else { + /* Add to global array. */ + BT_LOGD("Adding plugin to loaded plugins: plugin-path=\"%s\"", + bt_plugin_get_name(plugin)); + bt_plugin_get_ref(plugin); + g_ptr_array_add(loaded_plugins, (void *) plugin); + } + } +} + +static +int load_dynamic_plugins(const bt_value *plugin_paths) +{ + int nr_paths, i, ret = 0; + + nr_paths = bt_value_array_get_size(plugin_paths); + if (nr_paths < 0) { + BT_LOGE_STR("Cannot load dynamic plugins: no plugin path."); + ret = -1; + goto end; + } + + BT_LOGI("Loading dynamic plugins."); + + for (i = 0; i < nr_paths; i++) { + const bt_value *plugin_path_value = NULL; + const char *plugin_path; + const bt_plugin_set *plugin_set; + + plugin_path_value = + bt_value_array_borrow_element_by_index_const( + plugin_paths, i); + plugin_path = bt_value_string_get(plugin_path_value); + + /* + * Skip this if the directory does not exist because + * bt_plugin_find_all_from_dir() expects an existing + * directory. + */ + if (!g_file_test(plugin_path, G_FILE_TEST_IS_DIR)) { + BT_LOGV("Skipping nonexistent directory path: " + "path=\"%s\"", plugin_path); + continue; + } + + plugin_set = bt_plugin_find_all_from_dir(plugin_path, false); + if (!plugin_set) { + BT_LOGD("Unable to load dynamic plugins: path=\"%s\"", + plugin_path); + continue; + } + + add_to_loaded_plugins(plugin_set); + bt_plugin_set_put_ref(plugin_set); + } +end: + return ret; +} + +static +int load_static_plugins(void) +{ + int ret = 0; + const bt_plugin_set *plugin_set; + + BT_LOGI("Loading static plugins."); + plugin_set = bt_plugin_find_all_from_static(); + if (!plugin_set) { + BT_LOGE("Unable to load static plugins."); + ret = -1; + goto end; + } + + add_to_loaded_plugins(plugin_set); + bt_plugin_set_put_ref(plugin_set); +end: + return ret; +} + +static +int load_all_plugins(const bt_value *plugin_paths) +{ + int ret = 0; + + if (load_dynamic_plugins(plugin_paths)) { + ret = -1; + goto end; + } + + if (load_static_plugins()) { + ret = -1; + goto end; + } + + BT_LOGI("Loaded all plugins: count=%u", loaded_plugins->len); + +end: + return ret; +} + +static +void print_plugin_info(const bt_plugin *plugin) +{ + unsigned int major, minor, patch; + const char *extra; + bt_property_availability version_avail; + const char *plugin_name; + const char *path; + const char *author; + const char *license; + const char *plugin_description; + + plugin_name = bt_plugin_get_name(plugin); + path = bt_plugin_get_path(plugin); + author = bt_plugin_get_author(plugin); + license = bt_plugin_get_license(plugin); + plugin_description = bt_plugin_get_description(plugin); + version_avail = bt_plugin_get_version(plugin, &major, &minor, + &patch, &extra); + printf("%s%s%s%s:\n", bt_common_color_bold(), + bt_common_color_fg_blue(), plugin_name, + bt_common_color_reset()); + if (path) { + printf(" %sPath%s: %s\n", bt_common_color_bold(), + bt_common_color_reset(), path); + } else { + puts(" Built-in"); + } + + if (version_avail == BT_PROPERTY_AVAILABILITY_AVAILABLE) { + printf(" %sVersion%s: %u.%u.%u", + bt_common_color_bold(), bt_common_color_reset(), + major, minor, patch); + + if (extra) { + printf("%s", extra); + } + + printf("\n"); + } + + printf(" %sDescription%s: %s\n", bt_common_color_bold(), + bt_common_color_reset(), + plugin_description ? plugin_description : "(None)"); + printf(" %sAuthor%s: %s\n", bt_common_color_bold(), + bt_common_color_reset(), author ? author : "(Unknown)"); + printf(" %sLicense%s: %s\n", bt_common_color_bold(), + bt_common_color_reset(), + license ? license : "(Unknown)"); +} + +static +int cmd_query(struct bt_config *cfg) +{ + int ret = 0; + const bt_component_class *comp_cls = NULL; + const bt_value *results = NULL; + const char *fail_reason = NULL; + + comp_cls = find_component_class( + cfg->cmd_data.query.cfg_component->plugin_name->str, + cfg->cmd_data.query.cfg_component->comp_cls_name->str, + cfg->cmd_data.query.cfg_component->type); + if (!comp_cls) { + BT_LOGE("Cannot find component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d", + cfg->cmd_data.query.cfg_component->plugin_name->str, + cfg->cmd_data.query.cfg_component->comp_cls_name->str, + cfg->cmd_data.query.cfg_component->type); + fprintf(stderr, "%s%sCannot find component class %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, + cfg->cmd_data.query.cfg_component->plugin_name->str, + cfg->cmd_data.query.cfg_component->comp_cls_name->str, + cfg->cmd_data.query.cfg_component->type); + fprintf(stderr, "\n"); + ret = -1; + goto end; + } + + ret = query(comp_cls, cfg->cmd_data.query.object->str, + cfg->cmd_data.query.cfg_component->params, + &results, &fail_reason); + if (ret) { + goto failed; + } + + print_value(stdout, results, 0); + goto end; + +failed: + BT_LOGE("Failed to query component class: %s: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d " + "object=\"%s\"", fail_reason, + cfg->cmd_data.query.cfg_component->plugin_name->str, + cfg->cmd_data.query.cfg_component->comp_cls_name->str, + cfg->cmd_data.query.cfg_component->type, + cfg->cmd_data.query.object->str); + fprintf(stderr, "%s%sFailed to query info to %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, + cfg->cmd_data.query.cfg_component->plugin_name->str, + cfg->cmd_data.query.cfg_component->comp_cls_name->str, + cfg->cmd_data.query.cfg_component->type); + fprintf(stderr, "%s%s with object `%s`: %s%s\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + cfg->cmd_data.query.object->str, + fail_reason, + bt_common_color_reset()); + ret = -1; + +end: + bt_component_class_put_ref(comp_cls); + bt_value_put_ref(results); + return ret; +} + +static +void print_component_class_help(const char *plugin_name, + const bt_component_class *comp_cls) +{ + const char *comp_class_name = + bt_component_class_get_name(comp_cls); + const char *comp_class_description = + bt_component_class_get_description(comp_cls); + const char *comp_class_help = + bt_component_class_get_help(comp_cls); + bt_component_class_type type = + bt_component_class_get_type(comp_cls); + + print_plugin_comp_cls_opt(stdout, plugin_name, comp_class_name, type); + printf("\n"); + printf(" %sDescription%s: %s\n", bt_common_color_bold(), + bt_common_color_reset(), + comp_class_description ? comp_class_description : "(None)"); + + if (comp_class_help) { + printf("\n%s\n", comp_class_help); + } +} + +static +int cmd_help(struct bt_config *cfg) +{ + int ret = 0; + const bt_plugin *plugin = NULL; + const bt_component_class *needed_comp_cls = NULL; + + plugin = find_plugin(cfg->cmd_data.help.cfg_component->plugin_name->str); + if (!plugin) { + BT_LOGE("Cannot find plugin: plugin-name=\"%s\"", + cfg->cmd_data.help.cfg_component->plugin_name->str); + fprintf(stderr, "%s%sCannot find plugin %s%s%s\n", + bt_common_color_bold(), bt_common_color_fg_red(), + bt_common_color_fg_blue(), + cfg->cmd_data.help.cfg_component->plugin_name->str, + bt_common_color_reset()); + ret = -1; + goto end; + } + + print_plugin_info(plugin); + printf(" %sSource component classes%s: %d\n", + bt_common_color_bold(), + bt_common_color_reset(), + (int) bt_plugin_get_source_component_class_count(plugin)); + printf(" %sFilter component classes%s: %d\n", + bt_common_color_bold(), + bt_common_color_reset(), + (int) bt_plugin_get_filter_component_class_count(plugin)); + printf(" %sSink component classes%s: %d\n", + bt_common_color_bold(), + bt_common_color_reset(), + (int) bt_plugin_get_sink_component_class_count(plugin)); + + if (strlen(cfg->cmd_data.help.cfg_component->comp_cls_name->str) == 0) { + /* Plugin help only */ + goto end; + } + + needed_comp_cls = find_component_class( + cfg->cmd_data.help.cfg_component->plugin_name->str, + cfg->cmd_data.help.cfg_component->comp_cls_name->str, + cfg->cmd_data.help.cfg_component->type); + if (!needed_comp_cls) { + BT_LOGE("Cannot find component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d", + cfg->cmd_data.help.cfg_component->plugin_name->str, + cfg->cmd_data.help.cfg_component->comp_cls_name->str, + cfg->cmd_data.help.cfg_component->type); + fprintf(stderr, "\n%s%sCannot find component class %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, + cfg->cmd_data.help.cfg_component->plugin_name->str, + cfg->cmd_data.help.cfg_component->comp_cls_name->str, + cfg->cmd_data.help.cfg_component->type); + fprintf(stderr, "\n"); + ret = -1; + goto end; + } + + printf("\n"); + print_component_class_help( + cfg->cmd_data.help.cfg_component->plugin_name->str, + needed_comp_cls); + +end: + bt_component_class_put_ref(needed_comp_cls); + bt_plugin_put_ref(plugin); + return ret; +} + +typedef void *(* plugin_borrow_comp_cls_by_index_func_t)(const bt_plugin *, + uint64_t); +typedef const bt_component_class *(* spec_comp_cls_borrow_comp_cls_func_t)( + void *); + +void cmd_list_plugins_print_component_classes(const bt_plugin *plugin, + const char *cc_type_name, uint64_t count, + plugin_borrow_comp_cls_by_index_func_t borrow_comp_cls_by_index_func, + spec_comp_cls_borrow_comp_cls_func_t spec_comp_cls_borrow_comp_cls_func) +{ + uint64_t i; + + if (count == 0) { + printf(" %s%s component classes%s: (none)\n", + bt_common_color_bold(), + cc_type_name, + bt_common_color_reset()); + goto end; + } else { + printf(" %s%s component classes%s:\n", + bt_common_color_bold(), + cc_type_name, + bt_common_color_reset()); + } + + for (i = 0; i < count; i++) { + const bt_component_class *comp_class = + spec_comp_cls_borrow_comp_cls_func( + borrow_comp_cls_by_index_func(plugin, i)); + const char *comp_class_name = + bt_component_class_get_name(comp_class); + const char *comp_class_description = + bt_component_class_get_description(comp_class); + bt_component_class_type type = + bt_component_class_get_type(comp_class); + + printf(" "); + print_plugin_comp_cls_opt(stdout, + bt_plugin_get_name(plugin), comp_class_name, + type); + + if (comp_class_description) { + printf(": %s", comp_class_description); + } + + printf("\n"); + } + +end: + return; +} + +static +int cmd_list_plugins(struct bt_config *cfg) +{ + int ret = 0; + int plugins_count, component_classes_count = 0, i; + + printf("From the following plugin paths:\n\n"); + print_value(stdout, cfg->plugin_paths, 2); + printf("\n"); + plugins_count = loaded_plugins->len; + if (plugins_count == 0) { + printf("No plugins found.\n"); + goto end; + } + + for (i = 0; i < plugins_count; i++) { + const bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); + + component_classes_count += + bt_plugin_get_source_component_class_count(plugin) + + bt_plugin_get_filter_component_class_count(plugin) + + bt_plugin_get_sink_component_class_count(plugin); + } + + printf("Found %s%d%s component classes in %s%d%s plugins.\n", + bt_common_color_bold(), + component_classes_count, + bt_common_color_reset(), + bt_common_color_bold(), + plugins_count, + bt_common_color_reset()); + + for (i = 0; i < plugins_count; i++) { + const bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); + + printf("\n"); + print_plugin_info(plugin); + cmd_list_plugins_print_component_classes(plugin, "Source", + bt_plugin_get_source_component_class_count(plugin), + (plugin_borrow_comp_cls_by_index_func_t) + bt_plugin_borrow_source_component_class_by_index_const, + (spec_comp_cls_borrow_comp_cls_func_t) + bt_component_class_source_as_component_class); + cmd_list_plugins_print_component_classes(plugin, "Filter", + bt_plugin_get_filter_component_class_count(plugin), + (plugin_borrow_comp_cls_by_index_func_t) + bt_plugin_borrow_filter_component_class_by_index_const, + (spec_comp_cls_borrow_comp_cls_func_t) + bt_component_class_filter_as_component_class); + cmd_list_plugins_print_component_classes(plugin, "Sink", + bt_plugin_get_sink_component_class_count(plugin), + (plugin_borrow_comp_cls_by_index_func_t) + bt_plugin_borrow_sink_component_class_by_index_const, + (spec_comp_cls_borrow_comp_cls_func_t) + bt_component_class_sink_as_component_class); + } + +end: + return ret; +} + +static +int cmd_print_lttng_live_sessions(struct bt_config *cfg) +{ + int ret = 0; + const bt_component_class *comp_cls = NULL; + const bt_value *results = NULL; + bt_value *params = NULL; + const bt_value *map = NULL; + const bt_value *v = NULL; + static const char * const plugin_name = "ctf"; + static const char * const comp_cls_name = "lttng-live"; + static const bt_component_class_type comp_cls_type = + BT_COMPONENT_CLASS_TYPE_SOURCE; + int64_t array_size, i; + const char *fail_reason = NULL; + FILE *out_stream = stdout; + + BT_ASSERT(cfg->cmd_data.print_lttng_live_sessions.url); + comp_cls = find_component_class(plugin_name, comp_cls_name, + comp_cls_type); + if (!comp_cls) { + BT_LOGE("Cannot find component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d", + plugin_name, comp_cls_name, + BT_COMPONENT_CLASS_TYPE_SOURCE); + fprintf(stderr, "%s%sCannot find component class %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, plugin_name, + comp_cls_name, comp_cls_type); + fprintf(stderr, "\n"); + goto error; + } + + params = bt_value_map_create(); + if (!params) { + goto error; + } + + ret = bt_value_map_insert_string_entry(params, "url", + cfg->cmd_data.print_lttng_live_sessions.url->str); + if (ret) { + goto error; + } + + ret = query(comp_cls, "sessions", params, + &results, &fail_reason); + if (ret) { + goto failed; + } + + BT_ASSERT(results); + + if (!bt_value_is_array(results)) { + BT_LOGE_STR("Expecting an array for sessions query."); + fprintf(stderr, "%s%sUnexpected type returned by session query%s\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + goto error; + } + + if (cfg->cmd_data.print_lttng_live_sessions.output_path->len > 0) { + out_stream = + fopen(cfg->cmd_data.print_lttng_live_sessions.output_path->str, + "w"); + if (!out_stream) { + ret = -1; + BT_LOGE_ERRNO("Cannot open file for writing", + ": path=\"%s\"", + cfg->cmd_data.print_lttng_live_sessions.output_path->str); + goto end; + } + } + + array_size = bt_value_array_get_size(results); + for (i = 0; i < array_size; i++) { + const char *url_text; + int64_t timer_us, streams, clients; + + map = bt_value_array_borrow_element_by_index_const(results, i); + if (!map) { + BT_LOGE_STR("Unexpected empty array entry."); + goto error; + } + if (!bt_value_is_map(map)) { + BT_LOGE_STR("Unexpected entry type."); + goto error; + } + + v = bt_value_map_borrow_entry_value_const(map, "url"); + if (!v) { + BT_LOGE_STR("Unexpected empty array \"url\" entry."); + goto error; + } + url_text = bt_value_string_get(v); + fprintf(out_stream, "%s", url_text); + v = bt_value_map_borrow_entry_value_const(map, "timer-us"); + if (!v) { + BT_LOGE_STR("Unexpected empty array \"timer-us\" entry."); + goto error; + } + timer_us = bt_value_signed_integer_get(v); + fprintf(out_stream, " (timer = %" PRIu64 ", ", timer_us); + v = bt_value_map_borrow_entry_value_const(map, "stream-count"); + if (!v) { + BT_LOGE_STR("Unexpected empty array \"stream-count\" entry."); + goto error; + } + streams = bt_value_signed_integer_get(v); + fprintf(out_stream, "%" PRIu64 " stream(s), ", streams); + v = bt_value_map_borrow_entry_value_const(map, "client-count"); + if (!v) { + BT_LOGE_STR("Unexpected empty array \"client-count\" entry."); + goto error; + } + clients = bt_value_signed_integer_get(v); + fprintf(out_stream, "%" PRIu64 " client(s) connected)\n", clients); + } + + goto end; + +failed: + BT_LOGE("Failed to query for sessions: %s", fail_reason); + fprintf(stderr, "%s%sFailed to request sessions: %s%s\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + fail_reason, + bt_common_color_reset()); + +error: + ret = -1; + +end: + bt_value_put_ref(results); + bt_value_put_ref(params); + bt_component_class_put_ref(comp_cls); + + if (out_stream && out_stream != stdout) { + int fclose_ret = fclose(out_stream); + + if (fclose_ret) { + BT_LOGE_ERRNO("Cannot close file stream", + ": path=\"%s\"", + cfg->cmd_data.print_lttng_live_sessions.output_path->str); + } + } + + return ret; +} + +static +int cmd_print_ctf_metadata(struct bt_config *cfg) +{ + int ret = 0; + const bt_component_class *comp_cls = NULL; + const bt_value *results = NULL; + bt_value *params = NULL; + const bt_value *metadata_text_value = NULL; + const char *metadata_text = NULL; + static const char * const plugin_name = "ctf"; + static const char * const comp_cls_name = "fs"; + static const bt_component_class_type comp_cls_type = + BT_COMPONENT_CLASS_TYPE_SOURCE; + const char *fail_reason = NULL; + FILE *out_stream = stdout; + + BT_ASSERT(cfg->cmd_data.print_ctf_metadata.path); + comp_cls = find_component_class(plugin_name, comp_cls_name, + comp_cls_type); + if (!comp_cls) { + BT_LOGE("Cannot find component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d", + plugin_name, comp_cls_name, + BT_COMPONENT_CLASS_TYPE_SOURCE); + fprintf(stderr, "%s%sCannot find component class %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, plugin_name, + comp_cls_name, comp_cls_type); + fprintf(stderr, "\n"); + ret = -1; + goto end; + } + + params = bt_value_map_create(); + if (!params) { + ret = -1; + goto end; + } + + ret = bt_value_map_insert_string_entry(params, "path", + cfg->cmd_data.print_ctf_metadata.path->str); + if (ret) { + ret = -1; + goto end; + } + + ret = query(comp_cls, "metadata-info", + params, &results, &fail_reason); + if (ret) { + goto failed; + } + + metadata_text_value = bt_value_map_borrow_entry_value_const(results, + "text"); + if (!metadata_text_value) { + BT_LOGE_STR("Cannot find `text` string value in the resulting metadata info object."); + ret = -1; + goto end; + } + + metadata_text = bt_value_string_get(metadata_text_value); + + if (cfg->cmd_data.print_ctf_metadata.output_path->len > 0) { + out_stream = + fopen(cfg->cmd_data.print_ctf_metadata.output_path->str, + "w"); + if (!out_stream) { + ret = -1; + BT_LOGE_ERRNO("Cannot open file for writing", + ": path=\"%s\"", + cfg->cmd_data.print_ctf_metadata.output_path->str); + goto end; + } + } + + ret = fprintf(out_stream, "%s\n", metadata_text); + if (ret < 0) { + BT_LOGE("Cannot write whole metadata text to output stream: " + "ret=%d", ret); + goto end; + } + + ret = 0; + + goto end; + +failed: + ret = -1; + BT_LOGE("Failed to query for metadata info: %s", fail_reason); + fprintf(stderr, "%s%sFailed to request metadata info: %s%s\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + fail_reason, + bt_common_color_reset()); + +end: + bt_value_put_ref(results); + bt_value_put_ref(params); + bt_component_class_put_ref(comp_cls); + + if (out_stream && out_stream != stdout) { + int fclose_ret = fclose(out_stream); + + if (fclose_ret) { + BT_LOGE_ERRNO("Cannot close file stream", + ": path=\"%s\"", + cfg->cmd_data.print_ctf_metadata.output_path->str); + } + } + + return ret; +} + +struct port_id { + char *instance_name; + char *port_name; +}; + +struct trace_range { + uint64_t intersection_range_begin_ns; + uint64_t intersection_range_end_ns; +}; + +static +guint port_id_hash(gconstpointer v) +{ + const struct port_id *id = v; + + BT_ASSERT(id->instance_name); + BT_ASSERT(id->port_name); + + return g_str_hash(id->instance_name) ^ g_str_hash(id->port_name); +} + +static +gboolean port_id_equal(gconstpointer v1, gconstpointer v2) +{ + const struct port_id *id1 = v1; + const struct port_id *id2 = v2; + + return !strcmp(id1->instance_name, id2->instance_name) && + !strcmp(id1->port_name, id2->port_name); +} + +static +void port_id_destroy(gpointer data) +{ + struct port_id *id = data; + + free(id->instance_name); + free(id->port_name); + free(id); +} + +static +void trace_range_destroy(gpointer data) +{ + free(data); +} + +struct cmd_run_ctx { + /* Owned by this */ + GHashTable *src_components; + + /* Owned by this */ + GHashTable *flt_components; + + /* Owned by this */ + GHashTable *sink_components; + + /* Owned by this */ + bt_graph *graph; + + /* Weak */ + struct bt_config *cfg; + + bool connect_ports; + + bool stream_intersection_mode; + + /* + * Association of struct port_id -> struct trace_range. + */ + GHashTable *intersections; +}; + +/* Returns a timestamp of the form "(-)s.ns" */ +static +char *s_from_ns(int64_t ns) +{ + int ret; + char *s_ret = NULL; + bool is_negative; + int64_t ts_sec_abs, ts_nsec_abs; + int64_t ts_sec = ns / NSEC_PER_SEC; + int64_t ts_nsec = ns % NSEC_PER_SEC; + + if (ts_sec >= 0 && ts_nsec >= 0) { + is_negative = false; + ts_sec_abs = ts_sec; + ts_nsec_abs = ts_nsec; + } else if (ts_sec > 0 && ts_nsec < 0) { + is_negative = false; + ts_sec_abs = ts_sec - 1; + ts_nsec_abs = NSEC_PER_SEC + ts_nsec; + } else if (ts_sec == 0 && ts_nsec < 0) { + is_negative = true; + ts_sec_abs = ts_sec; + ts_nsec_abs = -ts_nsec; + } else if (ts_sec < 0 && ts_nsec > 0) { + is_negative = true; + ts_sec_abs = -(ts_sec + 1); + ts_nsec_abs = NSEC_PER_SEC - ts_nsec; + } else if (ts_sec < 0 && ts_nsec == 0) { + is_negative = true; + ts_sec_abs = -ts_sec; + ts_nsec_abs = ts_nsec; + } else { /* (ts_sec < 0 && ts_nsec < 0) */ + is_negative = true; + ts_sec_abs = -ts_sec; + ts_nsec_abs = -ts_nsec; + } + + ret = asprintf(&s_ret, "%s%" PRId64 ".%09" PRId64, + is_negative ? "-" : "", ts_sec_abs, ts_nsec_abs); + if (ret < 0) { + s_ret = NULL; + } + return s_ret; +} + +static +int cmd_run_ctx_connect_upstream_port_to_downstream_component( + struct cmd_run_ctx *ctx, + const bt_component *upstream_comp, + const bt_port_output *out_upstream_port, + struct bt_config_connection *cfg_conn) +{ + typedef uint64_t (*input_port_count_func_t)(void *); + typedef const bt_port_input *(*borrow_input_port_by_index_func_t)( + const void *, uint64_t); + const bt_port *upstream_port = + bt_port_output_as_port_const(out_upstream_port); + + int ret = 0; + GQuark downstreamp_comp_name_quark; + void *downstream_comp; + uint64_t downstream_port_count; + uint64_t i; + input_port_count_func_t port_count_fn; + borrow_input_port_by_index_func_t port_by_index_fn; + bt_graph_status status = BT_GRAPH_STATUS_ERROR; + bool insert_trimmer = false; + bt_value *trimmer_params = NULL; + char *intersection_begin = NULL; + char *intersection_end = NULL; + const bt_component_filter *trimmer = NULL; + const bt_component_class_filter *trimmer_class = NULL; + const bt_port_input *trimmer_input = NULL; + const bt_port_output *trimmer_output = NULL; + + if (ctx->intersections && + bt_component_get_class_type(upstream_comp) == + BT_COMPONENT_CLASS_TYPE_SOURCE) { + struct trace_range *range; + struct port_id port_id = { + .instance_name = (char *) bt_component_get_name(upstream_comp), + .port_name = (char *) bt_port_get_name(upstream_port) + }; + + if (!port_id.instance_name || !port_id.port_name) { + goto error; + } + + range = (struct trace_range *) g_hash_table_lookup( + ctx->intersections, &port_id); + if (range) { + bt_value_status status; + + intersection_begin = s_from_ns( + range->intersection_range_begin_ns); + intersection_end = s_from_ns( + range->intersection_range_end_ns); + if (!intersection_begin || !intersection_end) { + BT_LOGE_STR("Cannot create trimmer argument timestamp string."); + goto error; + } + + insert_trimmer = true; + trimmer_params = bt_value_map_create(); + if (!trimmer_params) { + goto error; + } + + status = bt_value_map_insert_string_entry( + trimmer_params, "begin", intersection_begin); + if (status != BT_VALUE_STATUS_OK) { + goto error; + } + status = bt_value_map_insert_string_entry( + trimmer_params, + "end", intersection_end); + if (status != BT_VALUE_STATUS_OK) { + goto error; + } + } + + trimmer_class = find_filter_component_class("utils", "trimmer"); + if (!trimmer_class) { + goto error; + } + } + + BT_LOGI("Connecting upstream port to the next available downstream port: " + "upstream-port-addr=%p, upstream-port-name=\"%s\", " + "downstream-comp-name=\"%s\", conn-arg=\"%s\"", + upstream_port, bt_port_get_name(upstream_port), + cfg_conn->downstream_comp_name->str, + cfg_conn->arg->str); + downstreamp_comp_name_quark = g_quark_from_string( + cfg_conn->downstream_comp_name->str); + BT_ASSERT(downstreamp_comp_name_quark > 0); + downstream_comp = g_hash_table_lookup(ctx->flt_components, + GUINT_TO_POINTER(downstreamp_comp_name_quark)); + port_count_fn = (input_port_count_func_t) + bt_component_filter_get_input_port_count; + port_by_index_fn = (borrow_input_port_by_index_func_t) + bt_component_filter_borrow_input_port_by_index_const; + + if (!downstream_comp) { + downstream_comp = g_hash_table_lookup(ctx->sink_components, + GUINT_TO_POINTER(downstreamp_comp_name_quark)); + port_count_fn = (input_port_count_func_t) + bt_component_sink_get_input_port_count; + port_by_index_fn = (borrow_input_port_by_index_func_t) + bt_component_sink_borrow_input_port_by_index_const; + } + + if (!downstream_comp) { + BT_LOGE("Cannot find downstream component: comp-name=\"%s\", " + "conn-arg=\"%s\"", cfg_conn->downstream_comp_name->str, + cfg_conn->arg->str); + fprintf(stderr, "Cannot create connection: cannot find downstream component: %s\n", + cfg_conn->arg->str); + goto error; + } + + downstream_port_count = port_count_fn(downstream_comp); + + for (i = 0; i < downstream_port_count; i++) { + const bt_port_input *in_downstream_port = + port_by_index_fn(downstream_comp, i); + const bt_port *downstream_port = + bt_port_input_as_port_const(in_downstream_port); + const char *upstream_port_name; + const char *downstream_port_name; + + BT_ASSERT(downstream_port); + + /* Skip port if it's already connected. */ + if (bt_port_is_connected(downstream_port)) { + BT_LOGD("Skipping downstream port: already connected: " + "port-addr=%p, port-name=\"%s\"", + downstream_port, + bt_port_get_name(downstream_port)); + continue; + } + + downstream_port_name = bt_port_get_name(downstream_port); + BT_ASSERT(downstream_port_name); + upstream_port_name = bt_port_get_name(upstream_port); + BT_ASSERT(upstream_port_name); + + if (!bt_common_star_glob_match( + cfg_conn->downstream_port_glob->str, SIZE_MAX, + downstream_port_name, SIZE_MAX)) { + continue; + } + + if (insert_trimmer) { + /* + * In order to insert the trimmer between the + * two components that were being connected, we + * create a connection configuration entry which + * describes a connection from the trimmer's + * output to the original input that was being + * connected. + * + * Hence, the creation of the trimmer will cause + * the graph "new port" listener to establish + * all downstream connections as its output port + * is connected. We will then establish the + * connection between the original upstream + * source and the trimmer. + */ + char *trimmer_name = NULL; + bt_graph_status graph_status; + + ret = asprintf(&trimmer_name, + "stream-intersection-trimmer-%s", + upstream_port_name); + if (ret < 0) { + goto error; + } + ret = 0; + + ctx->connect_ports = false; + graph_status = bt_graph_add_filter_component( + ctx->graph, trimmer_class, trimmer_name, + trimmer_params, &trimmer); + free(trimmer_name); + if (graph_status != BT_GRAPH_STATUS_OK) { + goto error; + } + BT_ASSERT(trimmer); + + trimmer_input = + bt_component_filter_borrow_input_port_by_index_const( + trimmer, 0); + if (!trimmer_input) { + goto error; + } + trimmer_output = + bt_component_filter_borrow_output_port_by_index_const( + trimmer, 0); + if (!trimmer_output) { + goto error; + } + + /* + * Replace the current downstream port by the trimmer's + * upstream port. + */ + in_downstream_port = trimmer_input; + downstream_port = + bt_port_input_as_port_const(in_downstream_port); + downstream_port_name = bt_port_get_name( + downstream_port); + BT_ASSERT(downstream_port_name); + } + + /* We have a winner! */ + status = bt_graph_connect_ports(ctx->graph, + out_upstream_port, in_downstream_port, NULL); + downstream_port = NULL; + switch (status) { + case BT_GRAPH_STATUS_OK: + break; + case BT_GRAPH_STATUS_CANCELED: + BT_LOGI_STR("Graph was canceled by user."); + status = BT_GRAPH_STATUS_OK; + break; + case BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: + BT_LOGE("A component refused a connection to one of its ports: " + "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " + "upstream-port-addr=%p, upstream-port-name=\"%s\", " + "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " + "downstream-port-addr=%p, downstream-port-name=\"%s\", " + "conn-arg=\"%s\"", + upstream_comp, bt_component_get_name(upstream_comp), + upstream_port, bt_port_get_name(upstream_port), + downstream_comp, cfg_conn->downstream_comp_name->str, + downstream_port, downstream_port_name, + cfg_conn->arg->str); + fprintf(stderr, + "A component refused a connection to one of its ports (`%s` to `%s`): %s\n", + bt_port_get_name(upstream_port), + downstream_port_name, + cfg_conn->arg->str); + break; + default: + BT_LOGE("Cannot create connection: graph refuses to connect ports: " + "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " + "upstream-port-addr=%p, upstream-port-name=\"%s\", " + "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " + "downstream-port-addr=%p, downstream-port-name=\"%s\", " + "conn-arg=\"%s\"", + upstream_comp, bt_component_get_name(upstream_comp), + upstream_port, bt_port_get_name(upstream_port), + downstream_comp, cfg_conn->downstream_comp_name->str, + downstream_port, downstream_port_name, + cfg_conn->arg->str); + fprintf(stderr, + "Cannot create connection: graph refuses to connect ports (`%s` to `%s`): %s\n", + bt_port_get_name(upstream_port), + downstream_port_name, + cfg_conn->arg->str); + goto error; + } + + BT_LOGI("Connected component ports: " + "upstream-comp-addr=%p, upstream-comp-name=\"%s\", " + "upstream-port-addr=%p, upstream-port-name=\"%s\", " + "downstream-comp-addr=%p, downstream-comp-name=\"%s\", " + "downstream-port-addr=%p, downstream-port-name=\"%s\", " + "conn-arg=\"%s\"", + upstream_comp, bt_component_get_name(upstream_comp), + upstream_port, bt_port_get_name(upstream_port), + downstream_comp, cfg_conn->downstream_comp_name->str, + downstream_port, downstream_port_name, + cfg_conn->arg->str); + + if (insert_trimmer) { + /* + * The first connection, from the source to the trimmer, + * has been done. We now connect the trimmer to the + * original downstream port. + */ + ret = cmd_run_ctx_connect_upstream_port_to_downstream_component( + ctx, + bt_component_filter_as_component_const(trimmer), + trimmer_output, cfg_conn); + if (ret) { + goto error; + } + ctx->connect_ports = true; + } + + /* + * We found a matching downstream port: the search is + * over. + */ + goto end; + } + + /* No downstream port found */ + BT_LOGE("Cannot create connection: cannot find a matching downstream port for upstream port: " + "upstream-port-addr=%p, upstream-port-name=\"%s\", " + "downstream-comp-name=\"%s\", conn-arg=\"%s\"", + upstream_port, bt_port_get_name(upstream_port), + cfg_conn->downstream_comp_name->str, + cfg_conn->arg->str); + fprintf(stderr, + "Cannot create connection: cannot find a matching downstream port for upstream port `%s`: %s\n", + bt_port_get_name(upstream_port), cfg_conn->arg->str); + +error: + ret = -1; + +end: + free(intersection_begin); + free(intersection_end); + BT_VALUE_PUT_REF_AND_RESET(trimmer_params); + BT_COMPONENT_CLASS_FILTER_PUT_REF_AND_RESET(trimmer_class); + BT_COMPONENT_FILTER_PUT_REF_AND_RESET(trimmer); + return ret; +} + +static +int cmd_run_ctx_connect_upstream_port(struct cmd_run_ctx *ctx, + const bt_port_output *upstream_port) +{ + int ret = 0; + const char *upstream_port_name; + const char *upstream_comp_name; + const bt_component *upstream_comp = NULL; + size_t i; + + BT_ASSERT(ctx); + BT_ASSERT(upstream_port); + upstream_port_name = bt_port_get_name( + bt_port_output_as_port_const(upstream_port)); + BT_ASSERT(upstream_port_name); + upstream_comp = bt_port_borrow_component_const( + bt_port_output_as_port_const(upstream_port)); + if (!upstream_comp) { + BT_LOGW("Upstream port to connect is not part of a component: " + "port-addr=%p, port-name=\"%s\"", + upstream_port, upstream_port_name); + ret = -1; + goto end; + } + + upstream_comp_name = bt_component_get_name(upstream_comp); + BT_ASSERT(upstream_comp_name); + BT_LOGI("Connecting upstream port: comp-addr=%p, comp-name=\"%s\", " + "port-addr=%p, port-name=\"%s\"", + upstream_comp, upstream_comp_name, + upstream_port, upstream_port_name); + + for (i = 0; i < ctx->cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *cfg_conn = + g_ptr_array_index( + ctx->cfg->cmd_data.run.connections, i); + + if (strcmp(cfg_conn->upstream_comp_name->str, + upstream_comp_name)) { + continue; + } + + if (!bt_common_star_glob_match( + cfg_conn->upstream_port_glob->str, + SIZE_MAX, upstream_port_name, SIZE_MAX)) { + continue; + } + + ret = cmd_run_ctx_connect_upstream_port_to_downstream_component( + ctx, upstream_comp, upstream_port, cfg_conn); + if (ret) { + BT_LOGE("Cannot connect upstream port: " + "port-addr=%p, port-name=\"%s\"", + upstream_port, + upstream_port_name); + fprintf(stderr, + "Cannot connect port `%s` of component `%s` to a downstream port: %s\n", + upstream_port_name, + upstream_comp_name, + cfg_conn->arg->str); + goto error; + } + goto end; + } + + BT_LOGE("Cannot connect upstream port: port does not match any connection argument: " + "port-addr=%p, port-name=\"%s\"", upstream_port, + upstream_port_name); + fprintf(stderr, + "Cannot create connection: upstream port `%s` does not match any connection\n", + upstream_port_name); + +error: + ret = -1; + +end: + return ret; +} + +static +bt_graph_listener_status +graph_output_port_added_listener(struct cmd_run_ctx *ctx, + const bt_port_output *out_port) +{ + const bt_component *comp; + const bt_port *port = bt_port_output_as_port_const(out_port); + bt_graph_listener_status ret = BT_GRAPH_LISTENER_STATUS_OK; + + comp = bt_port_borrow_component_const(port); + BT_LOGI("Port added to a graph's component: comp-addr=%p, " + "comp-name=\"%s\", port-addr=%p, port-name=\"%s\"", + comp, comp ? bt_component_get_name(comp) : "", + port, bt_port_get_name(port)); + + if (!ctx->connect_ports) { + goto end; + } + + if (!comp) { + BT_LOGW_STR("Port has no component."); + goto end; + } + + if (bt_port_is_connected(port)) { + BT_LOGW_STR("Port is already connected."); + goto end; + } + + if (cmd_run_ctx_connect_upstream_port(ctx, out_port)) { + BT_LOGF_STR("Cannot connect upstream port."); + fprintf(stderr, "Added port could not be connected: aborting\n"); + ret = BT_GRAPH_LISTENER_STATUS_ERROR; + goto end; + } + +end: + return ret; +} + +static +bt_graph_listener_status graph_source_output_port_added_listener( + const bt_component_source *component, + const bt_port_output *port, void *data) +{ + return graph_output_port_added_listener(data, port); +} + +static +bt_graph_listener_status graph_filter_output_port_added_listener( + const bt_component_filter *component, + const bt_port_output *port, void *data) +{ + return graph_output_port_added_listener(data, port); +} + +static +void cmd_run_ctx_destroy(struct cmd_run_ctx *ctx) +{ + if (!ctx) { + return; + } + + if (ctx->src_components) { + g_hash_table_destroy(ctx->src_components); + ctx->src_components = NULL; + } + + if (ctx->flt_components) { + g_hash_table_destroy(ctx->flt_components); + ctx->flt_components = NULL; + } + + if (ctx->sink_components) { + g_hash_table_destroy(ctx->sink_components); + ctx->sink_components = NULL; + } + + if (ctx->intersections) { + g_hash_table_destroy(ctx->intersections); + ctx->intersections = NULL; + } + + BT_GRAPH_PUT_REF_AND_RESET(ctx->graph); + the_graph = NULL; + ctx->cfg = NULL; +} + +static +int cmd_run_ctx_init(struct cmd_run_ctx *ctx, struct bt_config *cfg) +{ + int ret = 0; + bt_graph_status status; + + ctx->cfg = cfg; + ctx->connect_ports = false; + ctx->src_components = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); + if (!ctx->src_components) { + goto error; + } + + ctx->flt_components = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); + if (!ctx->flt_components) { + goto error; + } + + ctx->sink_components = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify) bt_object_put_ref); + if (!ctx->sink_components) { + goto error; + } + + if (cfg->cmd_data.run.stream_intersection_mode) { + ctx->stream_intersection_mode = true; + ctx->intersections = g_hash_table_new_full(port_id_hash, + port_id_equal, port_id_destroy, trace_range_destroy); + if (!ctx->intersections) { + goto error; + } + } + + ctx->graph = bt_graph_create(); + if (!ctx->graph) { + goto error; + } + + the_graph = ctx->graph; + status = bt_graph_add_source_component_output_port_added_listener( + ctx->graph, graph_source_output_port_added_listener, NULL, ctx, + NULL); + if (status != BT_GRAPH_STATUS_OK) { + BT_LOGE_STR("Cannot add \"port added\" listener to graph."); + goto error; + } + + status = bt_graph_add_filter_component_output_port_added_listener( + ctx->graph, graph_filter_output_port_added_listener, NULL, ctx, + NULL); + if (status != BT_GRAPH_STATUS_OK) { + BT_LOGE_STR("Cannot add \"port added\" listener to graph."); + goto error; + } + + goto end; + +error: + cmd_run_ctx_destroy(ctx); + ret = -1; + +end: + return ret; +} + +static +int set_stream_intersections(struct cmd_run_ctx *ctx, + struct bt_config_component *cfg_comp, + const bt_component_class_source *src_comp_cls) +{ + int ret = 0; + uint64_t trace_idx; + int64_t trace_count; + const char *path = NULL; + const bt_value *query_result = NULL; + const bt_value *trace_info = NULL; + const bt_value *intersection_range = NULL; + const bt_value *intersection_begin = NULL; + const bt_value *intersection_end = NULL; + const bt_value *stream_infos = NULL; + const bt_value *stream_info = NULL; + struct port_id *port_id = NULL; + struct trace_range *trace_range = NULL; + const char *fail_reason = NULL; + const bt_component_class *comp_cls = + bt_component_class_source_as_component_class_const(src_comp_cls); + + ret = query(comp_cls, "trace-info", + cfg_comp->params, &query_result, + &fail_reason); + if (ret) { + BT_LOGD("Component class does not support the `trace-info` query: %s: " + "comp-class-name=\"%s\"", fail_reason, + bt_component_class_get_name(comp_cls)); + ret = -1; + goto error; + } + + BT_ASSERT(query_result); + + if (!bt_value_is_array(query_result)) { + BT_LOGD("Unexpected format of \'trace-info\' query result: " + "component-class-name=%s", + bt_component_class_get_name(comp_cls)); + ret = -1; + goto error; + } + + trace_count = bt_value_array_get_size(query_result); + if (trace_count < 0) { + ret = -1; + goto error; + } + + for (trace_idx = 0; trace_idx < trace_count; trace_idx++) { + int64_t begin, end; + uint64_t stream_idx; + int64_t stream_count; + + trace_info = bt_value_array_borrow_element_by_index_const( + query_result, trace_idx); + if (!trace_info || !bt_value_is_map(trace_info)) { + ret = -1; + BT_LOGD_STR("Cannot retrieve trace from query result."); + goto error; + } + + intersection_range = bt_value_map_borrow_entry_value_const( + trace_info, "intersection-range-ns"); + if (!intersection_range) { + ret = -1; + BT_LOGD_STR("Cannot retrieve \'intersetion-range-ns\' field from query result."); + goto error; + } + + intersection_begin = bt_value_map_borrow_entry_value_const(intersection_range, + "begin"); + if (!intersection_begin) { + ret = -1; + BT_LOGD_STR("Cannot retrieve intersection-range-ns \'begin\' field from query result."); + goto error; + } + + intersection_end = bt_value_map_borrow_entry_value_const(intersection_range, + "end"); + if (!intersection_end) { + ret = -1; + BT_LOGD_STR("Cannot retrieve intersection-range-ns \'end\' field from query result."); + goto error; + } + + begin = bt_value_signed_integer_get(intersection_begin); + end = bt_value_signed_integer_get(intersection_end); + + if (begin < 0 || end < 0 || end < begin) { + BT_LOGW("Invalid trace stream intersection values: " + "intersection-range-ns:begin=%" PRId64 + ", intersection-range-ns:end=%" PRId64, + begin, end); + ret = -1; + goto error; + } + + stream_infos = bt_value_map_borrow_entry_value_const(trace_info, + "streams"); + if (!stream_infos || !bt_value_is_array(stream_infos)) { + ret = -1; + BT_LOGD_STR("Cannot retrieve stream information from trace in query result."); + goto error; + } + + stream_count = bt_value_array_get_size(stream_infos); + if (stream_count < 0) { + ret = -1; + goto error; + } + + for (stream_idx = 0; stream_idx < stream_count; stream_idx++) { + const bt_value *port_name; + + port_id = g_new0(struct port_id, 1); + if (!port_id) { + ret = -1; + BT_LOGE_STR("Cannot allocate memory for port_id structure."); + goto error; + } + port_id->instance_name = strdup(cfg_comp->instance_name->str); + if (!port_id->instance_name) { + ret = -1; + BT_LOGE_STR("Cannot allocate memory for port_id component instance name."); + goto error; + } + + trace_range = g_new0(struct trace_range, 1); + if (!trace_range) { + ret = -1; + BT_LOGE_STR("Cannot allocate memory for trace_range structure."); + goto error; + } + trace_range->intersection_range_begin_ns = begin; + trace_range->intersection_range_end_ns = end; + + stream_info = bt_value_array_borrow_element_by_index_const( + stream_infos, stream_idx); + if (!stream_info || !bt_value_is_map(stream_info)) { + ret = -1; + BT_LOGD_STR("Cannot retrieve stream informations from trace in query result."); + goto error; + } + + port_name = bt_value_map_borrow_entry_value_const(stream_info, "port-name"); + if (!port_name || !bt_value_is_string(port_name)) { + ret = -1; + BT_LOGD_STR("Cannot retrieve port name in query result."); + goto error; + } + + port_id->port_name = g_strdup(bt_value_string_get(port_name)); + if (!port_id->port_name) { + ret = -1; + BT_LOGE_STR("Cannot allocate memory for port_id port_name."); + goto error; + } + + BT_LOGD("Inserting stream intersection "); + + g_hash_table_insert(ctx->intersections, port_id, trace_range); + + port_id = NULL; + trace_range = NULL; + } + } + + goto end; + +error: + fprintf(stderr, "%s%sCannot determine stream intersection of trace at path \'%s\'.%s\n", + bt_common_color_bold(), + bt_common_color_fg_yellow(), + path ? path : "(unknown)", + bt_common_color_reset()); +end: + bt_value_put_ref(query_result); + g_free(port_id); + g_free(trace_range); + return ret; +} + +static +int cmd_run_ctx_create_components_from_config_components( + struct cmd_run_ctx *ctx, GPtrArray *cfg_components) +{ + size_t i; + const void *comp_cls = NULL; + const void *comp = NULL; + int ret = 0; + + for (i = 0; i < cfg_components->len; i++) { + struct bt_config_component *cfg_comp = + g_ptr_array_index(cfg_components, i); + GQuark quark; + + switch (cfg_comp->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + comp_cls = find_source_component_class( + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str); + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + comp_cls = find_filter_component_class( + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str); + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + comp_cls = find_sink_component_class( + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str); + break; + default: + abort(); + } + + if (!comp_cls) { + BT_LOGE("Cannot find component class: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d", + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str, + cfg_comp->type); + fprintf(stderr, "%s%sCannot find component class %s", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + print_plugin_comp_cls_opt(stderr, + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str, + cfg_comp->type); + fprintf(stderr, "\n"); + goto error; + } + + switch (cfg_comp->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + ret = bt_graph_add_source_component(ctx->graph, + comp_cls, cfg_comp->instance_name->str, + cfg_comp->params, + (void *) &comp); + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + ret = bt_graph_add_filter_component(ctx->graph, + comp_cls, cfg_comp->instance_name->str, + cfg_comp->params, + (void *) &comp); + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + ret = bt_graph_add_sink_component(ctx->graph, + comp_cls, cfg_comp->instance_name->str, + cfg_comp->params, + (void *) &comp); + break; + default: + abort(); + } + + if (ret) { + BT_LOGE("Cannot create component: plugin-name=\"%s\", " + "comp-cls-name=\"%s\", comp-cls-type=%d, " + "comp-name=\"%s\"", + cfg_comp->plugin_name->str, + cfg_comp->comp_cls_name->str, + cfg_comp->type, cfg_comp->instance_name->str); + fprintf(stderr, "%s%sCannot create component `%s`%s\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + cfg_comp->instance_name->str, + bt_common_color_reset()); + goto error; + } + + if (ctx->stream_intersection_mode && + cfg_comp->type == BT_COMPONENT_CLASS_TYPE_SOURCE) { + ret = set_stream_intersections(ctx, cfg_comp, comp_cls); + if (ret) { + goto error; + } + } + + BT_LOGI("Created and inserted component: comp-addr=%p, comp-name=\"%s\"", + comp, cfg_comp->instance_name->str); + quark = g_quark_from_string(cfg_comp->instance_name->str); + BT_ASSERT(quark > 0); + + switch (cfg_comp->type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + g_hash_table_insert(ctx->src_components, + GUINT_TO_POINTER(quark), (void *) comp); + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + g_hash_table_insert(ctx->flt_components, + GUINT_TO_POINTER(quark), (void *) comp); + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + g_hash_table_insert(ctx->sink_components, + GUINT_TO_POINTER(quark), (void *) comp); + break; + default: + abort(); + } + + comp = NULL; + BT_OBJECT_PUT_REF_AND_RESET(comp_cls); + } + + goto end; + +error: + ret = -1; + +end: + bt_object_put_ref(comp); + bt_object_put_ref(comp_cls); + return ret; +} + +static +int cmd_run_ctx_create_components(struct cmd_run_ctx *ctx) +{ + int ret = 0; + + /* + * Make sure that, during this phase, our graph's "port added" + * listener does not connect ports while we are creating the + * components because we have a special, initial phase for + * this. + */ + ctx->connect_ports = false; + + ret = cmd_run_ctx_create_components_from_config_components( + ctx, ctx->cfg->cmd_data.run.sources); + if (ret) { + ret = -1; + goto end; + } + + ret = cmd_run_ctx_create_components_from_config_components( + ctx, ctx->cfg->cmd_data.run.filters); + if (ret) { + ret = -1; + goto end; + } + + ret = cmd_run_ctx_create_components_from_config_components( + ctx, ctx->cfg->cmd_data.run.sinks); + if (ret) { + ret = -1; + goto end; + } + +end: + return ret; +} + +typedef uint64_t (*output_port_count_func_t)(const void *); +typedef const bt_port_output *(*borrow_output_port_by_index_func_t)( + const void *, uint64_t); + +static +int cmd_run_ctx_connect_comp_ports(struct cmd_run_ctx *ctx, + void *comp, output_port_count_func_t port_count_fn, + borrow_output_port_by_index_func_t port_by_index_fn) +{ + int ret = 0; + uint64_t count; + uint64_t i; + + count = port_count_fn(comp); + + for (i = 0; i < count; i++) { + const bt_port_output *upstream_port = port_by_index_fn(comp, i); + + BT_ASSERT(upstream_port); + ret = cmd_run_ctx_connect_upstream_port(ctx, upstream_port); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +static +int cmd_run_ctx_connect_ports(struct cmd_run_ctx *ctx) +{ + int ret = 0; + GHashTableIter iter; + gpointer g_name_quark, g_comp; + + ctx->connect_ports = true; + g_hash_table_iter_init(&iter, ctx->src_components); + + while (g_hash_table_iter_next(&iter, &g_name_quark, &g_comp)) { + ret = cmd_run_ctx_connect_comp_ports(ctx, g_comp, + (output_port_count_func_t) + bt_component_source_get_output_port_count, + (borrow_output_port_by_index_func_t) + bt_component_source_borrow_output_port_by_index_const); + if (ret) { + goto end; + } + } + + g_hash_table_iter_init(&iter, ctx->flt_components); + + while (g_hash_table_iter_next(&iter, &g_name_quark, &g_comp)) { + ret = cmd_run_ctx_connect_comp_ports(ctx, g_comp, + (output_port_count_func_t) + bt_component_filter_get_output_port_count, + (borrow_output_port_by_index_func_t) + bt_component_filter_borrow_output_port_by_index_const); + if (ret) { + goto end; + } + } + +end: + return ret; +} + +static inline +const char *bt_graph_status_str(bt_graph_status status) +{ + switch (status) { + case BT_GRAPH_STATUS_OK: + return "BT_GRAPH_STATUS_OK"; + case BT_GRAPH_STATUS_END: + return "BT_GRAPH_STATUS_END"; + case BT_GRAPH_STATUS_AGAIN: + return "BT_GRAPH_STATUS_AGAIN"; + case BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION: + return "BT_GRAPH_STATUS_COMPONENT_REFUSES_PORT_CONNECTION"; + case BT_GRAPH_STATUS_CANCELED: + return "BT_GRAPH_STATUS_CANCELED"; + case BT_GRAPH_STATUS_ERROR: + return "BT_GRAPH_STATUS_ERROR"; + case BT_GRAPH_STATUS_NOMEM: + return "BT_GRAPH_STATUS_NOMEM"; + default: + return "(unknown)"; + } +} + +static +int cmd_run(struct bt_config *cfg) +{ + int ret = 0; + struct cmd_run_ctx ctx = { 0 }; + + /* Initialize the command's context and the graph object */ + if (cmd_run_ctx_init(&ctx, cfg)) { + BT_LOGE_STR("Cannot initialize the command's context."); + fprintf(stderr, "Cannot initialize the command's context\n"); + goto error; + } + + if (canceled) { + BT_LOGI_STR("Canceled by user before creating components."); + goto error; + } + + BT_LOGI_STR("Creating components."); + + /* Create the requested component instances */ + if (cmd_run_ctx_create_components(&ctx)) { + BT_LOGE_STR("Cannot create components."); + fprintf(stderr, "Cannot create components\n"); + goto error; + } + + if (canceled) { + BT_LOGI_STR("Canceled by user before connecting components."); + goto error; + } + + BT_LOGI_STR("Connecting components."); + + /* Connect the initially visible component ports */ + if (cmd_run_ctx_connect_ports(&ctx)) { + BT_LOGE_STR("Cannot connect initial component ports."); + fprintf(stderr, "Cannot connect initial component ports\n"); + goto error; + } + + if (canceled) { + BT_LOGI_STR("Canceled by user before running the graph."); + goto error; + } + + BT_LOGI_STR("Running the graph."); + + /* Run the graph */ + while (true) { + bt_graph_status graph_status = bt_graph_run(ctx.graph); + + /* + * Reset console in case something messed with console + * codes during the graph's execution. + */ + printf("%s", bt_common_color_reset()); + fflush(stdout); + fprintf(stderr, "%s", bt_common_color_reset()); + BT_LOGV("bt_graph_run() returned: status=%s", + bt_graph_status_str(graph_status)); + + switch (graph_status) { + case BT_GRAPH_STATUS_OK: + break; + case BT_GRAPH_STATUS_CANCELED: + BT_LOGI_STR("Graph was canceled by user."); + goto error; + case BT_GRAPH_STATUS_AGAIN: + if (bt_graph_is_canceled(ctx.graph)) { + BT_LOGI_STR("Graph was canceled by user."); + goto error; + } + + if (cfg->cmd_data.run.retry_duration_us > 0) { + BT_LOGV("Got BT_GRAPH_STATUS_AGAIN: sleeping: " + "time-us=%" PRIu64, + cfg->cmd_data.run.retry_duration_us); + + if (usleep(cfg->cmd_data.run.retry_duration_us)) { + if (bt_graph_is_canceled(ctx.graph)) { + BT_LOGI_STR("Graph was canceled by user."); + goto error; + } + } + } + break; + case BT_GRAPH_STATUS_END: + goto end; + default: + BT_LOGE_STR("Graph failed to complete successfully"); + fprintf(stderr, "Graph failed to complete successfully\n"); + goto error; + } + } + + goto end; + +error: + if (ret == 0) { + ret = -1; + } + +end: + cmd_run_ctx_destroy(&ctx); + return ret; +} + +static +void warn_command_name_and_directory_clash(struct bt_config *cfg) +{ + const char *env_clash; + + if (!cfg->command_name) { + return; + } + + env_clash = getenv(ENV_BABELTRACE_WARN_COMMAND_NAME_DIRECTORY_CLASH); + if (env_clash && strcmp(env_clash, "0") == 0) { + return; + } + + if (g_file_test(cfg->command_name, + G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + fprintf(stderr, "\nNOTE: The `%s` command was executed. If you meant to convert a\n", + cfg->command_name); + fprintf(stderr, "trace located in the local `%s` directory, please use:\n", + cfg->command_name); + fprintf(stderr, "\n"); + fprintf(stderr, " babeltrace2 convert %s [OPTIONS]\n", + cfg->command_name); + } +} + +static +void init_log_level(void) +{ + bt_cli_log_level = bt_log_get_level_from_env(ENV_BABELTRACE_CLI_LOG_LEVEL); +} + +static +void set_auto_log_levels(struct bt_config *cfg) +{ + const char **env_var_name; + + /* + * Override the configuration's default log level if + * BABELTRACE_VERBOSE or BABELTRACE_DEBUG environment variables + * are found for backward compatibility with legacy Babetrace 1. + */ + if (getenv("BABELTRACE_DEBUG") && + strcmp(getenv("BABELTRACE_DEBUG"), "1") == 0) { + cfg->log_level = 'V'; + } else if (getenv("BABELTRACE_VERBOSE") && + strcmp(getenv("BABELTRACE_VERBOSE"), "1") == 0) { + cfg->log_level = 'I'; + } + + /* + * Set log levels according to --debug or --verbose. For + * backward compatibility, --debug is more verbose than + * --verbose. So: + * + * --verbose: INFO log level + * --debug: VERBOSE log level (includes DEBUG, which is + * is less verbose than VERBOSE in the internal + * logging framework) + */ + if (!getenv("BABELTRACE_LOGGING_GLOBAL_LEVEL")) { + if (cfg->verbose) { + bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO); + } else if (cfg->debug) { + bt_logging_set_global_level(BT_LOGGING_LEVEL_VERBOSE); + } else { + /* + * Set library's default log level if not + * explicitly specified. + */ + switch (cfg->log_level) { + case 'N': + bt_logging_set_global_level(BT_LOGGING_LEVEL_NONE); + break; + case 'V': + bt_logging_set_global_level(BT_LOGGING_LEVEL_VERBOSE); + break; + case 'D': + bt_logging_set_global_level(BT_LOGGING_LEVEL_DEBUG); + break; + case 'I': + bt_logging_set_global_level(BT_LOGGING_LEVEL_INFO); + break; + case 'W': + bt_logging_set_global_level(BT_LOGGING_LEVEL_WARN); + break; + case 'E': + bt_logging_set_global_level(BT_LOGGING_LEVEL_ERROR); + break; + case 'F': + bt_logging_set_global_level(BT_LOGGING_LEVEL_FATAL); + break; + default: + abort(); + } + } + } + + if (!getenv(ENV_BABELTRACE_CLI_LOG_LEVEL)) { + if (cfg->verbose) { + bt_cli_log_level = BT_LOG_INFO; + } else if (cfg->debug) { + bt_cli_log_level = BT_LOG_VERBOSE; + } else { + /* + * Set CLI's default log level if not explicitly + * specified. + */ + switch (cfg->log_level) { + case 'N': + bt_cli_log_level = BT_LOG_NONE; + break; + case 'V': + bt_cli_log_level = BT_LOG_VERBOSE; + break; + case 'D': + bt_cli_log_level = BT_LOG_DEBUG; + break; + case 'I': + bt_cli_log_level = BT_LOG_INFO; + break; + case 'W': + bt_cli_log_level = BT_LOG_WARN; + break; + case 'E': + bt_cli_log_level = BT_LOG_ERROR; + break; + case 'F': + bt_cli_log_level = BT_LOG_FATAL; + break; + default: + abort(); + } + } + } + + env_var_name = log_level_env_var_names; + + while (*env_var_name) { + if (!getenv(*env_var_name)) { + if (cfg->verbose) { + g_setenv(*env_var_name, "I", 1); + } else if (cfg->debug) { + g_setenv(*env_var_name, "V", 1); + } else { + char val[2] = { 0 }; + + /* + * Set module's default log level if not + * explicitly specified. + */ + val[0] = cfg->log_level; + g_setenv(*env_var_name, val, 1); + } + } + + env_var_name++; + } +} + +int main(int argc, const char **argv) +{ + int ret; + int retcode; + struct bt_config *cfg; + + init_log_level(); + set_signal_handler(); + init_static_data(); + cfg = bt_config_cli_args_create_with_default(argc, argv, &retcode); + + if (retcode < 0) { + /* Quit without errors; typically usage/version */ + retcode = 0; + BT_LOGI_STR("Quitting without errors."); + goto end; + } + + if (retcode > 0) { + BT_LOGE("Command-line error: retcode=%d", retcode); + goto end; + } + + if (!cfg) { + BT_LOGE_STR("Failed to create a valid Babeltrace configuration."); + fprintf(stderr, "Failed to create Babeltrace configuration\n"); + retcode = 1; + goto end; + } + + set_auto_log_levels(cfg); + print_cfg(cfg); + + if (cfg->command_needs_plugins) { + ret = load_all_plugins(cfg->plugin_paths); + if (ret) { + BT_LOGE("Failed to load plugins: ret=%d", ret); + retcode = 1; + goto end; + } + } + + BT_LOGI("Executing command: cmd=%d, command-name=\"%s\"", + cfg->command, cfg->command_name); + + switch (cfg->command) { + case BT_CONFIG_COMMAND_RUN: + ret = cmd_run(cfg); + break; + case BT_CONFIG_COMMAND_LIST_PLUGINS: + ret = cmd_list_plugins(cfg); + break; + case BT_CONFIG_COMMAND_HELP: + ret = cmd_help(cfg); + break; + case BT_CONFIG_COMMAND_QUERY: + ret = cmd_query(cfg); + break; + case BT_CONFIG_COMMAND_PRINT_CTF_METADATA: + ret = cmd_print_ctf_metadata(cfg); + break; + case BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS: + ret = cmd_print_lttng_live_sessions(cfg); + break; + default: + BT_LOGF("Invalid/unknown command: cmd=%d", cfg->command); + abort(); + } + + BT_LOGI("Command completed: cmd=%d, command-name=\"%s\", ret=%d", + cfg->command, cfg->command_name, ret); + warn_command_name_and_directory_clash(cfg); + retcode = ret ? 1 : 0; + +end: + BT_OBJECT_PUT_REF_AND_RESET(cfg); + fini_static_data(); + return retcode; +} diff --git a/configure.ac b/configure.ac index f0ef757a..20f03456 100644 --- a/configure.ac +++ b/configure.ac @@ -457,7 +457,7 @@ AC_ARG_ENABLE([api-doc], # Built-in plugins # Disabled by default AC_ARG_ENABLE([built-in-plugins], - [AC_HELP_STRING([--enable-built-in-plugins], [Statically-link in-tree plug-ins into the babeltrace binary])], + [AC_HELP_STRING([--enable-built-in-plugins], [Statically-link in-tree plug-ins into the babeltrace2 executable])], [], dnl AC_ARG_ENABLE will fill enable_built_in_plugins with the user choice [enable_built_in_plugins=no] ) @@ -528,17 +528,17 @@ AS_IF([test "x$enable_python_bindings" = xno], AS_IF([test "x$enable_built_in_plugins" = xyes], [ # Built-in plug-ins are only available when the --disable-shared --enable-static options are used. - AS_IF([test "x$enable_static" != xyes], [AC_MSG_ERROR(--enable-static must be used to bundle plug-ins in the babeltrace executable)]) - AS_IF([test "x$enable_shared" = xyes], [AC_MSG_ERROR(--disable-shared must be used to bundle plug-ins in the babeltrace executable)]) + AS_IF([test "x$enable_static" != xyes], [AC_MSG_ERROR(--enable-static must be used to bundle plug-ins in the babeltrace2 executable)]) + AS_IF([test "x$enable_shared" = xyes], [AC_MSG_ERROR(--disable-shared must be used to bundle plug-ins in the babeltrace2 executable)]) ] ) AS_IF([test "x$enable_built_in_python_plugin_support" = xyes], [ - AS_IF([test "x$enable_python_plugins" = xno], [AC_MSG_ERROR([--enable-python-plugins must be used to bundle Python plugin support in the babeltrace executable])]) + AS_IF([test "x$enable_python_plugins" = xno], [AC_MSG_ERROR([--enable-python-plugins must be used to bundle Python plugin support in the babeltrace2 executable])]) # Built-in plug-ins are only available when the --disable-shared --enable-static options are used. - AS_IF([test "x$enable_static" != xyes], [AC_MSG_ERROR(--enable-static must be used to bundle Python plugin support in the babeltrace executable)]) - AS_IF([test "x$enable_shared" = xyes], [AC_MSG_ERROR(--disable-shared must be used to bundle Python plugin support in the babeltrace executable)]) + AS_IF([test "x$enable_static" != xyes], [AC_MSG_ERROR(--enable-static must be used to bundle Python plugin support in the babeltrace2 executable)]) + AS_IF([test "x$enable_shared" = xyes], [AC_MSG_ERROR(--disable-shared must be used to bundle Python plugin support in the babeltrace2 executable)]) ] ) @@ -715,8 +715,8 @@ target. CFLAGS=${save_CFLAGS} # Abuse autoconf's AC_ARG_PROGRAM output variable 'program_transform_name' -# to rename babeltrace.bin to babeltrace at install time. -program_transform_name="s&babeltrace\.bin&babeltrace&;s&babeltrace-log\.bin&babeltrace-log&;$program_transform_name" +# to rename babeltrace2.bin to babeltrace2 at install time. +program_transform_name="s&babeltrace2\.bin&babeltrace2&;s&babeltrace2-log\.bin&babeltrace2-log&;$program_transform_name" AC_SUBST(program_transform_name) AC_CONFIG_FILES([ diff --git a/tests/cli/test_trace_read.in b/tests/cli/test_trace_read.in index 464dffa0..e9857eef 100644 --- a/tests/cli/test_trace_read.in +++ b/tests/cli/test_trace_read.in @@ -27,15 +27,15 @@ plan_tests $NUM_TESTS for path in "${SUCCESS_TRACES[@]}"; do trace=$(basename "${path}") "${BT_BIN}" "${path}" > /dev/null 2>&1 - ok $? "Run babeltrace with trace ${trace}" + ok $? "Run babeltrace2 with trace ${trace}" done for path in "${FAIL_TRACES[@]}"; do trace=$(basename "${path}") "${BT_BIN}" "${path}" > /dev/null 2>&1 if [ $? -eq 0 ]; then - fail "Run babeltrace with invalid trace ${trace}" + fail "Run babeltrace2 with invalid trace ${trace}" else - pass "Run babeltrace with invalid trace ${trace}" + pass "Run babeltrace2 with invalid trace ${trace}" fi done diff --git a/tests/utils/common.sh.in b/tests/utils/common.sh.in index 08243f5f..13de3679 100644 --- a/tests/utils/common.sh.in +++ b/tests/utils/common.sh.in @@ -3,7 +3,7 @@ BT_SRC_PATH="@abs_top_srcdir@" BT_BUILD_PATH="@abs_top_builddir@" -BT_BIN="${BT_BUILD_PATH}/cli/babeltrace@EXEEXT@" +BT_BIN="${BT_BUILD_PATH}/cli/babeltrace2@EXEEXT@" BT_CTF_TRACES="${BT_SRC_PATH}/tests/ctf-traces" if [ "x${NO_SH_TAP}" = x ]; then