From: Philippe Proulx Date: Mon, 3 Apr 2017 18:10:34 +0000 (-0400) Subject: Rename converter/ -> cli/ (it's more than a converter now) X-Git-Tag: v2.0.0-pre1~419 X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=commitdiff_plain;h=8d77550c9ec0f0bdb483a672a676e842510bb384 Rename converter/ -> cli/ (it's more than a converter now) Signed-off-by: Philippe Proulx Signed-off-by: Jérémie Galarneau --- diff --git a/.gitignore b/.gitignore index b268cc4d..d4b3b448 100644 --- a/.gitignore +++ b/.gitignore @@ -37,8 +37,8 @@ plugins/ctf/common/metadata/lexer.c plugins/ctf/common/metadata/parser.c plugins/ctf/common/metadata/parser.h plugins/ctf/common/metadata/parser.output -/converter/babeltrace -/converter/babeltrace.bin +/cli/babeltrace +/cli/babeltrace.bin /config.h /config.h.in /config.status @@ -55,7 +55,7 @@ Makefile Makefile.in autom4te.cache/ config/ -converter/babeltrace-log +cli/babeltrace-log core stamp-h1 bindings/python/__init__.py diff --git a/Makefile.am b/Makefile.am index a7fecfea..6e90e481 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,7 +14,7 @@ endif SUBDIRS += \ lib \ plugins \ - converter \ + cli \ bindings \ tests \ doc \ diff --git a/cli/Makefile.am b/cli/Makefile.am new file mode 100644 index 00000000..c1fce34e --- /dev/null +++ b/cli/Makefile.am @@ -0,0 +1,44 @@ +PLUGINS_PATH = $(abs_top_builddir)/plugins +AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include \ + -DCONFIG_IN_TREE_PLUGIN_PATH=\"$(PLUGINS_PATH)/ctf:$(PLUGINS_PATH)/text:$(PLUGINS_PATH)/muxer:$(PLUGINS_PATH)/writer:$(PLUGINS_PATH)/utils\" +AM_LDFLAGS = -lpopt + +bin_PROGRAMS = babeltrace.bin +noinst_PROGRAMS = babeltrace +#check_PROGRAMS = babeltrace + +babeltrace_bin_SOURCES = \ + babeltrace.c \ + babeltrace-cfg.c \ + babeltrace-cfg.h \ + default-cfg.h \ + default-cfg.c \ + babeltrace-cfg-connect.h \ + babeltrace-cfg-connect.c + +# -Wl,--no-as-needed is needed for recent gold linker who seems to think +# it knows better and considers libraries with constructors having +# side-effects as dead code. +babeltrace_bin_LDFLAGS = -Wl, $(LD_NO_AS_NEEDED), -export-dynamic +babeltrace_bin_LDADD = \ + $(top_builddir)/lib/libbabeltrace.la \ + $(top_builddir)/compat/libcompat.la \ + $(top_builddir)/common/libbabeltrace-common.la + +if ENABLE_DEBUG_INFO +babeltrace_bin_LDADD += $(top_builddir)/lib/libdebug-info.la +endif + +if BUILT_IN_PLUGINS +babeltrace_bin_LDFLAGS += -Wl,--whole-archive,$(top_builddir)/plugins/ctf/.libs/libbabeltrace-plugin-ctf.a,$(top_builddir)/plugins/text/.libs/libbabeltrace-plugin-ctf-text.a,$(top_builddir)/plugins/muxer/.libs/libbabeltrace-plugin-muxer.a,$(top_builddir)/plugins/writer/.libs/libbabeltrace-plugin-ctf-writer.a,--no-whole-archive +endif + +if BABELTRACE_BUILD_WITH_MINGW +babeltrace_bin_LDADD += -lrpcrt4 -lintl -liconv -lole32 -lpopt -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 diff --git a/cli/babeltrace-cfg-connect.c b/cli/babeltrace-cfg-connect.c new file mode 100644 index 00000000..e1de064d --- /dev/null +++ b/cli/babeltrace-cfg-connect.c @@ -0,0 +1,680 @@ +/* + * 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 "babeltrace-cfg.h" +#include "babeltrace-cfg-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); +} + +void bt_config_connection_destroy(struct bt_config_connection *connection) +{ + if (!connection) { + return; + } + + if (connection->src_instance_name) { + g_string_free(connection->src_instance_name, TRUE); + } + + if (connection->dst_instance_name) { + g_string_free(connection->dst_instance_name, TRUE); + } + + if (connection->src_port_name) { + g_string_free(connection->src_port_name, TRUE); + } + + if (connection->dst_port_name) { + g_string_free(connection->dst_port_name, TRUE); + } + + if (connection->arg) { + g_string_free(connection->arg, TRUE); + } + + g_free(connection); +} + +static struct bt_config_connection *bt_config_connection_create(const char *arg) +{ + struct bt_config_connection *cfg_connection; + + cfg_connection = g_new0(struct bt_config_connection, 1); + if (!cfg_connection) { + goto error; + } + + cfg_connection->src_instance_name = g_string_new(NULL); + if (!cfg_connection->src_instance_name) { + goto error; + } + + cfg_connection->dst_instance_name = g_string_new(NULL); + if (!cfg_connection->dst_instance_name) { + goto error; + } + + cfg_connection->src_port_name = g_string_new(NULL); + if (!cfg_connection->src_port_name) { + goto error; + } + + cfg_connection->dst_port_name = g_string_new(NULL); + if (!cfg_connection->dst_port_name) { + goto error; + } + + cfg_connection->arg = g_string_new(arg); + if (!cfg_connection->arg) { + goto error; + } + + goto end; + +error: + g_free(cfg_connection); + cfg_connection = NULL; + +end: + return cfg_connection; +} + +static struct bt_config_connection *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 { + SRC_NAME, + DST_NAME, + SRC_PORT_NAME, + DST_PORT_NAME, + } state = SRC_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 SRC_NAME: + gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + g_string_free(cfg_conn->src_instance_name, TRUE); + cfg_conn->src_instance_name = gs; + gs = NULL; + + if (at[end_pos] == ':') { + state = DST_NAME; + } else if (at[end_pos] == '.') { + state = SRC_PORT_NAME; + } else { + goto error; + } + + at += end_pos + 1; + break; + case DST_NAME: + gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + g_string_free(cfg_conn->dst_instance_name, TRUE); + cfg_conn->dst_instance_name = gs; + gs = NULL; + + if (at[end_pos] == '.') { + state = DST_PORT_NAME; + } else if (at[end_pos] == '\0') { + goto end; + } else { + goto error; + } + + at += end_pos + 1; + break; + case SRC_PORT_NAME: + gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + g_string_free(cfg_conn->src_port_name, TRUE); + cfg_conn->src_port_name = gs; + gs = NULL; + + if (at[end_pos] == ':') { + state = DST_NAME; + } else { + goto error; + } + + at += end_pos + 1; + break; + case DST_PORT_NAME: + gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); + if (!gs || gs->len == 0) { + goto error; + } + + g_string_free(cfg_conn->dst_port_name, TRUE); + cfg_conn->dst_port_name = gs; + gs = NULL; + + if (at[end_pos] == '\0') { + goto end; + } else { + goto error; + } + break; + default: + assert(false); + } + } + +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 = bt_get(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->src_instance_name->str); + bt_put(comp); + if (!comp) { + snprintf(error_buf, error_buf_size, + "Invalid connection: cannot find upstream component `%s`:\n %s\n", + connection->src_instance_name->str, + connection->arg->str); + ret = -1; + goto end; + } + + comp = find_component(cfg, connection->dst_instance_name->str); + bt_put(comp); + if (!comp) { + snprintf(error_buf, error_buf_size, + "Invalid connection: cannot find downstream component `%s`:\n %s\n", + connection->dst_instance_name->str, + connection->arg->str); + ret = -1; + goto end; + } + } + +end: + return ret; +} + +static int validate_connection_directions(struct bt_config *cfg, + char *error_buf, size_t error_buf_size) +{ + size_t i; + int ret = 0; + struct bt_config_component *src_comp = NULL; + struct bt_config_component *dst_comp = NULL; + + for (i = 0; i < cfg->cmd_data.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->src_instance_name->str); + assert(src_comp); + dst_comp = find_component(cfg, + connection->dst_instance_name->str); + 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->src_instance_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->src_instance_name->str, + connection->arg->str); + ret = -1; + goto end; + } + } else { + snprintf(error_buf, error_buf_size, + "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n", + connection->src_instance_name->str, + connection->dst_instance_name->str, + connection->arg->str); + ret = -1; + goto end; + } + + BT_PUT(src_comp); + BT_PUT(dst_comp); + } + +end: + bt_put(src_comp); + bt_put(dst_comp); + return ret; +} + +static int validate_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; + + 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->src_instance_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->dst_instance_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->dst_instance_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->src_instance_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, + struct bt_value *connected_components, + char *error_buf, size_t error_buf_size) +{ + int ret = 0; + size_t i; + + for (i = 0; i < comps->len; i++) { + struct bt_config_component *comp = g_ptr_array_index(comps, i); + + if (!bt_value_map_has_key(connected_components, + comp->instance_name->str)) { + snprintf(error_buf, error_buf_size, + "Component `%s` is not connected\n", + comp->instance_name->str); + ret = -1; + goto end; + } + } + +end: + return ret; +} + +static int validate_all_components_connected(struct bt_config *cfg, + char *error_buf, size_t error_buf_size) +{ + size_t i; + int ret = 0; + struct bt_value *connected_components = bt_value_map_create(); + + if (!connected_components) { + ret = -1; + goto end; + } + + for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { + struct bt_config_connection *connection = + g_ptr_array_index(cfg->cmd_data.run.connections, i); + + ret = bt_value_map_insert(connected_components, + connection->src_instance_name->str, bt_value_null); + if (ret) { + goto end; + } + + ret = bt_value_map_insert(connected_components, + connection->dst_instance_name->str, bt_value_null); + if (ret) { + goto end; + } + } + + ret = validate_all_components_connected_in_array( + cfg->cmd_data.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_put(connected_components); + return ret; +} + +static int validate_no_duplicate_connection(struct bt_config *cfg, + char *error_buf, size_t error_buf_size) +{ + size_t i; + int ret = 0; + struct bt_value *flat_connection_names = bt_value_map_create(); + GString *flat_connection_name = NULL; + + if (!flat_connection_names) { + ret = -1; + goto end; + } + + flat_connection_name = g_string_new(NULL); + if (!flat_connection_name) { + ret = -1; + goto end; + } + + for (i = 0; i < cfg->cmd_data.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->src_instance_name->str, + connection->src_port_name->str, + connection->dst_instance_name->str, + connection->dst_port_name->str); + + if (bt_value_map_has_key(flat_connection_names, + flat_connection_name->str)) { + snprintf(error_buf, error_buf_size, + "Duplicate connection:\n %s\n", + connection->arg->str); + ret = -1; + goto end; + } + + ret = bt_value_map_insert(flat_connection_names, + flat_connection_name->str, bt_value_null); + if (ret) { + goto end; + } + } + +end: + bt_put(flat_connection_names); + + if (flat_connection_name) { + g_string_free(flat_connection_name, TRUE); + } + + return ret; +} + +static int validate_connections(struct bt_config *cfg, char *error_buf, + size_t error_buf_size) +{ + int ret; + + ret = validate_all_endpoints_exist(cfg, error_buf, error_buf_size); + if (ret) { + goto end; + } + + ret = validate_connection_directions(cfg, error_buf, error_buf_size); + if (ret) { + goto end; + } + + ret = validate_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_create_connections(struct bt_config *cfg, + struct bt_value *connection_args, + char *error_buf, size_t error_buf_size) +{ + int ret; + size_t i; + + if (!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_size(connection_args); i++) { + struct bt_value *arg_value = + bt_value_array_get(connection_args, i); + const char *arg; + struct bt_config_connection *cfg_connection; + + ret = bt_value_string_get(arg_value, &arg); + BT_PUT(arg_value); + assert(ret == 0); + cfg_connection = cfg_connection_from_arg(arg); + if (!cfg_connection) { + snprintf(error_buf, error_buf_size, "Cannot parse --connect option's argument:\n %s\n", + arg); + goto error; + } + + g_ptr_array_add(cfg->cmd_data.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-connect.h b/cli/babeltrace-cfg-connect.h new file mode 100644 index 00000000..98f9d041 --- /dev/null +++ b/cli/babeltrace-cfg-connect.h @@ -0,0 +1,46 @@ +#ifndef BABELTRACE_CONNECT_H +#define BABELTRACE_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" + +struct bt_config_connection { + GString *src_instance_name; + GString *dst_instance_name; + GString *src_port_name; + GString *dst_port_name; + GString *arg; +}; + +int bt_config_create_connections(struct bt_config *cfg, + struct bt_value *connection_args, + char *error_buf, size_t error_buf_size); + +void bt_config_connection_destroy(struct bt_config_connection *connection); + +#endif /* BABELTRACE_CONNECT_H */ diff --git a/cli/babeltrace-cfg.c b/cli/babeltrace-cfg.c new file mode 100644 index 00000000..bf36be9b --- /dev/null +++ b/cli/babeltrace-cfg.c @@ -0,0 +1,4727 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "babeltrace-cfg.h" +#include "babeltrace-cfg-connect.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 negative number value */ + INI_EXPECT_VALUE_NUMBER_NEG, + + /* 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) */ + struct 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; + struct bt_value *names; + struct 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"); +} + +static +int ini_handle_state(struct ini_parsing_state *state) +{ + int ret = 0; + GTokenType token_type; + struct 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: + case INI_EXPECT_VALUE_NUMBER_NEG: + 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_key(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: + { + switch (token_type) { + case G_TOKEN_CHAR: + if (state->scanner->value.v_char == '-') { + /* Negative number */ + state->expecting = + INI_EXPECT_VALUE_NUMBER_NEG; + goto success; + } else { + ini_append_error_expecting(state, + state->scanner, "value"); + goto error; + } + break; + case G_TOKEN_INT: + { + /* Positive integer */ + uint64_t int_val = state->scanner->value.v_int64; + + if (int_val > (1ULL << 63) - 1) { + g_string_append_printf(state->ini_error, + "Integer value %" PRIu64 " is outside the range of a 64-bit signed integer\n", + int_val); + goto error; + } + + value = bt_value_integer_create_init( + (int64_t) int_val); + break; + } + case G_TOKEN_FLOAT: + /* Positive floating point number */ + value = bt_value_float_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 */ + break; + } + + if (!value) { + ini_append_error_expecting(state, + state->scanner, "value"); + goto error; + } + + state->expecting = INI_EXPECT_COMMA; + goto success; + } + case INI_EXPECT_VALUE_NUMBER_NEG: + { + switch (token_type) { + case G_TOKEN_INT: + { + /* Negative integer */ + uint64_t int_val = state->scanner->value.v_int64; + + if (int_val > (1ULL << 63) - 1) { + g_string_append_printf(state->ini_error, + "Integer value -%" PRIu64 " is outside the range of a 64-bit signed integer\n", + int_val); + goto error; + } + + value = bt_value_integer_create_init( + -((int64_t) int_val)); + break; + } + case G_TOKEN_FLOAT: + /* Negative floating point number */ + value = bt_value_float_create_init( + -state->scanner->value.v_float); + break; + default: + /* Unset value variable will trigger the error */ + break; + } + + if (!value) { + ini_append_error_expecting(state, + state->scanner, "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: + assert(false); + } + +error: + ret = -1; + goto end; + +success: + if (value) { + if (bt_value_map_insert(state->params, + state->last_map_key, value)) { + /* Only override return value on error */ + ret = -1; + } + } + +end: + BT_PUT(value); + return ret; +} + +/* + * Converts an INI-style argument to an equivalent map value object. + * + * Return value is owned by the caller. + */ +static +struct 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_PUT(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 +struct bt_value *bt_value_from_arg(const char *arg) +{ + struct 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 and component class names, and the instance name, + * from a command-line source/filter/sink option's argument. arg must + * have the following format: + * + * [NAME:]PLUGIN.CLS + * + * where NAME is the optional component name, PLUGIN is the plugin name, + * and CLS is the component class name. + * + * On success, both *plugin and *component are not NULL. *plugin + * and *component are owned by the caller. On success, *name can be NULL + * if no component name was found. + */ +static +void plugin_comp_cls_names(const char *arg, char **name, char **plugin, + char **comp_cls) +{ + const char *at = arg; + GString *gs_name = NULL; + GString *gs_plugin = NULL; + GString *gs_comp_cls = NULL; + size_t end_pos; + + assert(arg); + assert(plugin); + assert(comp_cls); + + 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 plugin name */ + gs_plugin = bt_common_string_until(at, ".:\\", ".", &end_pos); + if (!gs_plugin || gs_plugin->len == 0 || at[end_pos] == '\0') { + 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) { + 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 (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 (name) { + *name = NULL; + } + + *plugin = NULL; + *comp_cls = NULL; + +end: + return; +} + +/* + * Prints the Babeltrace version. + */ +static +void print_version(void) +{ + puts("Babeltrace " VERSION); +} + +/* + * Destroys a component configuration. + */ +static +void bt_config_component_destroy(struct 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_PUT(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( + enum 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(cfg_component, 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_PUT(cfg_component); + +end: + return cfg_component; +} + +/* + * Creates a component configuration from a command-line source/sink + * option's argument. + */ +struct bt_config_component *bt_config_component_from_arg( + enum bt_component_class_type type, const char *arg) +{ + struct bt_config_component *cfg_comp = NULL; + char *name = NULL; + char *plugin_name = NULL; + char *comp_cls_name = NULL; + + plugin_comp_cls_names(arg, &name, &plugin_name, &comp_cls_name); + if (!plugin_name || !comp_cls_name) { + printf_err("Cannot get plugin or component class name\n"); + 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_PUT(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(struct bt_object *obj) +{ + struct bt_config *cfg = + container_of(obj, struct bt_config, base); + + if (!obj) { + goto end; + } + + BT_PUT(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_PUT(cfg->cmd_data.help.cfg_component); + break; + case BT_CONFIG_COMMAND_QUERY: + BT_PUT(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); + } + 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); + } + break; + default: + assert(false); + } + + g_free(cfg); + +end: + return; +} + +static void destroy_gstring(void *data) +{ + g_string_free(data, TRUE); +} + +static void destroy_glist_of_gstring(GList *list) +{ + if (!list) { + return; + } + + GList *at; + + for (at = list; at != NULL; at = g_list_next(at)) { + g_string_free(at->data, TRUE); + } + + g_list_free(list); +} + +/* + * Extracts the various paths from the string arg, delimited by ':', + * and appends them to the array value object plugin_paths. + */ +enum bt_value_status bt_config_append_plugin_paths( + struct bt_value *plugin_paths, const char *arg) +{ + enum bt_value_status status = BT_VALUE_STATUS_OK; + GPtrArray *dirs = g_ptr_array_new_with_free_func(destroy_gstring); + int ret; + size_t i; + + if (!dirs) { + status = BT_VALUE_STATUS_ERROR; + goto end; + } + + ret = bt_common_append_plugin_path_dirs(arg, dirs); + if (ret) { + status = BT_VALUE_STATUS_ERROR; + goto end; + } + + for (i = 0; i < dirs->len; i++) { + GString *dir = g_ptr_array_index(dirs, i); + + bt_value_array_append_string(plugin_paths, dir->str); + } + +end: + g_ptr_array_free(dirs, TRUE); + return status; +} + +/* + * 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 +struct bt_value *names_from_arg(const char *arg) +{ + GScanner *scanner = NULL; + struct 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(names, + "payload")) { + goto error; + } + } else if (!strcmp(identifier, "context") || + !strcmp(identifier, "ctx")) { + found_item = true; + if (bt_value_array_append_string(names, + "context")) { + goto error; + } + } else if (!strcmp(identifier, "scope") || + !strcmp(identifier, "header")) { + found_item = true; + if (bt_value_array_append_string(names, + identifier)) { + goto error; + } + } else if (!strcmp(identifier, "all")) { + found_all = true; + if (bt_value_array_append_string(names, + identifier)) { + goto error; + } + } else if (!strcmp(identifier, "none")) { + found_none = true; + if (bt_value_array_append_string(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(names, "none")) { + goto error; + } + } + if (scanner) { + g_scanner_destroy(scanner); + } + return names; + +error: + BT_PUT(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 +struct bt_value *fields_from_arg(const char *arg) +{ + GScanner *scanner = NULL; + struct 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(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_PUT(fields); + +end: + if (scanner) { + g_scanner_destroy(scanner); + } + return fields; +} + +static +void append_param_arg(GString *params_arg, const char *key, const char *value) +{ + assert(params_arg); + assert(key); + 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, + struct bt_value *names_array, const char *prefix) +{ + int ret = 0; + int i; + GString *tmpstr = NULL, *default_value = NULL; + + /* + * 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_size(names_array); i++) { + struct bt_value *str_obj = bt_value_array_get(names_array, i); + const char *suffix; + bool is_default = false; + + if (!str_obj) { + printf_err("Unexpected error\n"); + ret = -1; + goto end; + } + + ret = bt_value_string_get(str_obj, &suffix); + BT_PUT(str_obj); + if (ret) { + printf_err("Unexpected error\n"); + goto end; + } + + 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) { + g_string_append(tmpstr, "default"); + append_param_arg(params_arg, tmpstr->str, + default_value->str); + } else { + g_string_append(tmpstr, suffix); + append_param_arg(params_arg, tmpstr->str, "yes"); + } + } + +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_CONNECT, + OPT_DEBUG, + OPT_DEBUG_INFO_DIR, + OPT_DEBUG_INFO_FULL_PATH, + OPT_DEBUG_INFO_TARGET_PREFIX, + OPT_END, + OPT_FIELDS, + OPT_FILTER, + OPT_HELP, + OPT_INPUT_FORMAT, + OPT_KEY, + OPT_LIST, + OPT_NAME, + OPT_NAMES, + OPT_NO_DEBUG_INFO, + OPT_NO_DELTA, + OPT_OMIT_HOME_PLUGIN_PATH, + OPT_OMIT_SYSTEM_PLUGIN_PATH, + OPT_OUTPUT_FORMAT, + OPT_OUTPUT, + OPT_PARAMS, + OPT_PATH, + OPT_PLUGIN_PATH, + OPT_RESET_BASE_PARAMS, + OPT_RUN_ARGS, + OPT_RUN_ARGS_0, + OPT_SINK, + OPT_SOURCE, + OPT_STREAM_INTERSECTION, + OPT_TIMERANGE, + OPT_URL, + OPT_VALUE, + OPT_VERBOSE, +}; + +enum bt_config_component_dest { + 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_get(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: + assert(false); + } +} + +static int add_run_cfg_comp_check_name(struct bt_config *cfg, + struct bt_config_component *cfg_comp, + enum bt_config_component_dest dest, + struct 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_key(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(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(struct bt_value *plugin_paths) +{ + int ret = 0; + const char *envvar; + + if (bt_common_is_setuid_setgid()) { + printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); + 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(struct 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()) { + printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); + } 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, + struct 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(cfg, bt_config_destroy); + cfg->command = command; + cfg->command_needs_plugins = needs_plugins; + + if (initial_plugin_paths) { + cfg->plugin_paths = bt_get(initial_plugin_paths); + } else { + cfg->plugin_paths = bt_value_array_create(); + if (!cfg->plugin_paths) { + print_err_oom(); + goto error; + } + } + + goto end; + +error: + BT_PUT(cfg); + +end: + return cfg; +} + +static struct bt_config *bt_config_run_create( + struct 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_put); + 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_put); + 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_put); + 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_PUT(cfg); + +end: + return cfg; +} + +static struct bt_config *bt_config_list_plugins_create( + struct 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_PUT(cfg); + +end: + return cfg; +} + +static struct bt_config *bt_config_help_create( + struct 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(BT_COMPONENT_CLASS_TYPE_UNKNOWN, + NULL, NULL); + if (!cfg->cmd_data.help.cfg_component) { + goto error; + } + + goto end; + +error: + BT_PUT(cfg); + +end: + return cfg; +} + +static struct bt_config *bt_config_query_create( + struct 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_PUT(cfg); + +end: + return cfg; +} + +static struct bt_config *bt_config_print_ctf_metadata_create( + struct 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; + } + + goto end; + +error: + BT_PUT(cfg); + +end: + return cfg; +} + +static struct bt_config *bt_config_print_lttng_live_sessions_create( + struct 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; + } + + goto end; + +error: + BT_PUT(cfg); + +end: + return cfg; +} + +int bt_config_append_plugin_paths_check_setuid_setgid( + struct bt_value *plugin_paths, const char *arg) +{ + int ret = 0; + + if (bt_common_is_setuid_setgid()) { + printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); + 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) 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, "\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, " observe=3.14, simple=beef, needs-quotes=\"some string\",\n"); + fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\"\n"); + fprintf(fp, "\n"); + fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run babeltrace\n"); + fprintf(fp, "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] --source=PLUGIN.CLS\n"); + fprintf(fp, " babeltrace [GENERAL OPTIONS] help [OPTIONS] --filter=PLUGIN.CLS\n"); + fprintf(fp, " babeltrace [GENERAL OPTIONS] help [OPTIONS] --sink=PLUGIN.CLS\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -f, --filter=PLUGIN.CLS Get help for the filter component class\n"); + fprintf(fp, " CLS found in the plugin PLUGIN\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, " -S, --sink=PLUGIN.CLS Get help for the sink component class\n"); + fprintf(fp, " CLS found in the plugin PLUGIN\n"); + fprintf(fp, " -s, --source=PLUGIN.CLS Get help for the source component class\n"); + fprintf(fp, " CLS found in the plugin PLUGIN\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 */ + { "filter", 'f', POPT_ARG_STRING, NULL, OPT_FILTER, NULL, NULL }, + { "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 }, + { "sink", 'S', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, + { "source", 's', POPT_ARG_STRING, NULL, OPT_SOURCE, 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 omit_system_plugin_path, + bool omit_home_plugin_path, + struct 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; + char *plug_comp_cls_names = NULL; + + *retcode = 0; + cfg = bt_config_help_create(initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->omit_system_plugin_path = omit_system_plugin_path; + cfg->omit_home_plugin_path = 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_SOURCE: + case OPT_FILTER: + case OPT_SINK: + if (cfg->cmd_data.help.cfg_component->type != + BT_COMPONENT_CLASS_TYPE_UNKNOWN) { + printf_err("Cannot specify more than one plugin and component class:\n %s\n", + arg); + goto error; + } + + switch (opt) { + case OPT_SOURCE: + cfg->cmd_data.help.cfg_component->type = + BT_COMPONENT_CLASS_TYPE_SOURCE; + break; + case OPT_FILTER: + cfg->cmd_data.help.cfg_component->type = + BT_COMPONENT_CLASS_TYPE_FILTER; + break; + case OPT_SINK: + cfg->cmd_data.help.cfg_component->type = + BT_COMPONENT_CLASS_TYPE_SINK; + break; + default: + assert(false); + } + plug_comp_cls_names = strdup(arg); + if (!plug_comp_cls_names) { + print_err_oom(); + goto error; + } + break; + case OPT_HELP: + print_help_usage(stdout); + *retcode = -1; + BT_PUT(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) { + if (cfg->cmd_data.help.cfg_component->type != + BT_COMPONENT_CLASS_TYPE_UNKNOWN) { + printf_err("Cannot specify plugin name and --source/--filter/--sink component class:\n %s\n", + leftover); + goto error; + } + + g_string_assign(cfg->cmd_data.help.cfg_component->plugin_name, + leftover); + } else { + if (cfg->cmd_data.help.cfg_component->type == + BT_COMPONENT_CLASS_TYPE_UNKNOWN) { + print_help_usage(stdout); + *retcode = -1; + BT_PUT(cfg); + goto end; + } + + plugin_comp_cls_names(plug_comp_cls_names, NULL, + &plugin_name, &comp_cls_name); + if (plugin_name && comp_cls_name) { + 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 { + printf_err("Invalid --source/--filter/--sink option's argument:\n %s\n", + plug_comp_cls_names); + goto error; + } + } + + if (append_home_and_system_plugin_paths_cfg(cfg)) { + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_PUT(cfg); + +end: + free(plug_comp_cls_names); + 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] OBJECT --source=PLUGIN.CLS\n"); + fprintf(fp, " babeltrace [GEN OPTS] query [OPTS] OBJECT --filter=PLUGIN.CLS\n"); + fprintf(fp, " babeltrace [GEN OPTS] query [OPTS] OBJECT --sink=PLUGIN.CLS\n"); + fprintf(fp, "\n"); + fprintf(fp, "Options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -f. --filter=PLUGIN.CLS Query object from the filter component\n"); + fprintf(fp, " class CLS found in the plugin PLUGIN\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, " -S, --sink=PLUGIN.CLS Query object from the sink component class\n"); + fprintf(fp, " CLS found in the plugin PLUGIN\n"); + fprintf(fp, " -s, --source=PLUGIN.CLS Query object from the source component\n"); + fprintf(fp, " class CLS found in the plugin PLUGIN\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 */ + { "filter", 'f', POPT_ARG_STRING, NULL, OPT_FILTER, NULL, NULL }, + { "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 }, + { "sink", 'S', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, + { "source", 's', POPT_ARG_STRING, NULL, OPT_SOURCE, 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 omit_system_plugin_path, + bool omit_home_plugin_path, + struct bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + int opt; + int ret; + struct bt_config *cfg = NULL; + const char *leftover; + struct bt_value *params = bt_value_null; + + *retcode = 0; + cfg = bt_config_query_create(initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->omit_system_plugin_path = + omit_system_plugin_path; + cfg->omit_home_plugin_path = 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_SOURCE: + case OPT_FILTER: + case OPT_SINK: + { + enum bt_component_class_type type; + + if (cfg->cmd_data.query.cfg_component) { + printf_err("Cannot specify more than one plugin and component class:\n %s\n", + arg); + goto error; + } + + switch (opt) { + case OPT_SOURCE: + type = BT_COMPONENT_CLASS_TYPE_SOURCE; + break; + case OPT_FILTER: + type = BT_COMPONENT_CLASS_TYPE_FILTER; + break; + case OPT_SINK: + type = BT_COMPONENT_CLASS_TYPE_SINK; + break; + default: + assert(false); + } + + cfg->cmd_data.query.cfg_component = + bt_config_component_from_arg(type, arg); + if (!cfg->cmd_data.query.cfg_component) { + printf_err("Invalid format for --source/--filter/--sink option's argument:\n %s\n", + arg); + goto error; + } + + /* Default parameters: null */ + bt_put(cfg->cmd_data.query.cfg_component->params); + cfg->cmd_data.query.cfg_component->params = + bt_value_null; + break; + } + case OPT_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_PUT(cfg); + goto end; + default: + printf_err("Unknown command-line option specified (option code %d)\n", + opt); + goto error; + } + + free(arg); + arg = NULL; + } + + if (!cfg->cmd_data.query.cfg_component) { + printf_err("No target component class specified with --source/--filter/--sink option\n"); + goto error; + } + + assert(params); + BT_MOVE(cfg->cmd_data.query.cfg_component->params, params); + + /* 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 one leftover argument which is the + * mandatory object. + */ + 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_PUT(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_PUT(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + BT_PUT(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 omit_system_plugin_path, + bool omit_home_plugin_path, + struct 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 = omit_system_plugin_path; + cfg->omit_home_plugin_path = 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_PUT(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_PUT(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, --connect=CONNECTION Connect two created components (see the\n"); + fprintf(fp, " expected format of CONNECTION below)\n"); + fprintf(fp, " -f, --filter=[NAME:]PLUGIN.CLS Instantiate a filter component from plugin\n"); + fprintf(fp, " PLUGIN and component class CLS, set it as\n"); + fprintf(fp, " the current component, and if NAME is\n"); + fprintf(fp, " given, set its instance name to NAME\n"); + fprintf(fp, " --key=KEY Set the current initialization string\n"); + fprintf(fp, " parameter key to KEY (see --value)\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, " -S, --sink=[NAME:]PLUGIN.CLS Instantiate a sink component from plugin\n"); + fprintf(fp, " PLUGIN and component class CLS, set it as\n"); + fprintf(fp, " the current component, and if NAME is\n"); + fprintf(fp, " given, set its instance name to NAME\n"); + fprintf(fp, " -s, --source=[NAME:]PLUGIN.CLS Instantiate a source component from plugin\n"); + fprintf(fp, " PLUGIN and component class CLS, set it as\n"); + fprintf(fp, " the current component, and if NAME is\n"); + fprintf(fp, " given, set its instance name to NAME\n"); + fprintf(fp, " --value=VAL Add a string initialization parameter to\n"); + fprintf(fp, " the current component with a name given by\n"); + fprintf(fp, " the last argument of the --key option and a\n"); + fprintf(fp, " value set to VAL\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 the names of the upstream and downstream components\n"); + fprintf(fp, "to connect together. You can set the name of the current component with the\n"); + fprintf(fp, "--name option.\n"); + fprintf(fp, "\n"); + fprintf(fp, "UPSTREAM-PORT and DOWNSTREAM-PORT are the optional upstream and downstream\n"); + fprintf(fp, "ports to use for the connection. When the port is not specified, the default\n"); + fprintf(fp, "port is used.\n"); + fprintf(fp, "\n"); + fprintf(fp, "You can connect a source component to a filter or sink component. You\n"); + fprintf(fp, "can connect a filter component to a sink component.\n"); + fprintf(fp, "\n"); + fprintf(fp, "Example:\n"); + fprintf(fp, "\n"); + fprintf(fp, " my-filter.top10:json-out\n"); + fprintf(fp, "\n\n"); + print_expected_params_format(fp); +} + +static struct poptOption run_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL }, + { "connect", 'c', POPT_ARG_STRING, NULL, OPT_CONNECT, NULL, NULL }, + { "filter", 'f', POPT_ARG_STRING, NULL, OPT_FILTER, NULL, NULL }, + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "key", '\0', POPT_ARG_STRING, NULL, OPT_KEY, 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 }, + { "sink", 'S', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, + { "source", 's', POPT_ARG_STRING, NULL, OPT_SOURCE, NULL, NULL }, + { "value", '\0', POPT_ARG_STRING, NULL, OPT_VALUE, NULL, NULL }, + { NULL, 0, 0, NULL, 0, NULL, NULL }, +}; + +/* + * 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 omit_system_plugin_path, + bool omit_home_plugin_path, + struct 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; + struct bt_value *cur_base_params = NULL; + int opt, ret = 0; + struct bt_config *cfg = NULL; + struct bt_value *instance_names = NULL; + struct bt_value *connection_args = NULL; + GString *cur_param_key = NULL; + char error_buf[256] = { 0 }; + + *retcode = 0; + cur_param_key = g_string_new(NULL); + if (!cur_param_key) { + print_err_oom(); + goto error; + } + + if (argc <= 1) { + print_run_usage(stdout); + *retcode = -1; + goto end; + } + + cfg = bt_config_run_create(initial_plugin_paths); + if (!cfg) { + goto error; + } + + cfg->omit_system_plugin_path = omit_system_plugin_path; + cfg->omit_home_plugin_path = 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_SOURCE: + case OPT_FILTER: + case OPT_SINK: + { + enum bt_component_class_type new_comp_type; + enum bt_config_component_dest new_dest; + const char *opt_name; + + switch (opt) { + case OPT_SOURCE: + new_comp_type = BT_COMPONENT_CLASS_TYPE_SOURCE; + new_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; + opt_name = "--source"; + break; + case OPT_FILTER: + new_comp_type = BT_COMPONENT_CLASS_TYPE_FILTER; + new_dest = BT_CONFIG_COMPONENT_DEST_FILTER; + opt_name = "--filter"; + break; + case OPT_SINK: + new_comp_type = BT_COMPONENT_CLASS_TYPE_SINK; + new_dest = BT_CONFIG_COMPONENT_DEST_SINK; + opt_name = "--sink"; + break; + default: + assert(false); + } + + if (cur_cfg_comp) { + ret = add_run_cfg_comp_check_name(cfg, + cur_cfg_comp, cur_cfg_comp_dest, + instance_names); + BT_PUT(cur_cfg_comp); + if (ret) { + goto error; + } + } + + cur_cfg_comp = bt_config_component_from_arg( + new_comp_type, arg); + if (!cur_cfg_comp) { + printf_err("Invalid format for %s option's argument:\n %s\n", + opt_name, arg); + goto error; + } + + assert(cur_base_params); + bt_put(cur_cfg_comp->params); + cur_cfg_comp->params = bt_value_copy(cur_base_params); + if (!cur_cfg_comp->params) { + print_err_oom(); + goto error; + } + + cur_cfg_comp_dest = new_dest; + break; + } + case OPT_PARAMS: + { + struct bt_value *params; + struct 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; + } + + params_to_set = bt_value_map_extend(cur_cfg_comp->params, + params); + BT_PUT(params); + if (!params_to_set) { + printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n", + arg); + goto error; + } + + BT_MOVE(cur_cfg_comp->params, params_to_set); + break; + } + case OPT_KEY: + if (strlen(arg) == 0) { + printf_err("Cannot set an empty string as the current parameter key\n"); + goto error; + } + + g_string_assign(cur_param_key, arg); + break; + case OPT_VALUE: + if (!cur_cfg_comp) { + printf_err("Cannot set a parameter's value of unavailable component:\n %s\n", + arg); + goto error; + } + + if (cur_param_key->len == 0) { + printf_err("--value option specified without preceding --key option:\n %s\n", + arg); + goto error; + } + + if (bt_value_map_insert_string(cur_cfg_comp->params, + cur_param_key->str, arg)) { + print_err_oom(); + goto error; + } + 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: + { + struct 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_MOVE(cur_base_params, params); + break; + } + case OPT_RESET_BASE_PARAMS: + BT_PUT(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(connection_args, + arg)) { + print_err_oom(); + goto error; + } + break; + case OPT_HELP: + print_run_usage(stdout); + *retcode = -1; + BT_PUT(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_PUT(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_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_PUT(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + if (cur_param_key) { + g_string_free(cur_param_key, TRUE); + } + + free(arg); + BT_PUT(cur_cfg_comp); + BT_PUT(cur_base_params); + BT_PUT(instance_names); + BT_PUT(connection_args); + return cfg; +} + +static +struct bt_config *bt_config_run_from_args_array(struct bt_value *run_args, + int *retcode, bool omit_system_plugin_path, + bool omit_home_plugin_path, + struct bt_value *initial_plugin_paths) +{ + struct bt_config *cfg = NULL; + const char **argv; + size_t i; + const size_t argc = bt_value_array_size(run_args) + 1; + + argv = calloc(argc, sizeof(*argv)); + if (!argv) { + print_err_oom(); + goto end; + } + + argv[0] = "run"; + + for (i = 0; i < bt_value_array_size(run_args); i++) { + int ret; + struct bt_value *arg_value = bt_value_array_get(run_args, i); + const char *arg; + + assert(arg_value); + ret = bt_value_string_get(arg_value, &arg); + assert(ret == 0); + assert(arg); + argv[i + 1] = arg; + bt_put(arg_value); + } + + cfg = bt_config_run_from_args(argc, argv, retcode, + omit_system_plugin_path, 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, " --filter=[NAME:]PLUGIN.CLS Instantiate a filter component from plugin\n"); + fprintf(fp, " PLUGIN and component class CLS, set it as\n"); + fprintf(fp, " the current component, and if NAME is\n"); + fprintf(fp, " given, set its instance name to 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, " 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, " --sink=[NAME:]PLUGIN.CLS Instantiate a sink component from plugin\n"); + fprintf(fp, " PLUGIN and component class CLS, set it as\n"); + fprintf(fp, " the current component, and if NAME is\n"); + fprintf(fp, " given, set its instance name to NAME\n"); + fprintf(fp, " --source=[NAME:]PLUGIN.CLS Instantiate a source component from plugin\n"); + fprintf(fp, " PLUGIN and component class CLS, set it as\n"); + fprintf(fp, " the current component, and if NAME is\n"); + fprintf(fp, " given, set its instance name to NAME\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 `ctf.fs` source 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, " --stream-intersection Only process events when all streams\n"); + fprintf(fp, " are active\n"); + fprintf(fp, "\n"); + fprintf(fp, "Implicit `text.text` sink 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 `utils.muxer` filter 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 `utils.trimmer` filter 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 `lttng-utils.debug-info` filter component options:\n"); + fprintf(fp, "\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, " --no-debug-info Do not create an implicit\n"); + fprintf(fp, " `lttng-utils.debug-info` filter component\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 `ctf.fs` source\n"); + fprintf(fp, " component\n"); + fprintf(fp, " `lttng-live`:\n"); + fprintf(fp, " Create an implicit `ctf.lttng-live`\n"); + fprintf(fp, " source component\n"); + fprintf(fp, " -o, --output-format=(text | dummy | ctf-metadata)\n"); + fprintf(fp, " `text`:\n"); + fprintf(fp, " Create an implicit `text.text` sink\n"); + fprintf(fp, " component\n"); + fprintf(fp, " `dummy`:\n"); + fprintf(fp, " Create an implicit `utils.dummy` sink\n"); + fprintf(fp, " component\n"); + fprintf(fp, " `ctf-metadata`:\n"); + fprintf(fp, " Query the `ctf.fs` component class for\n"); + fprintf(fp, " 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 }, + { "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 }, + { "filter", '\0', POPT_ARG_STRING, NULL, OPT_FILTER, 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 }, + { "no-debug-info", '\0', POPT_ARG_NONE, NULL, OPT_NO_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 }, + { "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 }, + { "sink", '\0', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, + { "source", '\0', POPT_ARG_STRING, NULL, OPT_SOURCE, 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, + struct 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_key(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_key(existing_names, auto_name->str)); + +end: + return auto_name; +} + +struct implicit_component_args { + bool exists; + GString *plugin_comp_cls_arg; + GString *name_arg; + GString *params_arg; + struct bt_value *extra_args; +}; + +static +int assign_name_to_implicit_component(struct implicit_component_args *args, + const char *prefix, struct 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(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( + enum bt_component_class_type type, + struct implicit_component_args *impl_args, + struct bt_value *run_args) +{ + int ret = 0; + size_t i; + + if (!impl_args->exists) { + goto end; + } + + switch (type) { + case BT_COMPONENT_CLASS_TYPE_SOURCE: + if (bt_value_array_append_string(run_args, "--source")) { + print_err_oom(); + goto error; + } + break; + case BT_COMPONENT_CLASS_TYPE_FILTER: + if (bt_value_array_append_string(run_args, "--filter")) { + print_err_oom(); + goto error; + } + break; + case BT_COMPONENT_CLASS_TYPE_SINK: + if (bt_value_array_append_string(run_args, "--sink")) { + print_err_oom(); + goto error; + } + break; + default: + assert(false); + } + + if (bt_value_array_append_string(run_args, + impl_args->plugin_comp_cls_arg->str)) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(run_args, "--name")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(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(run_args, "--params")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(run_args, + impl_args->params_arg->str)) { + print_err_oom(); + goto error; + } + } + + for (i = 0; i < bt_value_array_size(impl_args->extra_args); i++) { + struct bt_value *elem; + const char *arg; + + elem = bt_value_array_get(impl_args->extra_args, i); + if (!elem) { + goto error; + } + + assert(bt_value_is_string(elem)); + if (bt_value_string_get(elem, &arg)) { + goto error; + } + + ret = bt_value_array_append_string(run_args, arg); + bt_put(elem); + if (ret) { + print_err_oom(); + goto error; + } + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +static +void destroy_implicit_component_args(struct implicit_component_args *args) +{ + assert(args); + + if (args->plugin_comp_cls_arg) { + g_string_free(args->plugin_comp_cls_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_put(args->extra_args); +} + +static +int init_implicit_component_args(struct implicit_component_args *args, + const char *plugin_comp_cls_arg, bool exists) +{ + int ret = 0; + + args->exists = exists; + args->plugin_comp_cls_arg = g_string_new(plugin_comp_cls_arg); + args->name_arg = g_string_new(NULL); + args->params_arg = g_string_new(NULL); + args->extra_args = bt_value_array_create(); + + if (!args->plugin_comp_cls_arg || !args->name_arg || + !args->params_arg || !args->extra_args) { + ret = -1; + destroy_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) +{ + assert(args); + assert(key); + assert(value); + append_param_arg(args->params_arg, key, value); +} + +static +int append_implicit_component_extra_arg(struct implicit_component_args *args, + const char *key, const char *value) +{ + int ret = 0; + + assert(args); + assert(key); + assert(value); + + if (bt_value_array_append_string(args->extra_args, "--key")) { + print_err_oom(); + ret = -1; + goto end; + } + + if (bt_value_array_append_string(args->extra_args, key)) { + print_err_oom(); + ret = -1; + goto end; + } + + if (bt_value_array_append_string(args->extra_args, "--value")) { + print_err_oom(); + ret = -1; + goto end; + } + + if (bt_value_array_append_string(args->extra_args, value)) { + print_err_oom(); + ret = -1; + goto end; + } + +end: + return ret; +} + +static +int convert_append_name_param(enum bt_config_component_dest dest, + GString *cur_name, GString *cur_name_prefix, + struct bt_value *run_args, struct 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 --source/--filter/--sink */ + 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_key(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(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(run_args, "--name")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(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: + assert(false); + } + + g_string_assign(cur_name_prefix, ""); + } + + goto end; + +error: + ret = -1; + +end: + return ret; +} + +/* + * Escapes `.`, `:`, and `\` of `input` with `\`. + */ +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. + */ +int append_connect_arg(struct 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(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(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. + */ +int convert_auto_connect(struct 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; + + assert(source_names); + assert(filter_names); + 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; + + 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; + } + + assert(begin); + 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); + + 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 omit_system_plugin_path, + bool omit_home_plugin_path, + struct bt_value *initial_plugin_paths) +{ + poptContext pc = NULL; + char *arg = NULL; + enum bt_config_component_dest cur_comp_dest; + int opt, ret = 0; + struct bt_config *cfg = NULL; + bool got_verbose_opt = false; + bool got_debug_opt = false; + bool got_input_format_opt = false; + bool got_output_format_opt = false; + bool trimmer_has_begin = false; + bool trimmer_has_end = 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; + struct bt_value *run_args = NULL; + struct bt_value *all_names = NULL; + GList *source_names = NULL; + GList *filter_names = NULL; + GList *sink_names = NULL; + struct implicit_component_args implicit_ctf_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 }; + struct bt_value *plugin_paths = bt_get(initial_plugin_paths); + char error_buf[256] = { 0 }; + size_t i; + struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 }; + + *retcode = 0; + + if (argc <= 1) { + print_convert_usage(stdout); + *retcode = -1; + goto end; + } + + if (init_implicit_component_args(&implicit_ctf_args, "ctf.fs", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_lttng_live_args, + "ctf.lttng-live", false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_text_args, "text.text", + false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_dummy_args, "utils.dummy", + false)) { + goto error; + } + + if (init_implicit_component_args(&implicit_debug_info_args, + "lttng-utils.debug-info", true)) { + goto error; + } + + if (init_implicit_component_args(&implicit_muxer_args, "utils.muxer", + true)) { + goto error; + } + + if (init_implicit_component_args(&implicit_trimmer_args, + "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; + } + + /* + * 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 -> --key path --value PATH + * --url=URL -> --key url --value 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_SOURCE: + case OPT_FILTER: + case OPT_SINK: + /* 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); + if (!plugin_name || !comp_cls_name) { + printf_err("Invalid format for --source/--filter/--sink option's argument:\n %s\n", + arg); + goto error; + } + + if (name) { + g_string_assign(cur_name, name); + } else { + g_string_assign(cur_name, ""); + } + + switch (opt) { + case OPT_SOURCE: + cur_comp_dest = + BT_CONFIG_COMPONENT_DEST_SOURCE; + if (bt_value_array_append_string(run_args, + "--source")) { + print_err_oom(); + goto error; + } + break; + case OPT_FILTER: + cur_comp_dest = + BT_CONFIG_COMPONENT_DEST_FILTER; + if (bt_value_array_append_string(run_args, + "--filter")) { + print_err_oom(); + goto error; + } + break; + case OPT_SINK: + cur_comp_dest = + BT_CONFIG_COMPONENT_DEST_SINK; + if (bt_value_array_append_string(run_args, + "--sink")) { + print_err_oom(); + goto error; + } + break; + default: + assert(false); + } + + if (bt_value_array_append_string(run_args, arg)) { + print_err_oom(); + goto error; + } + + g_string_assign(cur_name_prefix, ""); + g_string_append(cur_name_prefix, plugin_name); + g_string_append_c(cur_name_prefix, '.'); + g_string_append(cur_name_prefix, 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(run_args, + "--params")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(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 (bt_value_array_append_string(run_args, "--key")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(run_args, "path")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(run_args, "--value")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(run_args, arg)) { + print_err_oom(); + 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 (bt_value_array_append_string(run_args, "--key")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(run_args, "url")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(run_args, "--value")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(run_args, arg)) { + print_err_oom(); + 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(run_args, "--name")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(run_args, arg)) { + print_err_oom(); + goto error; + } + + g_string_assign(cur_name, arg); + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + if (bt_value_array_append_string(run_args, + "--omit-home-plugin-path")) { + print_err_oom(); + goto error; + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + if (bt_value_array_append_string(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(run_args, + "--plugin-path")) { + print_err_oom(); + goto error; + } + + if (bt_value_array_append_string(run_args, arg)) { + print_err_oom(); + goto error; + } + break; + case OPT_HELP: + print_convert_usage(stdout); + *retcode = -1; + BT_PUT(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_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_DEBUG_INFO: + 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_arg( + &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_arg( + &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_arg( + &implicit_trimmer_args, "begin", begin); + ret |= append_implicit_component_extra_arg( + &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, "force-correlate", "yes"); + break; + case OPT_CLOCK_GMT: + append_implicit_component_param( + &implicit_text_args, "clock-gmt", "yes"); + implicit_text_args.exists = true; + break; + case OPT_CLOCK_OFFSET: + ret = append_implicit_component_extra_arg( + &implicit_ctf_args, "clock-offset-cycles", arg); + implicit_ctf_args.exists = true; + if (ret) { + goto error; + } + break; + case OPT_CLOCK_OFFSET_NS: + ret = append_implicit_component_extra_arg( + &implicit_ctf_args, "clock-offset-ns", arg); + implicit_ctf_args.exists = true; + if (ret) { + goto error; + } + break; + case OPT_CLOCK_SECONDS: + append_implicit_component_param( + &implicit_text_args, "clock-seconds", "yes"); + implicit_text_args.exists = true; + break; + case OPT_COLOR: + ret = append_implicit_component_extra_arg( + &implicit_text_args, "color", arg); + implicit_text_args.exists = true; + if (ret) { + goto error; + } + break; + case OPT_NO_DEBUG_INFO: + implicit_debug_info_args.exists = false; + break; + case OPT_DEBUG_INFO_DIR: + ret = append_implicit_component_extra_arg( + &implicit_debug_info_args, "dir", arg); + if (ret) { + goto error; + } + break; + case OPT_DEBUG_INFO_FULL_PATH: + append_implicit_component_param( + &implicit_debug_info_args, "full-path", "yes"); + break; + case OPT_DEBUG_INFO_TARGET_PREFIX: + ret = append_implicit_component_extra_arg( + &implicit_debug_info_args, + "target-prefix", arg); + if (ret) { + goto error; + } + break; + case OPT_FIELDS: + { + struct bt_value *fields = fields_from_arg(arg); + + if (!fields) { + goto error; + } + + ret = insert_flat_params_from_array( + implicit_text_args.params_arg, + fields, "field"); + bt_put(fields); + if (ret) { + goto error; + } + break; + } + case OPT_NAMES: + { + struct bt_value *names = names_from_arg(arg); + + if (!names) { + goto error; + } + + ret = insert_flat_params_from_array( + implicit_text_args.params_arg, + names, "name"); + bt_put(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_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, "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: + ret = append_implicit_component_extra_arg( + &implicit_text_args, "path", arg); + implicit_text_args.exists = true; + if (ret) { + 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: + append_implicit_component_param(&implicit_ctf_args, + "stream-intersection", "yes"); + implicit_ctf_args.exists = true; + break; + case OPT_VERBOSE: + if (got_verbose_opt) { + printf_err("Duplicate -v/--verbose option\n"); + goto error; + } + + append_implicit_component_param(&implicit_text_args, + "verbose", "yes"); + implicit_text_args.exists = true; + got_verbose_opt = true; + break; + case OPT_DEBUG: + got_debug_opt = true; + 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; + } + + /* + * Append home and system plugin paths now that we possibly got + * --plugin-path. + */ + if (append_home_and_system_plugin_paths(plugin_paths, + omit_system_plugin_path, omit_home_plugin_path)) { + goto error; + } + + /* Consume leftover argument */ + leftover = poptGetArg(pc); + + if (poptPeekArg(pc)) { + printf_err("Unexpected argument:\n %s\n", + poptPeekArg(pc)); + goto error; + } + + /* Print CTF metadata or print LTTng-live sessions */ + if (print_ctf_metadata) { + if (!leftover) { + printf_err("--output-format=ctf-metadata specified without a path\n"); + goto error; + } + + cfg = bt_config_print_ctf_metadata_create(plugin_paths); + if (!cfg) { + goto error; + } + + cfg->debug = got_debug_opt; + cfg->verbose = got_verbose_opt; + g_string_assign(cfg->cmd_data.print_ctf_metadata.path, + leftover); + goto end; + } + + /* + * If -o dummy was not specified, and if there are no explicit + * sink components, then use an implicit `text.text` sink. + */ + if (!implicit_dummy_args.exists && !sink_names) { + implicit_text_args.exists = true; + } + + /* Decide where the leftover argument goes */ + if (leftover) { + if (implicit_lttng_live_args.exists) { + lttng_live_url_parts = + bt_common_parse_lttng_live_url(leftover, + error_buf, 256); + 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; + } + + cfg->debug = got_debug_opt; + cfg->verbose = got_verbose_opt; + g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url, + leftover); + goto end; + } + + ret = append_implicit_component_extra_arg( + &implicit_lttng_live_args, "url", leftover); + if (ret) { + goto error; + } + } else { + ret = append_implicit_component_extra_arg( + &implicit_ctf_args, "path", leftover); + implicit_ctf_args.exists = true; + if (ret) { + goto error; + } + } + } + + /* + * Ensure mutual exclusion between implicit `ctf.fs` and + * `ctf.lttng-live` sources. + */ + if (implicit_ctf_args.exists && implicit_lttng_live_args.exists) { + printf_err("Cannot create both implicit `ctf.fs` and `ctf.lttng-live` source components\n"); + goto error; + } + + /* + * If the implicit `ctf.fs` or `ctf.lttng-live` source exists, + * make sure there's a leftover (which is the path or URL). + */ + if (implicit_ctf_args.exists && !leftover) { + printf_err("Missing path for implicit `ctf.fs` source component\n"); + goto error; + } + + if (implicit_lttng_live_args.exists && !leftover) { + printf_err("Missing URL for implicit `ctf.lttng-live` source component\n"); + goto error; + } + + /* Assign names to implicit components */ + ret = assign_name_to_implicit_component(&implicit_ctf_args, + "ctf", 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, + "text", 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, + "mux", all_names, NULL, false); + if (ret) { + goto error; + } + + ret = assign_name_to_implicit_component(&implicit_trimmer_args, + "trim", 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( + BT_COMPONENT_CLASS_TYPE_SOURCE, &implicit_ctf_args, run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component( + BT_COMPONENT_CLASS_TYPE_SOURCE, &implicit_lttng_live_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component( + BT_COMPONENT_CLASS_TYPE_SINK, &implicit_text_args, run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component( + BT_COMPONENT_CLASS_TYPE_SINK, &implicit_dummy_args, run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component( + BT_COMPONENT_CLASS_TYPE_FILTER, &implicit_muxer_args, run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component( + BT_COMPONENT_CLASS_TYPE_FILTER, &implicit_trimmer_args, + run_args); + if (ret) { + goto error; + } + + ret = append_run_args_for_implicit_component( + BT_COMPONENT_CLASS_TYPE_FILTER, &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) { + for (i = 0; i < bt_value_array_size(run_args); i++) { + struct bt_value *arg_value = + bt_value_array_get(run_args, i); + const char *arg; + GString *quoted = NULL; + const char *arg_to_print; + + assert(arg_value); + ret = bt_value_string_get(arg_value, &arg); + assert(ret == 0); + BT_PUT(arg_value); + + if (print_run_args) { + quoted = bt_common_shell_quote(arg); + 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_size(run_args) - 1) { + if (print_run_args) { + putchar(' '); + } else { + putchar('\0'); + } + } + } + + *retcode = -1; + BT_PUT(cfg); + goto end; + } + + cfg = bt_config_run_from_args_array(run_args, retcode, + omit_system_plugin_path, omit_home_plugin_path, + initial_plugin_paths); + goto end; + +error: + *retcode = 1; + BT_PUT(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + free(arg); + + if (cur_name) { + g_string_free(cur_name, TRUE); + } + + if (cur_name_prefix) { + g_string_free(cur_name_prefix, TRUE); + } + + bt_put(run_args); + bt_put(all_names); + destroy_glist_of_gstring(source_names); + destroy_glist_of_gstring(filter_names); + destroy_glist_of_gstring(sink_names); + destroy_implicit_component_args(&implicit_ctf_args); + destroy_implicit_component_args(&implicit_lttng_live_args); + destroy_implicit_component_args(&implicit_dummy_args); + destroy_implicit_component_args(&implicit_text_args); + destroy_implicit_component_args(&implicit_debug_info_args); + destroy_implicit_component_args(&implicit_muxer_args); + destroy_implicit_component_args(&implicit_trimmer_args); + bt_put(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 Turn on debug mode\n"); + fprintf(fp, " -h --help Show this help and quit\n"); + fprintf(fp, " -v, --verbose Turn on verbose mode\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"); +} + +struct bt_config *bt_config_from_args(int argc, const char *argv[], + int *retcode, bool omit_system_plugin_path, + bool omit_home_plugin_path, + struct bt_value *initial_plugin_paths) +{ + struct bt_config *config = NULL; + bool verbose = false; + bool debug = false; + int i; + const char **command_argv = NULL; + int command_argc = -1; + const char *command_name = NULL; + + 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_get(initial_plugin_paths); + } + + if (argc <= 1) { + print_gen_usage(stdout); + goto end; + } + + for (i = 1; i < argc; i++) { + const char *cur_arg = argv[i]; + + if (strcmp(cur_arg, "-d") == 0 || + strcmp(cur_arg, "--debug") == 0) { + debug = true; + } else if (strcmp(cur_arg, "-v") == 0 || + strcmp(cur_arg, "--verbose") == 0) { + verbose = true; + } 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 { + bool has_command = true; + + /* + * First unknown argument: is it a known command + * name? + */ + 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 whole + * arguments are for the default convert + * command. + */ + command_type = COMMAND_TYPE_CONVERT; + command_argv = argv; + command_argc = argc; + has_command = false; + } + + if (has_command) { + command_argv = &argv[i]; + command_argc = argc - i; + command_name = cur_arg; + } + 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; + } + + assert(command_argv); + assert(command_argc >= 0); + + switch (command_type) { + case COMMAND_TYPE_RUN: + config = bt_config_run_from_args(command_argc, command_argv, + retcode, omit_system_plugin_path, + omit_home_plugin_path, initial_plugin_paths); + break; + case COMMAND_TYPE_CONVERT: + config = bt_config_convert_from_args(command_argc, command_argv, + retcode, omit_system_plugin_path, + omit_home_plugin_path, initial_plugin_paths); + break; + case COMMAND_TYPE_LIST_PLUGINS: + config = bt_config_list_plugins_from_args(command_argc, + command_argv, retcode, omit_system_plugin_path, + omit_home_plugin_path, initial_plugin_paths); + break; + case COMMAND_TYPE_HELP: + config = bt_config_help_from_args(command_argc, + command_argv, retcode, omit_system_plugin_path, + omit_home_plugin_path, initial_plugin_paths); + break; + case COMMAND_TYPE_QUERY: + config = bt_config_query_from_args(command_argc, + command_argv, retcode, omit_system_plugin_path, + omit_home_plugin_path, initial_plugin_paths); + break; + default: + assert(false); + } + + if (config) { + if (verbose) { + config->verbose = true; + } + + if (debug) { + config->debug = true; + } + + config->command_name = command_name; + } + +end: + bt_put(initial_plugin_paths); + return config; +} diff --git a/cli/babeltrace-cfg.h b/cli/babeltrace-cfg.h new file mode 100644 index 00000000..ca2f9950 --- /dev/null +++ b/cli/babeltrace-cfg.h @@ -0,0 +1,122 @@ +#ifndef BABELTRACE_CONVERTER_CFG_H +#define BABELTRACE_CONVERTER_CFG_H + +/* + * Babeltrace trace converter - configuration + * + * 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 +#include +#include +#include +#include + +struct bt_config_component { + struct bt_object base; + enum bt_component_class_type type; + GString *plugin_name; + GString *comp_cls_name; + struct bt_value *params; + GString *instance_name; +}; + +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 { + struct bt_object base; + bool debug; + bool verbose; + struct bt_value *plugin_paths; + bool omit_system_plugin_path; + bool omit_home_plugin_path; + bool command_needs_plugins; + const char *command_name; + 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; + } 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; + } print_ctf_metadata; + + /* BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS */ + struct { + GString *url; + } print_lttng_live_sessions; + } cmd_data; +}; + +static inline +struct bt_config_component *bt_config_get_component(GPtrArray *array, + size_t index) +{ + return bt_get(g_ptr_array_index(array, index)); +} + +struct bt_config *bt_config_from_args(int argc, const char *argv[], + int *retcode, bool omit_system_plugin_path, + bool omit_home_plugin_path, + struct bt_value *initial_plugin_paths); + +struct bt_config_component *bt_config_component_from_arg( + enum bt_component_class_type type, const char *arg); + +enum bt_value_status bt_config_append_plugin_paths( + struct bt_value *plugin_paths, const char *arg); + +#endif /* BABELTRACE_CONVERTER_CFG_H */ diff --git a/cli/babeltrace-log.c b/cli/babeltrace-log.c new file mode 100644 index 00000000..38cf8392 --- /dev/null +++ b/cli/babeltrace-log.c @@ -0,0 +1,474 @@ +/* + * babeltrace-log.c + * + * BabelTrace - Convert Text Log to CTF + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define NSEC_PER_USEC 1000UL +#define NSEC_PER_MSEC 1000000UL +#define NSEC_PER_SEC 1000000000ULL +#define USEC_PER_SEC 1000000UL + +bool babeltrace_debug, babeltrace_verbose; + +static char *s_outputname; +static int s_timestamp; +static int s_help; +static unsigned char s_uuid[BABELTRACE_UUID_LEN]; + +/* Metadata format string */ +static const char metadata_fmt[] = +"/* CTF 1.8 */\n" +"typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\n" +"typealias integer { size = 32; align = 32; signed = false; } := uint32_t;\n" +"typealias integer { size = 64; align = 64; signed = false; } := uint64_t;\n" +"\n" +"trace {\n" +" major = %u;\n" /* major (e.g. 0) */ +" minor = %u;\n" /* minor (e.g. 1) */ +" uuid = \"%s\";\n" /* UUID */ +" byte_order = %s;\n" /* be or le */ +" packet.header := struct {\n" +" uint32_t magic;\n" +" uint8_t uuid[16];\n" +" };\n" +"};\n" +"\n" +"stream {\n" +" packet.context := struct {\n" +" uint64_t content_size;\n" +" uint64_t packet_size;\n" +" };\n" +"%s" /* Stream event header (opt.) */ +"};\n" +"\n" +"event {\n" +" name = string;\n" +" fields := struct { string str; };\n" +"};\n"; + +static const char metadata_stream_event_header_timestamp[] = +" typealias integer { size = 64; align = 64; signed = false; } := uint64_t;\n" +" event.header := struct {\n" +" uint64_t timestamp;\n" +" };\n"; + +static +void print_metadata(FILE *fp) +{ + char uuid_str[BABELTRACE_UUID_STR_LEN]; + unsigned int major = 0, minor = 0; + int ret; + + ret = sscanf(VERSION, "%u.%u", &major, &minor); + if (ret != 2) + fprintf(stderr, "[warning] Incorrect babeltrace version format\n."); + bt_uuid_unparse(s_uuid, uuid_str); + fprintf(fp, metadata_fmt, + major, + minor, + uuid_str, + BYTE_ORDER == LITTLE_ENDIAN ? "le" : "be", + s_timestamp ? metadata_stream_event_header_timestamp : ""); +} + +static +void write_packet_header(struct ctf_stream_pos *pos, unsigned char *uuid) +{ + struct ctf_stream_pos dummy; + + /* magic */ + ctf_dummy_pos(pos, &dummy); + if (!ctf_align_pos(&dummy, sizeof(uint32_t) * CHAR_BIT)) + goto error; + if (!ctf_move_pos(&dummy, sizeof(uint32_t) * CHAR_BIT)) + goto error; + assert(!ctf_pos_packet(&dummy)); + + if (!ctf_align_pos(pos, sizeof(uint32_t) * CHAR_BIT)) + goto error; + *(uint32_t *) ctf_get_pos_addr(pos) = 0xC1FC1FC1; + if (!ctf_move_pos(pos, sizeof(uint32_t) * CHAR_BIT)) + goto error; + + /* uuid */ + ctf_dummy_pos(pos, &dummy); + if (!ctf_align_pos(&dummy, sizeof(uint8_t) * CHAR_BIT)) + goto error; + if (!ctf_move_pos(&dummy, 16 * CHAR_BIT)) + goto error; + assert(!ctf_pos_packet(&dummy)); + + if (!ctf_align_pos(pos, sizeof(uint8_t) * CHAR_BIT)) + goto error; + memcpy(ctf_get_pos_addr(pos), uuid, BABELTRACE_UUID_LEN); + if (!ctf_move_pos(pos, BABELTRACE_UUID_LEN * CHAR_BIT)) + goto error; + return; + +error: + fprintf(stderr, "[error] Out of packet bounds when writing packet header\n"); + abort(); +} + +static +void write_packet_context(struct ctf_stream_pos *pos) +{ + struct ctf_stream_pos dummy; + + /* content_size */ + ctf_dummy_pos(pos, &dummy); + if (!ctf_align_pos(&dummy, sizeof(uint64_t) * CHAR_BIT)) + goto error; + if (!ctf_move_pos(&dummy, sizeof(uint64_t) * CHAR_BIT)) + goto error; + assert(!ctf_pos_packet(&dummy)); + + if (!ctf_align_pos(pos, sizeof(uint64_t) * CHAR_BIT)) + goto error; + *(uint64_t *) ctf_get_pos_addr(pos) = ~0ULL; /* Not known yet */ + pos->content_size_loc = (uint64_t *) ctf_get_pos_addr(pos); + if (!ctf_move_pos(pos, sizeof(uint64_t) * CHAR_BIT)) + goto error; + + /* packet_size */ + ctf_dummy_pos(pos, &dummy); + if (!ctf_align_pos(&dummy, sizeof(uint64_t) * CHAR_BIT)) + goto error; + if (!ctf_move_pos(&dummy, sizeof(uint64_t) * CHAR_BIT)) + goto error; + assert(!ctf_pos_packet(&dummy)); + + if (!ctf_align_pos(pos, sizeof(uint64_t) * CHAR_BIT)) + goto error; + *(uint64_t *) ctf_get_pos_addr(pos) = pos->packet_size; + if (!ctf_move_pos(pos, sizeof(uint64_t) * CHAR_BIT)) + goto error; + return; + +error: + fprintf(stderr, "[error] Out of packet bounds when writing packet context\n"); + abort(); +} + +static +void write_event_header(struct ctf_stream_pos *pos, char *line, + char **tline, size_t len, size_t *tlen, + uint64_t *ts) +{ + if (!s_timestamp) + return; + + /* Only need to be executed on first pass (dummy) */ + if (pos->dummy) { + int has_timestamp = 0; + unsigned long sec, usec, msec; + unsigned int year, mon, mday, hour, min; + + /* Extract time from input line */ + if (sscanf(line, "[%lu.%lu] ", &sec, &usec) == 2) { + *ts = (uint64_t) sec * USEC_PER_SEC + (uint64_t) usec; + /* + * Default CTF clock has 1GHz frequency. Convert + * from usec to nsec. + */ + *ts *= NSEC_PER_USEC; + has_timestamp = 1; + } else if (sscanf(line, "[%u-%u-%u %u:%u:%lu.%lu] ", + &year, &mon, &mday, &hour, &min, + &sec, &msec) == 7) { + time_t ep_sec; + struct tm ti; + + memset(&ti, 0, sizeof(ti)); + ti.tm_year = year - 1900; /* from 1900 */ + ti.tm_mon = mon - 1; /* 0 to 11 */ + ti.tm_mday = mday; + ti.tm_hour = hour; + ti.tm_min = min; + ti.tm_sec = sec; + + ep_sec = bt_timegm(&ti); + if (ep_sec != (time_t) -1) { + *ts = (uint64_t) ep_sec * NSEC_PER_SEC + + (uint64_t) msec * NSEC_PER_MSEC; + } + has_timestamp = 1; + } + if (has_timestamp) { + *tline = strchr(line, ']'); + assert(*tline); + (*tline)++; + if ((*tline)[0] == ' ') { + (*tline)++; + } + *tlen = len + line - *tline; + } + } + /* timestamp */ + if (!ctf_align_pos(pos, sizeof(uint64_t) * CHAR_BIT)) + goto error; + if (!pos->dummy) + *(uint64_t *) ctf_get_pos_addr(pos) = *ts; + if (!ctf_move_pos(pos, sizeof(uint64_t) * CHAR_BIT)) + goto error; + return; + +error: + fprintf(stderr, "[error] Out of packet bounds when writing event header\n"); + abort(); +} + +static +void trace_string(char *line, struct ctf_stream_pos *pos, size_t len) +{ + struct ctf_stream_pos dummy; + int attempt = 0; + char *tline = line; /* tline is start of text, after timestamp */ + size_t tlen = len; + uint64_t ts = 0; + + printf_debug("read: %s\n", line); + + for (;;) { + int packet_filled = 0; + + ctf_dummy_pos(pos, &dummy); + write_event_header(&dummy, line, &tline, len, &tlen, &ts); + if (!ctf_align_pos(&dummy, sizeof(uint8_t) * CHAR_BIT)) + packet_filled = 1; + if (!ctf_move_pos(&dummy, tlen * CHAR_BIT)) + packet_filled = 1; + if (packet_filled || ctf_pos_packet(&dummy)) { + ctf_pos_pad_packet(pos); + write_packet_header(pos, s_uuid); + write_packet_context(pos); + if (attempt++ == 1) { + fprintf(stderr, "[Error] Line too large for packet size (%" PRIu64 "kB) (discarded)\n", + pos->packet_size / CHAR_BIT / 1024); + return; + } + continue; + } else { + break; + } + } + + write_event_header(pos, line, &tline, len, &tlen, &ts); + if (!ctf_align_pos(pos, sizeof(uint8_t) * CHAR_BIT)) + goto error; + memcpy(ctf_get_pos_addr(pos), tline, tlen); + if (!ctf_move_pos(pos, tlen * CHAR_BIT)) + goto error; + return; + +error: + fprintf(stderr, "[error] Out of packet bounds when writing event payload\n"); + abort(); +} + +static +void trace_text(FILE *input, int output) +{ + struct ctf_stream_pos pos; + ssize_t len; + char *line = NULL, *nl; + size_t linesize = 0; + int ret; + + memset(&pos, 0, sizeof(pos)); + ret = ctf_init_pos(&pos, NULL, output, O_RDWR); + if (ret) { + fprintf(stderr, "Error in ctf_init_pos\n"); + return; + } + ctf_packet_seek(&pos.parent, 0, SEEK_CUR); + write_packet_header(&pos, s_uuid); + write_packet_context(&pos); + for (;;) { + len = bt_getline(&line, &linesize, input); + if (len < 0) + break; + nl = strrchr(line, '\n'); + if (nl) { + *nl = '\0'; + trace_string(line, &pos, nl - line + 1); + } else { + trace_string(line, &pos, strlen(line) + 1); + } + } + ret = ctf_fini_pos(&pos); + if (ret) { + fprintf(stderr, "Error in ctf_fini_pos\n"); + } +} + +static +void usage(FILE *fp) +{ + fprintf(fp, "BabelTrace Log Converter %s\n", VERSION); + fprintf(fp, "\n"); + fprintf(fp, "Convert for a text log (read from standard input) to CTF.\n"); + fprintf(fp, "\n"); + fprintf(fp, "usage : babeltrace-log [OPTIONS] OUTPUT\n"); + fprintf(fp, "\n"); + fprintf(fp, " OUTPUT Output trace path\n"); + fprintf(fp, "\n"); + fprintf(fp, " -t With timestamps (format: [sec.usec] string\\n)\n"); + fprintf(fp, " (format: [YYYY-MM-DD HH:MM:SS.MS] string\\n)\n"); + fprintf(fp, "\n"); +} + +static +int parse_args(int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-t")) + s_timestamp = 1; + else if (!strcmp(argv[i], "-h")) { + s_help = 1; + return 0; + } else if (argv[i][0] == '-') + return -EINVAL; + else + s_outputname = argv[i]; + } + if (!s_outputname) + return -EINVAL; + return 0; +} + +int main(int argc, char **argv) +{ + int fd, metadata_fd, ret; + DIR *dir; + int dir_fd; + FILE *metadata_fp; + + ret = parse_args(argc, argv); + if (ret) { + fprintf(stderr, "Error: invalid argument.\n"); + usage(stderr); + goto error; + } + + if (s_help) { + usage(stdout); + exit(EXIT_SUCCESS); + } + + ret = g_mkdir(s_outputname, S_IRWXU|S_IRWXG); + if (ret) { + perror("g_mkdir"); + goto error; + } + + dir = opendir(s_outputname); + if (!dir) { + perror("opendir"); + goto error_rmdir; + } + dir_fd = bt_dirfd(dir); + if (dir_fd < 0) { + perror("dirfd"); + goto error_closedir; + } + + fd = openat(dir_fd, "datastream", O_RDWR|O_CREAT, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); + if (fd < 0) { + perror("openat"); + goto error_closedirfd; + } + + metadata_fd = openat(dir_fd, "metadata", O_RDWR|O_CREAT, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); + if (metadata_fd < 0) { + perror("openat"); + goto error_closedatastream; + } + metadata_fp = fdopen(metadata_fd, "w"); + if (!metadata_fp) { + perror("fdopen"); + goto error_closemetadatafd; + } + + bt_uuid_generate(s_uuid); + print_metadata(metadata_fp); + trace_text(stdin, fd); + + ret = close(fd); + if (ret) + perror("close"); + exit(EXIT_SUCCESS); + + /* error handling */ +error_closemetadatafd: + ret = close(metadata_fd); + if (ret) + perror("close"); +error_closedatastream: + ret = close(fd); + if (ret) + perror("close"); +error_closedirfd: + ret = close(dir_fd); + if (ret) + perror("close"); +error_closedir: + ret = closedir(dir); + if (ret) + perror("closedir"); +error_rmdir: + ret = rmdir(s_outputname); + if (ret) + perror("rmdir"); +error: + exit(EXIT_FAILURE); +} diff --git a/cli/babeltrace.c b/cli/babeltrace.c new file mode 100644 index 00000000..cb8492f2 --- /dev/null +++ b/cli/babeltrace.c @@ -0,0 +1,1290 @@ +/* + * babeltrace.c + * + * Babeltrace Trace Converter + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "babeltrace-cfg.h" +#include "babeltrace-cfg-connect.h" +#include "default-cfg.h" + +GPtrArray *loaded_plugins; + +static +void init_loaded_plugins_array(void) +{ + loaded_plugins = g_ptr_array_new_full(8, bt_put); +} + +static +void fini_loaded_plugins_array(void) +{ + g_ptr_array_free(loaded_plugins, TRUE); +} + +static +struct bt_plugin *find_plugin(const char *name) +{ + int i; + struct bt_plugin *plugin = NULL; + + 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; + } + + return bt_get(plugin); +} + +static +struct bt_component_class *find_component_class(const char *plugin_name, + const char *comp_class_name, + enum bt_component_class_type comp_class_type) +{ + struct bt_component_class *comp_class = NULL; + struct bt_plugin *plugin = find_plugin(plugin_name); + + if (!plugin) { + goto end; + } + + comp_class = bt_plugin_get_component_class_by_name_and_type(plugin, + comp_class_name, comp_class_type); + BT_PUT(plugin); +end: + return comp_class; +} + +static +void print_indent(size_t indent) +{ + size_t i; + + for (i = 0; i < indent; i++) { + printf(" "); + } +} + +static char *escape_name_for_shell(const char *input) +{ + char *output = g_malloc0(strlen(input) * 5 + 1); + const char *in; + char *out = output; + + if (!output) { + goto end; + } + + for (in = input; *in != '\0'; in++) { + switch (*in) { + case '\\': + *out++ = '\\'; + *out++ = '\\'; + break; + case '\'': + *out++ = '\''; + *out++ = '"'; + *out++ = '\''; + *out++ = '"'; + *out++ = '\''; + break; + case '.': + *out++ = '\\'; + *out++ = '.'; + break; + default: + *out++ = *in; + break; + } + } + +end: + return output; +} + +static +const char *component_type_str(enum 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"; + case BT_COMPONENT_CLASS_TYPE_UNKNOWN: + default: + return "unknown"; + } +} + +static void print_plugin_comp_cls_opt(FILE *fh, const char *plugin_name, + const char *comp_cls_name, enum bt_component_class_type type) +{ + char *shell_plugin_name = NULL; + char *shell_comp_cls_name = NULL; + + shell_plugin_name = escape_name_for_shell(plugin_name); + if (!shell_plugin_name) { + goto end; + } + + shell_comp_cls_name = escape_name_for_shell(comp_cls_name); + if (!shell_comp_cls_name) { + goto end; + } + + fprintf(fh, "%s%s--%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_reset(), + bt_common_color_fg_default(), + bt_common_color_bold(), + bt_common_color_fg_blue(), + shell_plugin_name, + bt_common_color_fg_default(), + bt_common_color_fg_yellow(), + shell_comp_cls_name, + bt_common_color_reset()); + +end: + g_free(shell_plugin_name); + g_free(shell_comp_cls_name); +} + +static +void print_value(struct bt_value *, size_t); + +static +void print_value_rec(struct bt_value *, size_t); + +static +bool print_map_value(const char *key, struct bt_value *object, void *data) +{ + size_t *indent = data; + + print_indent(*indent); + printf("%s: ", key); + + if (bt_value_is_array(object) && + bt_value_array_is_empty(object)) { + printf("[ ]\n"); + return true; + } + + if (bt_value_is_map(object) && + bt_value_map_is_empty(object)) { + printf("{ }\n"); + return true; + } + + if (bt_value_is_array(object) || + bt_value_is_map(object)) { + printf("\n"); + } + + print_value_rec(object, *indent + 2); + return true; +} + +static +void print_value_rec(struct bt_value *value, size_t indent) +{ + bool bool_val; + int64_t int_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: + printf("%snull%s\n", bt_common_color_bold(), + bt_common_color_reset()); + break; + case BT_VALUE_TYPE_BOOL: + bt_value_bool_get(value, &bool_val); + printf("%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_INTEGER: + bt_value_integer_get(value, &int_val); + printf("%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_FLOAT: + bt_value_float_get(value, &dbl_val); + printf("%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: + bt_value_string_get(value, &str_val); + printf("%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_size(value); + assert(size >= 0); + + if (size == 0) { + print_indent(indent); + printf("[ ]\n"); + break; + } + + for (i = 0; i < size; i++) { + struct bt_value *element = + bt_value_array_get(value, i); + + assert(element); + print_indent(indent); + printf("- "); + + if (bt_value_is_array(element) && + bt_value_array_is_empty(element)) { + printf("[ ]\n"); + continue; + } + + if (bt_value_is_map(element) && + bt_value_map_is_empty(element)) { + printf("{ }\n"); + continue; + } + + if (bt_value_is_array(element) || + bt_value_is_map(element)) { + printf("\n"); + } + + print_value_rec(element, indent + 2); + BT_PUT(element); + } + break; + case BT_VALUE_TYPE_MAP: + if (bt_value_map_is_empty(value)) { + print_indent(indent); + printf("{ }\n"); + break; + } + + bt_value_map_foreach(value, print_map_value, &indent); + break; + default: + assert(false); + } +} + +static +void print_value(struct bt_value *value, size_t indent) +{ + if (!bt_value_is_array(value) && !bt_value_is_map(value)) { + print_indent(indent); + } + + print_value_rec(value, indent); +} + +static +void print_bt_config_component(struct bt_config_component *bt_config_component) +{ + printf(" "); + print_plugin_comp_cls_opt(stdout, bt_config_component->plugin_name->str, + bt_config_component->comp_cls_name->str, + bt_config_component->type); + printf(":\n"); + + if (bt_config_component->instance_name->len > 0) { + printf(" Name: %s\n", + bt_config_component->instance_name->str); + } + + printf(" Parameters:\n"); + print_value(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_PUT(cfg_component); + } +} + +static +void print_plugin_paths(struct bt_value *plugin_paths) +{ + printf(" Plugin paths:\n"); + print_value(plugin_paths, 4); +} + +static +void print_cfg_run(struct bt_config *cfg) +{ + size_t i; + + print_plugin_paths(cfg->plugin_paths); + printf(" Source component instances:\n"); + print_bt_config_components(cfg->cmd_data.run.sources); + + if (cfg->cmd_data.run.filters->len > 0) { + printf(" Filter component instances:\n"); + print_bt_config_components(cfg->cmd_data.run.filters); + } + + printf(" Sink component instances:\n"); + print_bt_config_components(cfg->cmd_data.run.sinks); + printf(" 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); + + printf(" %s%s%s -> %s%s%s\n", + cfg_connection->src_instance_name->str, + cfg_connection->src_port_name->len > 0 ? "." : "", + cfg_connection->src_port_name->str, + cfg_connection->dst_instance_name->str, + cfg_connection->dst_port_name->len > 0 ? "." : "", + cfg_connection->dst_port_name->str); + } +} + +static +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); + printf(" 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); + printf(" 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); + printf(" Object: `%s`\n", cfg->cmd_data.query.object->str); + printf(" Component class:\n"); + print_bt_config_component(cfg->cmd_data.query.cfg_component); +} + +static +void print_cfg(struct bt_config *cfg) +{ + if (!babeltrace_verbose) { + return; + } + + printf("Configuration:\n"); + printf(" Debug mode: %s\n", cfg->debug ? "yes" : "no"); + printf(" 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: + assert(false); + } +} + +static +struct bt_component *create_trimmer(struct bt_config_component *source_cfg) +{ + struct bt_component *trimmer = NULL; + struct bt_component_class *trimmer_class = NULL; + struct bt_value *trimmer_params = NULL; + struct bt_value *value; + + trimmer_params = bt_value_map_create(); + if (!trimmer_params) { + goto end; + } + + value = bt_value_map_get(source_cfg->params, "begin"); + if (value) { + enum bt_value_status ret; + + ret = bt_value_map_insert(trimmer_params, "begin", + value); + BT_PUT(value); + if (ret) { + goto end; + } + } + value = bt_value_map_get(source_cfg->params, "end"); + if (value) { + enum bt_value_status ret; + + ret = bt_value_map_insert(trimmer_params, "end", + value); + BT_PUT(value); + if (ret) { + goto end; + } + } + value = bt_value_map_get(source_cfg->params, "clock-gmt"); + if (value) { + enum bt_value_status ret; + + ret = bt_value_map_insert(trimmer_params, "clock-gmt", + value); + BT_PUT(value); + if (ret) { + goto end; + } + } + + trimmer_class = find_component_class("utils", "trimmer", + BT_COMPONENT_CLASS_TYPE_FILTER); + if (!trimmer_class) { + fprintf(stderr, "Could not find trimmer component class. Aborting...\n"); + goto end; + } + trimmer = bt_component_create(trimmer_class, "source_trimmer", + trimmer_params); + if (!trimmer) { + goto end; + } +end: + bt_put(trimmer_params); + bt_put(trimmer_class); + return trimmer; +} + +static +int connect_source_sink(struct bt_graph *graph, + struct bt_component *source, + struct bt_config_component *source_cfg, + struct bt_component *sink) +{ + int ret = 0; + struct bt_connection *connection = NULL; + struct bt_component *trimmer = NULL; + struct bt_port *source_port = + bt_component_source_get_default_output_port(source); + struct bt_port *sink_port = + bt_component_sink_get_default_input_port(sink); + struct bt_port *to_sink_port = NULL; + struct bt_port *trimmer_input_port = NULL; + + if (!source_port) { + fprintf(stderr, "Failed to find default source output port. Aborting...\n"); + ret = -1; + goto end; + } + if (!sink_port) { + fprintf(stderr, "Failed to find default sink input port. Aborting...\n"); + ret = -1; + goto end; + } + + if (bt_value_map_has_key(source_cfg->params, "begin") + || bt_value_map_has_key(source_cfg->params, "end")) { + /* A trimmer must be inserted in the graph. */ + trimmer = create_trimmer(source_cfg); + if (!trimmer) { + fprintf(stderr, "Failed to create trimmer component. Aborting...\n"); + ret = -1; + goto end; + } + + trimmer_input_port = bt_component_filter_get_default_input_port( + trimmer); + if (!trimmer_input_port) { + fprintf(stderr, "Failed to find trimmer input port. Aborting...\n"); + ret = -1; + goto end; + } + to_sink_port = bt_component_filter_get_default_output_port( + trimmer); + if (!to_sink_port) { + fprintf(stderr, "Failed to find trimmer output port. Aborting...\n"); + ret = -1; + goto end; + } + + connection = bt_graph_connect_ports(graph, source_port, + trimmer_input_port); + if (!connection) { + fprintf(stderr, "Failed to connect source to trimmer. Aborting...\n"); + ret = -1; + goto end; + } + BT_PUT(connection); + } else { + BT_MOVE(to_sink_port, source_port); + } + + connection = bt_graph_connect_ports(graph, to_sink_port, sink_port); + if (!connection) { + fprintf(stderr, "Failed to connect to sink. Aborting...\n"); + ret = -1; + goto end; + } +end: + bt_put(trimmer); + bt_put(source_port); + bt_put(sink_port); + bt_put(to_sink_port); + bt_put(connection); + return ret; +} + +static +void add_to_loaded_plugins(struct bt_plugin **plugins) +{ + while (*plugins) { + struct bt_plugin *plugin = *plugins; + /* Check if it's already loaded (from another path). */ + struct bt_plugin *loaded_plugin = + find_plugin(bt_plugin_get_name(plugin)); + + if (loaded_plugin) { + printf_verbose("Not loading plugin `%s`: already loaded from `%s`\n", + bt_plugin_get_path(plugin), + bt_plugin_get_path(loaded_plugin)); + BT_PUT(loaded_plugin); + BT_PUT(plugin); + } else { + /* Transfer ownership to global array. */ + g_ptr_array_add(loaded_plugins, plugin); + } + *(plugins++) = NULL; + } +} + +static +int load_dynamic_plugins(struct bt_value *plugin_paths) +{ + int nr_paths, i, ret = 0; + + nr_paths = bt_value_array_size(plugin_paths); + if (nr_paths < 0) { + ret = -1; + goto end; + } + + for (i = 0; i < nr_paths; i++) { + struct bt_value *plugin_path_value = NULL; + const char *plugin_path; + struct bt_plugin **plugins; + + plugin_path_value = bt_value_array_get(plugin_paths, i); + if (bt_value_string_get(plugin_path_value, + &plugin_path)) { + BT_PUT(plugin_path_value); + continue; + } + + plugins = bt_plugin_create_all_from_dir(plugin_path, false); + if (!plugins) { + printf_debug("Unable to dynamically load plugins from path %s.\n", + plugin_path); + BT_PUT(plugin_path_value); + continue; + } + + add_to_loaded_plugins(plugins); + free(plugins); + + BT_PUT(plugin_path_value); + } +end: + return ret; +} + +static +int load_static_plugins(void) +{ + int ret = 0; + struct bt_plugin **plugins; + + plugins = bt_plugin_create_all_from_static(); + if (!plugins) { + printf_debug("Unable to load static plugins.\n"); + ret = -1; + goto end; + } + + add_to_loaded_plugins(plugins); + free(plugins); +end: + return ret; +} + +static int load_all_plugins(struct bt_value *plugin_paths) +{ + int ret = 0; + + if (load_dynamic_plugins(plugin_paths)) { + fprintf(stderr, "Failed to load dynamic plugins.\n"); + ret = -1; + goto end; + } + + if (load_static_plugins()) { + fprintf(stderr, "Failed to load static plugins.\n"); + ret = -1; + goto end; + } + +end: + return ret; +} + +static void print_plugin_info(struct bt_plugin *plugin) +{ + unsigned int major, minor, patch; + const char *extra; + enum bt_plugin_status version_status; + 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_status = 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()); + printf(" %sPath%s: %s\n", bt_common_color_bold(), + bt_common_color_reset(), path ? path : "(None)"); + + if (version_status == BT_PLUGIN_STATUS_OK) { + 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; + struct bt_component_class *comp_cls = NULL; + struct bt_value *results = NULL; + + ret = load_all_plugins(cfg->plugin_paths); + if (ret) { + goto end; + } + + 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) { + 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; + } + + results = bt_component_class_query(comp_cls, + cfg->cmd_data.query.object->str, + cfg->cmd_data.query.cfg_component->params); + if (!results) { + 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\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + cfg->cmd_data.query.object->str, + bt_common_color_reset()); + ret = -1; + goto end; + } + + print_value(results, 0); + +end: + bt_put(comp_cls); + bt_put(results); + return ret; +} + +static int cmd_help(struct bt_config *cfg) +{ + int ret; + struct bt_plugin *plugin = NULL; + size_t i; + + ret = load_all_plugins(cfg->plugin_paths); + if (ret) { + goto end; + } + + plugin = find_plugin(cfg->cmd_data.help.cfg_component->plugin_name->str); + if (!plugin) { + 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(" %sComponent classes%s: %d\n", + bt_common_color_bold(), + bt_common_color_reset(), + bt_plugin_get_component_class_count(plugin)); + + + if (cfg->cmd_data.help.cfg_component->type != + BT_COMPONENT_CLASS_TYPE_UNKNOWN) { + struct bt_component_class *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) { + 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; + } + + bt_put(needed_comp_cls); + } + + for (i = 0; i < bt_plugin_get_component_class_count(plugin); i++) { + struct bt_component_class *comp_cls = + bt_plugin_get_component_class(plugin, i); + 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); + enum bt_component_class_type type = + bt_component_class_get_type(comp_cls); + + assert(comp_cls); + + if (cfg->cmd_data.help.cfg_component->type != + BT_COMPONENT_CLASS_TYPE_UNKNOWN) { + if (strcmp(cfg->cmd_data.help.cfg_component->comp_cls_name->str, + comp_class_name) != 0 && + type == + cfg->cmd_data.help.cfg_component->type) { + bt_put(comp_cls); + continue; + } + } + + printf("\n"); + print_plugin_comp_cls_opt(stdout, + cfg->cmd_data.help.cfg_component->plugin_name->str, + 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); + } + + bt_put(comp_cls); + } + +end: + bt_put(plugin); + return ret; +} + +static int cmd_list_plugins(struct bt_config *cfg) +{ + int ret; + int plugins_count, component_classes_count = 0, i; + + ret = load_all_plugins(cfg->plugin_paths); + if (ret) { + goto end; + } + + printf("From the following plugin paths:\n\n"); + print_value(cfg->plugin_paths, 2); + printf("\n"); + plugins_count = loaded_plugins->len; + if (plugins_count == 0) { + fprintf(stderr, "%s%sNo plugins found.%s\n", + bt_common_color_bold(), bt_common_color_fg_red(), + bt_common_color_reset()); + fprintf(stderr, "\n"); + fprintf(stderr, "Please make sure your plugin search path is set correctly. You can use\n"); + fprintf(stderr, "the --plugin-path command-line option or the BABELTRACE_PLUGIN_PATH\n"); + fprintf(stderr, "environment variable.\n"); + ret = -1; + goto end; + } + + for (i = 0; i < plugins_count; i++) { + struct bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); + + component_classes_count += bt_plugin_get_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++) { + int j; + struct bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); + + component_classes_count = + bt_plugin_get_component_class_count(plugin); + printf("\n"); + print_plugin_info(plugin); + + if (component_classes_count == 0) { + printf(" %sComponent classes%s: (None)\n", + bt_common_color_bold(), + bt_common_color_reset()); + } else { + printf(" %sComponent classes%s:\n", + bt_common_color_bold(), + bt_common_color_reset()); + } + + for (j = 0; j < component_classes_count; j++) { + struct bt_component_class *comp_class = + bt_plugin_get_component_class(plugin, j); + const char *comp_class_name = + bt_component_class_get_name(comp_class); + const char *comp_class_description = + bt_component_class_get_description(comp_class); + enum 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"); + bt_put(comp_class); + } + } + +end: + return ret; +} + +static int cmd_print_lttng_live_sessions(struct bt_config *cfg) +{ + printf("TODO\n"); + return -1; +} + +static int cmd_print_ctf_metadata(struct bt_config *cfg) +{ + int ret = 0; + struct bt_component_class *comp_cls = NULL; + struct bt_value *results = NULL; + struct bt_value *params = NULL; + struct 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 enum bt_component_class_type comp_cls_type = + BT_COMPONENT_CLASS_TYPE_SOURCE; + + assert(cfg->cmd_data.print_ctf_metadata.path); + comp_cls = find_component_class(plugin_name, comp_cls_name, + comp_cls_type); + if (!comp_cls) { + 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(params, "path", + cfg->cmd_data.print_ctf_metadata.path->str); + if (ret) { + ret = -1; + goto end; + } + + results = bt_component_class_query(comp_cls, "metadata-info", + params); + if (!results) { + ret = -1; + fprintf(stderr, "%s%sFailed to request metadata info%s\n", + bt_common_color_bold(), + bt_common_color_fg_red(), + bt_common_color_reset()); + goto end; + } + + metadata_text_value = bt_value_map_get(results, "text"); + if (!metadata_text_value) { + ret = -1; + goto end; + } + + ret = bt_value_string_get(metadata_text_value, &metadata_text); + assert(ret == 0); + printf("%s\n", metadata_text); + +end: + bt_put(results); + bt_put(params); + bt_put(metadata_text_value); + bt_put(comp_cls); + return 0; +} + +static int cmd_run(struct bt_config *cfg) +{ + int ret = 0; + struct bt_component_class *source_class = NULL; + struct bt_component_class *sink_class = NULL; + struct bt_component *source = NULL, *sink = NULL; + struct bt_value *source_params = NULL, *sink_params = NULL; + struct bt_config_component *source_cfg = NULL, *sink_cfg = NULL; + struct bt_graph *graph = NULL; + + ret = load_all_plugins(cfg->plugin_paths); + if (ret) { + goto end; + } + + /* TODO handle more than 1 source and 1 sink. */ + if (cfg->cmd_data.run.sources->len != 1 || + cfg->cmd_data.run.sinks->len != 1) { + fprintf(stderr, "Only one source and one sink component class are supported. Aborting...\n"); + ret = -1; + goto end; + } + + source_cfg = bt_config_get_component(cfg->cmd_data.run.sources, 0); + source_params = bt_get(source_cfg->params); + source_class = find_component_class(source_cfg->plugin_name->str, + source_cfg->comp_cls_name->str, + BT_COMPONENT_CLASS_TYPE_SOURCE); + if (!source_class) { + fprintf(stderr, "Could not find "); + print_plugin_comp_cls_opt(stderr, source_cfg->plugin_name->str, + source_cfg->comp_cls_name->str, BT_COMPONENT_CLASS_TYPE_SOURCE); + fprintf(stderr, ". Aborting...\n"); + ret = -1; + goto end; + } + + sink_cfg = bt_config_get_component(cfg->cmd_data.run.sinks, 0); + sink_params = bt_get(sink_cfg->params); + sink_class = find_component_class(sink_cfg->plugin_name->str, + sink_cfg->comp_cls_name->str, + BT_COMPONENT_CLASS_TYPE_SINK); + if (!sink_class) { + fprintf(stderr, "Could not find "); + print_plugin_comp_cls_opt(stderr, sink_cfg->plugin_name->str, + sink_cfg->comp_cls_name->str, BT_COMPONENT_CLASS_TYPE_SINK); + fprintf(stderr, ". Aborting...\n"); + ret = -1; + goto end; + } + + graph = bt_graph_create(); + if (!graph) { + ret = -1; + goto end; + } + + source = bt_component_create(source_class, "source", source_params); + if (!source) { + fprintf(stderr, "Failed to instantiate selected source component. Aborting...\n"); + ret = -1; + goto end; + } + + sink = bt_component_create(sink_class, "sink", sink_params); + if (!sink) { + fprintf(stderr, "Failed to instantiate selected output component. Aborting...\n"); + ret = -1; + goto end; + } + + ret = connect_source_sink(graph, source, source_cfg, sink); + if (ret) { + ret = -1; + goto end; + } + + while (true) { + enum bt_graph_status graph_status; + + graph_status = bt_graph_run(graph); + switch (graph_status) { + case BT_GRAPH_STATUS_AGAIN: + /* Wait for an arbitraty 500 ms. */ + usleep(500000); + break; + case BT_COMPONENT_STATUS_END: + goto end; + default: + fprintf(stderr, "Sink component returned an error, aborting...\n"); + ret = -1; + goto end; + } + } + +end: + bt_put(sink_class); + bt_put(source_class); + bt_put(source); + bt_put(sink); + bt_put(source_params); + bt_put(sink_params); + bt_put(sink_cfg); + bt_put(source_cfg); + bt_put(graph); + return ret; +} + +static void warn_command_name_and_directory_clash(struct bt_config *cfg) +{ + if (!cfg->command_name) { + 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); + } +} + +int main(int argc, const char **argv) +{ + int ret; + int retcode; + struct bt_config *cfg; + + init_loaded_plugins_array(); + cfg = bt_config_from_args_with_defaults(argc, argv, &retcode); + + if (retcode < 0) { + /* Quit without errors; typically usage/version */ + retcode = 0; + goto end; + } + + if (retcode > 0) { + goto end; + } + + if (!cfg) { + fprintf(stderr, "Failed to create Babeltrace configuration\n"); + retcode = 1; + goto end; + } + + babeltrace_debug = cfg->debug; + babeltrace_verbose = cfg->verbose; + print_cfg(cfg); + + if (cfg->command_needs_plugins) { + ret = load_all_plugins(cfg->plugin_paths); + if (ret) { + retcode = 1; + goto end; + } + } + + 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: + assert(false); + } + + warn_command_name_and_directory_clash(cfg); + retcode = ret ? 1 : 0; + +end: + BT_PUT(cfg); + fini_loaded_plugins_array(); + return retcode; +} diff --git a/cli/default-cfg.c b/cli/default-cfg.c new file mode 100644 index 00000000..fb325431 --- /dev/null +++ b/cli/default-cfg.c @@ -0,0 +1,75 @@ +/* + * default-cfg.c + * + * Babeltrace Trace Converter - Default Configuration + * + * Copyright 2016 - Jérémie Galarneau + * + * Author: 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 +#include "babeltrace-cfg.h" +#include "default-cfg.h" +#include "config.h" + +#ifdef BT_SET_DEFAULT_IN_TREE_CONFIGURATION + +struct bt_config *bt_config_from_args_with_defaults(int argc, + const char *argv[], int *retcode) +{ + struct 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_from_args(argc, argv, retcode, true, true, + initial_plugin_paths); + goto end; + +error: + *retcode = 1; + BT_PUT(cfg); + +end: + bt_put(initial_plugin_paths); + return cfg; +} + +#else /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ + +struct bt_config *bt_config_from_args_with_defaults(int argc, + const char *argv[], int *retcode) +{ + return bt_config_from_args(argc, argv, retcode, false, false, NULL); +} + +#endif /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ diff --git a/cli/default-cfg.h b/cli/default-cfg.h new file mode 100644 index 00000000..d6b91888 --- /dev/null +++ b/cli/default-cfg.h @@ -0,0 +1,33 @@ +#ifndef BABELTRACE_DEFAULT_CFG_H +#define BABELTRACE_DEFAULT_CFG_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_from_args_with_defaults(int argc, + const char *argv[], int *retcode); + +#endif /* BABELTRACE_DEFAULT_CFG_H */ diff --git a/configure.ac b/configure.ac index 0e0ddbef..a18c0cb8 100644 --- a/configure.ac +++ b/configure.ac @@ -446,7 +446,7 @@ AC_CONFIG_FILES([ Makefile common/Makefile compat/Makefile - converter/Makefile + cli/Makefile doc/Makefile doc/api/Makefile doc/api/Doxyfile diff --git a/converter/Makefile.am b/converter/Makefile.am deleted file mode 100644 index c1fce34e..00000000 --- a/converter/Makefile.am +++ /dev/null @@ -1,44 +0,0 @@ -PLUGINS_PATH = $(abs_top_builddir)/plugins -AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include \ - -DCONFIG_IN_TREE_PLUGIN_PATH=\"$(PLUGINS_PATH)/ctf:$(PLUGINS_PATH)/text:$(PLUGINS_PATH)/muxer:$(PLUGINS_PATH)/writer:$(PLUGINS_PATH)/utils\" -AM_LDFLAGS = -lpopt - -bin_PROGRAMS = babeltrace.bin -noinst_PROGRAMS = babeltrace -#check_PROGRAMS = babeltrace - -babeltrace_bin_SOURCES = \ - babeltrace.c \ - babeltrace-cfg.c \ - babeltrace-cfg.h \ - default-cfg.h \ - default-cfg.c \ - babeltrace-cfg-connect.h \ - babeltrace-cfg-connect.c - -# -Wl,--no-as-needed is needed for recent gold linker who seems to think -# it knows better and considers libraries with constructors having -# side-effects as dead code. -babeltrace_bin_LDFLAGS = -Wl, $(LD_NO_AS_NEEDED), -export-dynamic -babeltrace_bin_LDADD = \ - $(top_builddir)/lib/libbabeltrace.la \ - $(top_builddir)/compat/libcompat.la \ - $(top_builddir)/common/libbabeltrace-common.la - -if ENABLE_DEBUG_INFO -babeltrace_bin_LDADD += $(top_builddir)/lib/libdebug-info.la -endif - -if BUILT_IN_PLUGINS -babeltrace_bin_LDFLAGS += -Wl,--whole-archive,$(top_builddir)/plugins/ctf/.libs/libbabeltrace-plugin-ctf.a,$(top_builddir)/plugins/text/.libs/libbabeltrace-plugin-ctf-text.a,$(top_builddir)/plugins/muxer/.libs/libbabeltrace-plugin-muxer.a,$(top_builddir)/plugins/writer/.libs/libbabeltrace-plugin-ctf-writer.a,--no-whole-archive -endif - -if BABELTRACE_BUILD_WITH_MINGW -babeltrace_bin_LDADD += -lrpcrt4 -lintl -liconv -lole32 -lpopt -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 diff --git a/converter/babeltrace-cfg-connect.c b/converter/babeltrace-cfg-connect.c deleted file mode 100644 index e1de064d..00000000 --- a/converter/babeltrace-cfg-connect.c +++ /dev/null @@ -1,680 +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 "babeltrace-cfg.h" -#include "babeltrace-cfg-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); -} - -void bt_config_connection_destroy(struct bt_config_connection *connection) -{ - if (!connection) { - return; - } - - if (connection->src_instance_name) { - g_string_free(connection->src_instance_name, TRUE); - } - - if (connection->dst_instance_name) { - g_string_free(connection->dst_instance_name, TRUE); - } - - if (connection->src_port_name) { - g_string_free(connection->src_port_name, TRUE); - } - - if (connection->dst_port_name) { - g_string_free(connection->dst_port_name, TRUE); - } - - if (connection->arg) { - g_string_free(connection->arg, TRUE); - } - - g_free(connection); -} - -static struct bt_config_connection *bt_config_connection_create(const char *arg) -{ - struct bt_config_connection *cfg_connection; - - cfg_connection = g_new0(struct bt_config_connection, 1); - if (!cfg_connection) { - goto error; - } - - cfg_connection->src_instance_name = g_string_new(NULL); - if (!cfg_connection->src_instance_name) { - goto error; - } - - cfg_connection->dst_instance_name = g_string_new(NULL); - if (!cfg_connection->dst_instance_name) { - goto error; - } - - cfg_connection->src_port_name = g_string_new(NULL); - if (!cfg_connection->src_port_name) { - goto error; - } - - cfg_connection->dst_port_name = g_string_new(NULL); - if (!cfg_connection->dst_port_name) { - goto error; - } - - cfg_connection->arg = g_string_new(arg); - if (!cfg_connection->arg) { - goto error; - } - - goto end; - -error: - g_free(cfg_connection); - cfg_connection = NULL; - -end: - return cfg_connection; -} - -static struct bt_config_connection *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 { - SRC_NAME, - DST_NAME, - SRC_PORT_NAME, - DST_PORT_NAME, - } state = SRC_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 SRC_NAME: - gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - g_string_free(cfg_conn->src_instance_name, TRUE); - cfg_conn->src_instance_name = gs; - gs = NULL; - - if (at[end_pos] == ':') { - state = DST_NAME; - } else if (at[end_pos] == '.') { - state = SRC_PORT_NAME; - } else { - goto error; - } - - at += end_pos + 1; - break; - case DST_NAME: - gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - g_string_free(cfg_conn->dst_instance_name, TRUE); - cfg_conn->dst_instance_name = gs; - gs = NULL; - - if (at[end_pos] == '.') { - state = DST_PORT_NAME; - } else if (at[end_pos] == '\0') { - goto end; - } else { - goto error; - } - - at += end_pos + 1; - break; - case SRC_PORT_NAME: - gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - g_string_free(cfg_conn->src_port_name, TRUE); - cfg_conn->src_port_name = gs; - gs = NULL; - - if (at[end_pos] == ':') { - state = DST_NAME; - } else { - goto error; - } - - at += end_pos + 1; - break; - case DST_PORT_NAME: - gs = bt_common_string_until(at, ".:\\", ".:", &end_pos); - if (!gs || gs->len == 0) { - goto error; - } - - g_string_free(cfg_conn->dst_port_name, TRUE); - cfg_conn->dst_port_name = gs; - gs = NULL; - - if (at[end_pos] == '\0') { - goto end; - } else { - goto error; - } - break; - default: - assert(false); - } - } - -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 = bt_get(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->src_instance_name->str); - bt_put(comp); - if (!comp) { - snprintf(error_buf, error_buf_size, - "Invalid connection: cannot find upstream component `%s`:\n %s\n", - connection->src_instance_name->str, - connection->arg->str); - ret = -1; - goto end; - } - - comp = find_component(cfg, connection->dst_instance_name->str); - bt_put(comp); - if (!comp) { - snprintf(error_buf, error_buf_size, - "Invalid connection: cannot find downstream component `%s`:\n %s\n", - connection->dst_instance_name->str, - connection->arg->str); - ret = -1; - goto end; - } - } - -end: - return ret; -} - -static int validate_connection_directions(struct bt_config *cfg, - char *error_buf, size_t error_buf_size) -{ - size_t i; - int ret = 0; - struct bt_config_component *src_comp = NULL; - struct bt_config_component *dst_comp = NULL; - - for (i = 0; i < cfg->cmd_data.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->src_instance_name->str); - assert(src_comp); - dst_comp = find_component(cfg, - connection->dst_instance_name->str); - 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->src_instance_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->src_instance_name->str, - connection->arg->str); - ret = -1; - goto end; - } - } else { - snprintf(error_buf, error_buf_size, - "Invalid connection: cannot connect sink component `%s` to component `%s`:\n %s\n", - connection->src_instance_name->str, - connection->dst_instance_name->str, - connection->arg->str); - ret = -1; - goto end; - } - - BT_PUT(src_comp); - BT_PUT(dst_comp); - } - -end: - bt_put(src_comp); - bt_put(dst_comp); - return ret; -} - -static int validate_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; - - 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->src_instance_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->dst_instance_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->dst_instance_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->src_instance_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, - struct bt_value *connected_components, - char *error_buf, size_t error_buf_size) -{ - int ret = 0; - size_t i; - - for (i = 0; i < comps->len; i++) { - struct bt_config_component *comp = g_ptr_array_index(comps, i); - - if (!bt_value_map_has_key(connected_components, - comp->instance_name->str)) { - snprintf(error_buf, error_buf_size, - "Component `%s` is not connected\n", - comp->instance_name->str); - ret = -1; - goto end; - } - } - -end: - return ret; -} - -static int validate_all_components_connected(struct bt_config *cfg, - char *error_buf, size_t error_buf_size) -{ - size_t i; - int ret = 0; - struct bt_value *connected_components = bt_value_map_create(); - - if (!connected_components) { - ret = -1; - goto end; - } - - for (i = 0; i < cfg->cmd_data.run.connections->len; i++) { - struct bt_config_connection *connection = - g_ptr_array_index(cfg->cmd_data.run.connections, i); - - ret = bt_value_map_insert(connected_components, - connection->src_instance_name->str, bt_value_null); - if (ret) { - goto end; - } - - ret = bt_value_map_insert(connected_components, - connection->dst_instance_name->str, bt_value_null); - if (ret) { - goto end; - } - } - - ret = validate_all_components_connected_in_array( - cfg->cmd_data.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_put(connected_components); - return ret; -} - -static int validate_no_duplicate_connection(struct bt_config *cfg, - char *error_buf, size_t error_buf_size) -{ - size_t i; - int ret = 0; - struct bt_value *flat_connection_names = bt_value_map_create(); - GString *flat_connection_name = NULL; - - if (!flat_connection_names) { - ret = -1; - goto end; - } - - flat_connection_name = g_string_new(NULL); - if (!flat_connection_name) { - ret = -1; - goto end; - } - - for (i = 0; i < cfg->cmd_data.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->src_instance_name->str, - connection->src_port_name->str, - connection->dst_instance_name->str, - connection->dst_port_name->str); - - if (bt_value_map_has_key(flat_connection_names, - flat_connection_name->str)) { - snprintf(error_buf, error_buf_size, - "Duplicate connection:\n %s\n", - connection->arg->str); - ret = -1; - goto end; - } - - ret = bt_value_map_insert(flat_connection_names, - flat_connection_name->str, bt_value_null); - if (ret) { - goto end; - } - } - -end: - bt_put(flat_connection_names); - - if (flat_connection_name) { - g_string_free(flat_connection_name, TRUE); - } - - return ret; -} - -static int validate_connections(struct bt_config *cfg, char *error_buf, - size_t error_buf_size) -{ - int ret; - - ret = validate_all_endpoints_exist(cfg, error_buf, error_buf_size); - if (ret) { - goto end; - } - - ret = validate_connection_directions(cfg, error_buf, error_buf_size); - if (ret) { - goto end; - } - - ret = validate_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_create_connections(struct bt_config *cfg, - struct bt_value *connection_args, - char *error_buf, size_t error_buf_size) -{ - int ret; - size_t i; - - if (!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_size(connection_args); i++) { - struct bt_value *arg_value = - bt_value_array_get(connection_args, i); - const char *arg; - struct bt_config_connection *cfg_connection; - - ret = bt_value_string_get(arg_value, &arg); - BT_PUT(arg_value); - assert(ret == 0); - cfg_connection = cfg_connection_from_arg(arg); - if (!cfg_connection) { - snprintf(error_buf, error_buf_size, "Cannot parse --connect option's argument:\n %s\n", - arg); - goto error; - } - - g_ptr_array_add(cfg->cmd_data.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/converter/babeltrace-cfg-connect.h b/converter/babeltrace-cfg-connect.h deleted file mode 100644 index 98f9d041..00000000 --- a/converter/babeltrace-cfg-connect.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef BABELTRACE_CONNECT_H -#define BABELTRACE_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" - -struct bt_config_connection { - GString *src_instance_name; - GString *dst_instance_name; - GString *src_port_name; - GString *dst_port_name; - GString *arg; -}; - -int bt_config_create_connections(struct bt_config *cfg, - struct bt_value *connection_args, - char *error_buf, size_t error_buf_size); - -void bt_config_connection_destroy(struct bt_config_connection *connection); - -#endif /* BABELTRACE_CONNECT_H */ diff --git a/converter/babeltrace-cfg.c b/converter/babeltrace-cfg.c deleted file mode 100644 index bf36be9b..00000000 --- a/converter/babeltrace-cfg.c +++ /dev/null @@ -1,4727 +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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "babeltrace-cfg.h" -#include "babeltrace-cfg-connect.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 negative number value */ - INI_EXPECT_VALUE_NUMBER_NEG, - - /* 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) */ - struct 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; - struct bt_value *names; - struct 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"); -} - -static -int ini_handle_state(struct ini_parsing_state *state) -{ - int ret = 0; - GTokenType token_type; - struct 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: - case INI_EXPECT_VALUE_NUMBER_NEG: - 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_key(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: - { - switch (token_type) { - case G_TOKEN_CHAR: - if (state->scanner->value.v_char == '-') { - /* Negative number */ - state->expecting = - INI_EXPECT_VALUE_NUMBER_NEG; - goto success; - } else { - ini_append_error_expecting(state, - state->scanner, "value"); - goto error; - } - break; - case G_TOKEN_INT: - { - /* Positive integer */ - uint64_t int_val = state->scanner->value.v_int64; - - if (int_val > (1ULL << 63) - 1) { - g_string_append_printf(state->ini_error, - "Integer value %" PRIu64 " is outside the range of a 64-bit signed integer\n", - int_val); - goto error; - } - - value = bt_value_integer_create_init( - (int64_t) int_val); - break; - } - case G_TOKEN_FLOAT: - /* Positive floating point number */ - value = bt_value_float_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 */ - break; - } - - if (!value) { - ini_append_error_expecting(state, - state->scanner, "value"); - goto error; - } - - state->expecting = INI_EXPECT_COMMA; - goto success; - } - case INI_EXPECT_VALUE_NUMBER_NEG: - { - switch (token_type) { - case G_TOKEN_INT: - { - /* Negative integer */ - uint64_t int_val = state->scanner->value.v_int64; - - if (int_val > (1ULL << 63) - 1) { - g_string_append_printf(state->ini_error, - "Integer value -%" PRIu64 " is outside the range of a 64-bit signed integer\n", - int_val); - goto error; - } - - value = bt_value_integer_create_init( - -((int64_t) int_val)); - break; - } - case G_TOKEN_FLOAT: - /* Negative floating point number */ - value = bt_value_float_create_init( - -state->scanner->value.v_float); - break; - default: - /* Unset value variable will trigger the error */ - break; - } - - if (!value) { - ini_append_error_expecting(state, - state->scanner, "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: - assert(false); - } - -error: - ret = -1; - goto end; - -success: - if (value) { - if (bt_value_map_insert(state->params, - state->last_map_key, value)) { - /* Only override return value on error */ - ret = -1; - } - } - -end: - BT_PUT(value); - return ret; -} - -/* - * Converts an INI-style argument to an equivalent map value object. - * - * Return value is owned by the caller. - */ -static -struct 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_PUT(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 -struct bt_value *bt_value_from_arg(const char *arg) -{ - struct 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 and component class names, and the instance name, - * from a command-line source/filter/sink option's argument. arg must - * have the following format: - * - * [NAME:]PLUGIN.CLS - * - * where NAME is the optional component name, PLUGIN is the plugin name, - * and CLS is the component class name. - * - * On success, both *plugin and *component are not NULL. *plugin - * and *component are owned by the caller. On success, *name can be NULL - * if no component name was found. - */ -static -void plugin_comp_cls_names(const char *arg, char **name, char **plugin, - char **comp_cls) -{ - const char *at = arg; - GString *gs_name = NULL; - GString *gs_plugin = NULL; - GString *gs_comp_cls = NULL; - size_t end_pos; - - assert(arg); - assert(plugin); - assert(comp_cls); - - 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 plugin name */ - gs_plugin = bt_common_string_until(at, ".:\\", ".", &end_pos); - if (!gs_plugin || gs_plugin->len == 0 || at[end_pos] == '\0') { - 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) { - 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 (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 (name) { - *name = NULL; - } - - *plugin = NULL; - *comp_cls = NULL; - -end: - return; -} - -/* - * Prints the Babeltrace version. - */ -static -void print_version(void) -{ - puts("Babeltrace " VERSION); -} - -/* - * Destroys a component configuration. - */ -static -void bt_config_component_destroy(struct 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_PUT(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( - enum 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(cfg_component, 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_PUT(cfg_component); - -end: - return cfg_component; -} - -/* - * Creates a component configuration from a command-line source/sink - * option's argument. - */ -struct bt_config_component *bt_config_component_from_arg( - enum bt_component_class_type type, const char *arg) -{ - struct bt_config_component *cfg_comp = NULL; - char *name = NULL; - char *plugin_name = NULL; - char *comp_cls_name = NULL; - - plugin_comp_cls_names(arg, &name, &plugin_name, &comp_cls_name); - if (!plugin_name || !comp_cls_name) { - printf_err("Cannot get plugin or component class name\n"); - 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_PUT(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(struct bt_object *obj) -{ - struct bt_config *cfg = - container_of(obj, struct bt_config, base); - - if (!obj) { - goto end; - } - - BT_PUT(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_PUT(cfg->cmd_data.help.cfg_component); - break; - case BT_CONFIG_COMMAND_QUERY: - BT_PUT(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); - } - 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); - } - break; - default: - assert(false); - } - - g_free(cfg); - -end: - return; -} - -static void destroy_gstring(void *data) -{ - g_string_free(data, TRUE); -} - -static void destroy_glist_of_gstring(GList *list) -{ - if (!list) { - return; - } - - GList *at; - - for (at = list; at != NULL; at = g_list_next(at)) { - g_string_free(at->data, TRUE); - } - - g_list_free(list); -} - -/* - * Extracts the various paths from the string arg, delimited by ':', - * and appends them to the array value object plugin_paths. - */ -enum bt_value_status bt_config_append_plugin_paths( - struct bt_value *plugin_paths, const char *arg) -{ - enum bt_value_status status = BT_VALUE_STATUS_OK; - GPtrArray *dirs = g_ptr_array_new_with_free_func(destroy_gstring); - int ret; - size_t i; - - if (!dirs) { - status = BT_VALUE_STATUS_ERROR; - goto end; - } - - ret = bt_common_append_plugin_path_dirs(arg, dirs); - if (ret) { - status = BT_VALUE_STATUS_ERROR; - goto end; - } - - for (i = 0; i < dirs->len; i++) { - GString *dir = g_ptr_array_index(dirs, i); - - bt_value_array_append_string(plugin_paths, dir->str); - } - -end: - g_ptr_array_free(dirs, TRUE); - return status; -} - -/* - * 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 -struct bt_value *names_from_arg(const char *arg) -{ - GScanner *scanner = NULL; - struct 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(names, - "payload")) { - goto error; - } - } else if (!strcmp(identifier, "context") || - !strcmp(identifier, "ctx")) { - found_item = true; - if (bt_value_array_append_string(names, - "context")) { - goto error; - } - } else if (!strcmp(identifier, "scope") || - !strcmp(identifier, "header")) { - found_item = true; - if (bt_value_array_append_string(names, - identifier)) { - goto error; - } - } else if (!strcmp(identifier, "all")) { - found_all = true; - if (bt_value_array_append_string(names, - identifier)) { - goto error; - } - } else if (!strcmp(identifier, "none")) { - found_none = true; - if (bt_value_array_append_string(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(names, "none")) { - goto error; - } - } - if (scanner) { - g_scanner_destroy(scanner); - } - return names; - -error: - BT_PUT(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 -struct bt_value *fields_from_arg(const char *arg) -{ - GScanner *scanner = NULL; - struct 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(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_PUT(fields); - -end: - if (scanner) { - g_scanner_destroy(scanner); - } - return fields; -} - -static -void append_param_arg(GString *params_arg, const char *key, const char *value) -{ - assert(params_arg); - assert(key); - 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, - struct bt_value *names_array, const char *prefix) -{ - int ret = 0; - int i; - GString *tmpstr = NULL, *default_value = NULL; - - /* - * 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_size(names_array); i++) { - struct bt_value *str_obj = bt_value_array_get(names_array, i); - const char *suffix; - bool is_default = false; - - if (!str_obj) { - printf_err("Unexpected error\n"); - ret = -1; - goto end; - } - - ret = bt_value_string_get(str_obj, &suffix); - BT_PUT(str_obj); - if (ret) { - printf_err("Unexpected error\n"); - goto end; - } - - 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) { - g_string_append(tmpstr, "default"); - append_param_arg(params_arg, tmpstr->str, - default_value->str); - } else { - g_string_append(tmpstr, suffix); - append_param_arg(params_arg, tmpstr->str, "yes"); - } - } - -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_CONNECT, - OPT_DEBUG, - OPT_DEBUG_INFO_DIR, - OPT_DEBUG_INFO_FULL_PATH, - OPT_DEBUG_INFO_TARGET_PREFIX, - OPT_END, - OPT_FIELDS, - OPT_FILTER, - OPT_HELP, - OPT_INPUT_FORMAT, - OPT_KEY, - OPT_LIST, - OPT_NAME, - OPT_NAMES, - OPT_NO_DEBUG_INFO, - OPT_NO_DELTA, - OPT_OMIT_HOME_PLUGIN_PATH, - OPT_OMIT_SYSTEM_PLUGIN_PATH, - OPT_OUTPUT_FORMAT, - OPT_OUTPUT, - OPT_PARAMS, - OPT_PATH, - OPT_PLUGIN_PATH, - OPT_RESET_BASE_PARAMS, - OPT_RUN_ARGS, - OPT_RUN_ARGS_0, - OPT_SINK, - OPT_SOURCE, - OPT_STREAM_INTERSECTION, - OPT_TIMERANGE, - OPT_URL, - OPT_VALUE, - OPT_VERBOSE, -}; - -enum bt_config_component_dest { - 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_get(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: - assert(false); - } -} - -static int add_run_cfg_comp_check_name(struct bt_config *cfg, - struct bt_config_component *cfg_comp, - enum bt_config_component_dest dest, - struct 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_key(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(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(struct bt_value *plugin_paths) -{ - int ret = 0; - const char *envvar; - - if (bt_common_is_setuid_setgid()) { - printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); - 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(struct 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()) { - printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); - } 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, - struct 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(cfg, bt_config_destroy); - cfg->command = command; - cfg->command_needs_plugins = needs_plugins; - - if (initial_plugin_paths) { - cfg->plugin_paths = bt_get(initial_plugin_paths); - } else { - cfg->plugin_paths = bt_value_array_create(); - if (!cfg->plugin_paths) { - print_err_oom(); - goto error; - } - } - - goto end; - -error: - BT_PUT(cfg); - -end: - return cfg; -} - -static struct bt_config *bt_config_run_create( - struct 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_put); - 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_put); - 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_put); - 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_PUT(cfg); - -end: - return cfg; -} - -static struct bt_config *bt_config_list_plugins_create( - struct 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_PUT(cfg); - -end: - return cfg; -} - -static struct bt_config *bt_config_help_create( - struct 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(BT_COMPONENT_CLASS_TYPE_UNKNOWN, - NULL, NULL); - if (!cfg->cmd_data.help.cfg_component) { - goto error; - } - - goto end; - -error: - BT_PUT(cfg); - -end: - return cfg; -} - -static struct bt_config *bt_config_query_create( - struct 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_PUT(cfg); - -end: - return cfg; -} - -static struct bt_config *bt_config_print_ctf_metadata_create( - struct 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; - } - - goto end; - -error: - BT_PUT(cfg); - -end: - return cfg; -} - -static struct bt_config *bt_config_print_lttng_live_sessions_create( - struct 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; - } - - goto end; - -error: - BT_PUT(cfg); - -end: - return cfg; -} - -int bt_config_append_plugin_paths_check_setuid_setgid( - struct bt_value *plugin_paths, const char *arg) -{ - int ret = 0; - - if (bt_common_is_setuid_setgid()) { - printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); - 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) 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, "\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, " observe=3.14, simple=beef, needs-quotes=\"some string\",\n"); - fprintf(fp, " escape.chars-are:allowed=\"this is a \\\" double quote\"\n"); - fprintf(fp, "\n"); - fprintf(fp, "IMPORTANT: Make sure to single-quote the whole argument when you run babeltrace\n"); - fprintf(fp, "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] --source=PLUGIN.CLS\n"); - fprintf(fp, " babeltrace [GENERAL OPTIONS] help [OPTIONS] --filter=PLUGIN.CLS\n"); - fprintf(fp, " babeltrace [GENERAL OPTIONS] help [OPTIONS] --sink=PLUGIN.CLS\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -f, --filter=PLUGIN.CLS Get help for the filter component class\n"); - fprintf(fp, " CLS found in the plugin PLUGIN\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, " -S, --sink=PLUGIN.CLS Get help for the sink component class\n"); - fprintf(fp, " CLS found in the plugin PLUGIN\n"); - fprintf(fp, " -s, --source=PLUGIN.CLS Get help for the source component class\n"); - fprintf(fp, " CLS found in the plugin PLUGIN\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 */ - { "filter", 'f', POPT_ARG_STRING, NULL, OPT_FILTER, NULL, NULL }, - { "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 }, - { "sink", 'S', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, - { "source", 's', POPT_ARG_STRING, NULL, OPT_SOURCE, 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 omit_system_plugin_path, - bool omit_home_plugin_path, - struct 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; - char *plug_comp_cls_names = NULL; - - *retcode = 0; - cfg = bt_config_help_create(initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->omit_system_plugin_path = omit_system_plugin_path; - cfg->omit_home_plugin_path = 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_SOURCE: - case OPT_FILTER: - case OPT_SINK: - if (cfg->cmd_data.help.cfg_component->type != - BT_COMPONENT_CLASS_TYPE_UNKNOWN) { - printf_err("Cannot specify more than one plugin and component class:\n %s\n", - arg); - goto error; - } - - switch (opt) { - case OPT_SOURCE: - cfg->cmd_data.help.cfg_component->type = - BT_COMPONENT_CLASS_TYPE_SOURCE; - break; - case OPT_FILTER: - cfg->cmd_data.help.cfg_component->type = - BT_COMPONENT_CLASS_TYPE_FILTER; - break; - case OPT_SINK: - cfg->cmd_data.help.cfg_component->type = - BT_COMPONENT_CLASS_TYPE_SINK; - break; - default: - assert(false); - } - plug_comp_cls_names = strdup(arg); - if (!plug_comp_cls_names) { - print_err_oom(); - goto error; - } - break; - case OPT_HELP: - print_help_usage(stdout); - *retcode = -1; - BT_PUT(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) { - if (cfg->cmd_data.help.cfg_component->type != - BT_COMPONENT_CLASS_TYPE_UNKNOWN) { - printf_err("Cannot specify plugin name and --source/--filter/--sink component class:\n %s\n", - leftover); - goto error; - } - - g_string_assign(cfg->cmd_data.help.cfg_component->plugin_name, - leftover); - } else { - if (cfg->cmd_data.help.cfg_component->type == - BT_COMPONENT_CLASS_TYPE_UNKNOWN) { - print_help_usage(stdout); - *retcode = -1; - BT_PUT(cfg); - goto end; - } - - plugin_comp_cls_names(plug_comp_cls_names, NULL, - &plugin_name, &comp_cls_name); - if (plugin_name && comp_cls_name) { - 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 { - printf_err("Invalid --source/--filter/--sink option's argument:\n %s\n", - plug_comp_cls_names); - goto error; - } - } - - if (append_home_and_system_plugin_paths_cfg(cfg)) { - goto error; - } - - goto end; - -error: - *retcode = 1; - BT_PUT(cfg); - -end: - free(plug_comp_cls_names); - 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] OBJECT --source=PLUGIN.CLS\n"); - fprintf(fp, " babeltrace [GEN OPTS] query [OPTS] OBJECT --filter=PLUGIN.CLS\n"); - fprintf(fp, " babeltrace [GEN OPTS] query [OPTS] OBJECT --sink=PLUGIN.CLS\n"); - fprintf(fp, "\n"); - fprintf(fp, "Options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " -f. --filter=PLUGIN.CLS Query object from the filter component\n"); - fprintf(fp, " class CLS found in the plugin PLUGIN\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, " -S, --sink=PLUGIN.CLS Query object from the sink component class\n"); - fprintf(fp, " CLS found in the plugin PLUGIN\n"); - fprintf(fp, " -s, --source=PLUGIN.CLS Query object from the source component\n"); - fprintf(fp, " class CLS found in the plugin PLUGIN\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 */ - { "filter", 'f', POPT_ARG_STRING, NULL, OPT_FILTER, NULL, NULL }, - { "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 }, - { "sink", 'S', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, - { "source", 's', POPT_ARG_STRING, NULL, OPT_SOURCE, 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 omit_system_plugin_path, - bool omit_home_plugin_path, - struct bt_value *initial_plugin_paths) -{ - poptContext pc = NULL; - char *arg = NULL; - int opt; - int ret; - struct bt_config *cfg = NULL; - const char *leftover; - struct bt_value *params = bt_value_null; - - *retcode = 0; - cfg = bt_config_query_create(initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->omit_system_plugin_path = - omit_system_plugin_path; - cfg->omit_home_plugin_path = 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_SOURCE: - case OPT_FILTER: - case OPT_SINK: - { - enum bt_component_class_type type; - - if (cfg->cmd_data.query.cfg_component) { - printf_err("Cannot specify more than one plugin and component class:\n %s\n", - arg); - goto error; - } - - switch (opt) { - case OPT_SOURCE: - type = BT_COMPONENT_CLASS_TYPE_SOURCE; - break; - case OPT_FILTER: - type = BT_COMPONENT_CLASS_TYPE_FILTER; - break; - case OPT_SINK: - type = BT_COMPONENT_CLASS_TYPE_SINK; - break; - default: - assert(false); - } - - cfg->cmd_data.query.cfg_component = - bt_config_component_from_arg(type, arg); - if (!cfg->cmd_data.query.cfg_component) { - printf_err("Invalid format for --source/--filter/--sink option's argument:\n %s\n", - arg); - goto error; - } - - /* Default parameters: null */ - bt_put(cfg->cmd_data.query.cfg_component->params); - cfg->cmd_data.query.cfg_component->params = - bt_value_null; - break; - } - case OPT_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_PUT(cfg); - goto end; - default: - printf_err("Unknown command-line option specified (option code %d)\n", - opt); - goto error; - } - - free(arg); - arg = NULL; - } - - if (!cfg->cmd_data.query.cfg_component) { - printf_err("No target component class specified with --source/--filter/--sink option\n"); - goto error; - } - - assert(params); - BT_MOVE(cfg->cmd_data.query.cfg_component->params, params); - - /* 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 one leftover argument which is the - * mandatory object. - */ - 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_PUT(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_PUT(cfg); - -end: - if (pc) { - poptFreeContext(pc); - } - - BT_PUT(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 omit_system_plugin_path, - bool omit_home_plugin_path, - struct 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 = omit_system_plugin_path; - cfg->omit_home_plugin_path = 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_PUT(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_PUT(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, --connect=CONNECTION Connect two created components (see the\n"); - fprintf(fp, " expected format of CONNECTION below)\n"); - fprintf(fp, " -f, --filter=[NAME:]PLUGIN.CLS Instantiate a filter component from plugin\n"); - fprintf(fp, " PLUGIN and component class CLS, set it as\n"); - fprintf(fp, " the current component, and if NAME is\n"); - fprintf(fp, " given, set its instance name to NAME\n"); - fprintf(fp, " --key=KEY Set the current initialization string\n"); - fprintf(fp, " parameter key to KEY (see --value)\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, " -S, --sink=[NAME:]PLUGIN.CLS Instantiate a sink component from plugin\n"); - fprintf(fp, " PLUGIN and component class CLS, set it as\n"); - fprintf(fp, " the current component, and if NAME is\n"); - fprintf(fp, " given, set its instance name to NAME\n"); - fprintf(fp, " -s, --source=[NAME:]PLUGIN.CLS Instantiate a source component from plugin\n"); - fprintf(fp, " PLUGIN and component class CLS, set it as\n"); - fprintf(fp, " the current component, and if NAME is\n"); - fprintf(fp, " given, set its instance name to NAME\n"); - fprintf(fp, " --value=VAL Add a string initialization parameter to\n"); - fprintf(fp, " the current component with a name given by\n"); - fprintf(fp, " the last argument of the --key option and a\n"); - fprintf(fp, " value set to VAL\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 the names of the upstream and downstream components\n"); - fprintf(fp, "to connect together. You can set the name of the current component with the\n"); - fprintf(fp, "--name option.\n"); - fprintf(fp, "\n"); - fprintf(fp, "UPSTREAM-PORT and DOWNSTREAM-PORT are the optional upstream and downstream\n"); - fprintf(fp, "ports to use for the connection. When the port is not specified, the default\n"); - fprintf(fp, "port is used.\n"); - fprintf(fp, "\n"); - fprintf(fp, "You can connect a source component to a filter or sink component. You\n"); - fprintf(fp, "can connect a filter component to a sink component.\n"); - fprintf(fp, "\n"); - fprintf(fp, "Example:\n"); - fprintf(fp, "\n"); - fprintf(fp, " my-filter.top10:json-out\n"); - fprintf(fp, "\n\n"); - print_expected_params_format(fp); -} - -static struct poptOption run_long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL }, - { "connect", 'c', POPT_ARG_STRING, NULL, OPT_CONNECT, NULL, NULL }, - { "filter", 'f', POPT_ARG_STRING, NULL, OPT_FILTER, NULL, NULL }, - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "key", '\0', POPT_ARG_STRING, NULL, OPT_KEY, 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 }, - { "sink", 'S', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, - { "source", 's', POPT_ARG_STRING, NULL, OPT_SOURCE, NULL, NULL }, - { "value", '\0', POPT_ARG_STRING, NULL, OPT_VALUE, NULL, NULL }, - { NULL, 0, 0, NULL, 0, NULL, NULL }, -}; - -/* - * 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 omit_system_plugin_path, - bool omit_home_plugin_path, - struct 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; - struct bt_value *cur_base_params = NULL; - int opt, ret = 0; - struct bt_config *cfg = NULL; - struct bt_value *instance_names = NULL; - struct bt_value *connection_args = NULL; - GString *cur_param_key = NULL; - char error_buf[256] = { 0 }; - - *retcode = 0; - cur_param_key = g_string_new(NULL); - if (!cur_param_key) { - print_err_oom(); - goto error; - } - - if (argc <= 1) { - print_run_usage(stdout); - *retcode = -1; - goto end; - } - - cfg = bt_config_run_create(initial_plugin_paths); - if (!cfg) { - goto error; - } - - cfg->omit_system_plugin_path = omit_system_plugin_path; - cfg->omit_home_plugin_path = 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_SOURCE: - case OPT_FILTER: - case OPT_SINK: - { - enum bt_component_class_type new_comp_type; - enum bt_config_component_dest new_dest; - const char *opt_name; - - switch (opt) { - case OPT_SOURCE: - new_comp_type = BT_COMPONENT_CLASS_TYPE_SOURCE; - new_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; - opt_name = "--source"; - break; - case OPT_FILTER: - new_comp_type = BT_COMPONENT_CLASS_TYPE_FILTER; - new_dest = BT_CONFIG_COMPONENT_DEST_FILTER; - opt_name = "--filter"; - break; - case OPT_SINK: - new_comp_type = BT_COMPONENT_CLASS_TYPE_SINK; - new_dest = BT_CONFIG_COMPONENT_DEST_SINK; - opt_name = "--sink"; - break; - default: - assert(false); - } - - if (cur_cfg_comp) { - ret = add_run_cfg_comp_check_name(cfg, - cur_cfg_comp, cur_cfg_comp_dest, - instance_names); - BT_PUT(cur_cfg_comp); - if (ret) { - goto error; - } - } - - cur_cfg_comp = bt_config_component_from_arg( - new_comp_type, arg); - if (!cur_cfg_comp) { - printf_err("Invalid format for %s option's argument:\n %s\n", - opt_name, arg); - goto error; - } - - assert(cur_base_params); - bt_put(cur_cfg_comp->params); - cur_cfg_comp->params = bt_value_copy(cur_base_params); - if (!cur_cfg_comp->params) { - print_err_oom(); - goto error; - } - - cur_cfg_comp_dest = new_dest; - break; - } - case OPT_PARAMS: - { - struct bt_value *params; - struct 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; - } - - params_to_set = bt_value_map_extend(cur_cfg_comp->params, - params); - BT_PUT(params); - if (!params_to_set) { - printf_err("Cannot extend current component parameters with --params option's argument:\n %s\n", - arg); - goto error; - } - - BT_MOVE(cur_cfg_comp->params, params_to_set); - break; - } - case OPT_KEY: - if (strlen(arg) == 0) { - printf_err("Cannot set an empty string as the current parameter key\n"); - goto error; - } - - g_string_assign(cur_param_key, arg); - break; - case OPT_VALUE: - if (!cur_cfg_comp) { - printf_err("Cannot set a parameter's value of unavailable component:\n %s\n", - arg); - goto error; - } - - if (cur_param_key->len == 0) { - printf_err("--value option specified without preceding --key option:\n %s\n", - arg); - goto error; - } - - if (bt_value_map_insert_string(cur_cfg_comp->params, - cur_param_key->str, arg)) { - print_err_oom(); - goto error; - } - 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: - { - struct 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_MOVE(cur_base_params, params); - break; - } - case OPT_RESET_BASE_PARAMS: - BT_PUT(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(connection_args, - arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_HELP: - print_run_usage(stdout); - *retcode = -1; - BT_PUT(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_PUT(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_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_PUT(cfg); - -end: - if (pc) { - poptFreeContext(pc); - } - - if (cur_param_key) { - g_string_free(cur_param_key, TRUE); - } - - free(arg); - BT_PUT(cur_cfg_comp); - BT_PUT(cur_base_params); - BT_PUT(instance_names); - BT_PUT(connection_args); - return cfg; -} - -static -struct bt_config *bt_config_run_from_args_array(struct bt_value *run_args, - int *retcode, bool omit_system_plugin_path, - bool omit_home_plugin_path, - struct bt_value *initial_plugin_paths) -{ - struct bt_config *cfg = NULL; - const char **argv; - size_t i; - const size_t argc = bt_value_array_size(run_args) + 1; - - argv = calloc(argc, sizeof(*argv)); - if (!argv) { - print_err_oom(); - goto end; - } - - argv[0] = "run"; - - for (i = 0; i < bt_value_array_size(run_args); i++) { - int ret; - struct bt_value *arg_value = bt_value_array_get(run_args, i); - const char *arg; - - assert(arg_value); - ret = bt_value_string_get(arg_value, &arg); - assert(ret == 0); - assert(arg); - argv[i + 1] = arg; - bt_put(arg_value); - } - - cfg = bt_config_run_from_args(argc, argv, retcode, - omit_system_plugin_path, 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, " --filter=[NAME:]PLUGIN.CLS Instantiate a filter component from plugin\n"); - fprintf(fp, " PLUGIN and component class CLS, set it as\n"); - fprintf(fp, " the current component, and if NAME is\n"); - fprintf(fp, " given, set its instance name to 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, " 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, " --sink=[NAME:]PLUGIN.CLS Instantiate a sink component from plugin\n"); - fprintf(fp, " PLUGIN and component class CLS, set it as\n"); - fprintf(fp, " the current component, and if NAME is\n"); - fprintf(fp, " given, set its instance name to NAME\n"); - fprintf(fp, " --source=[NAME:]PLUGIN.CLS Instantiate a source component from plugin\n"); - fprintf(fp, " PLUGIN and component class CLS, set it as\n"); - fprintf(fp, " the current component, and if NAME is\n"); - fprintf(fp, " given, set its instance name to NAME\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 `ctf.fs` source 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, " --stream-intersection Only process events when all streams\n"); - fprintf(fp, " are active\n"); - fprintf(fp, "\n"); - fprintf(fp, "Implicit `text.text` sink 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 `utils.muxer` filter 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 `utils.trimmer` filter 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 `lttng-utils.debug-info` filter component options:\n"); - fprintf(fp, "\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, " --no-debug-info Do not create an implicit\n"); - fprintf(fp, " `lttng-utils.debug-info` filter component\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 `ctf.fs` source\n"); - fprintf(fp, " component\n"); - fprintf(fp, " `lttng-live`:\n"); - fprintf(fp, " Create an implicit `ctf.lttng-live`\n"); - fprintf(fp, " source component\n"); - fprintf(fp, " -o, --output-format=(text | dummy | ctf-metadata)\n"); - fprintf(fp, " `text`:\n"); - fprintf(fp, " Create an implicit `text.text` sink\n"); - fprintf(fp, " component\n"); - fprintf(fp, " `dummy`:\n"); - fprintf(fp, " Create an implicit `utils.dummy` sink\n"); - fprintf(fp, " component\n"); - fprintf(fp, " `ctf-metadata`:\n"); - fprintf(fp, " Query the `ctf.fs` component class for\n"); - fprintf(fp, " 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 }, - { "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 }, - { "filter", '\0', POPT_ARG_STRING, NULL, OPT_FILTER, 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 }, - { "no-debug-info", '\0', POPT_ARG_NONE, NULL, OPT_NO_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 }, - { "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 }, - { "sink", '\0', POPT_ARG_STRING, NULL, OPT_SINK, NULL, NULL }, - { "source", '\0', POPT_ARG_STRING, NULL, OPT_SOURCE, 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, - struct 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_key(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_key(existing_names, auto_name->str)); - -end: - return auto_name; -} - -struct implicit_component_args { - bool exists; - GString *plugin_comp_cls_arg; - GString *name_arg; - GString *params_arg; - struct bt_value *extra_args; -}; - -static -int assign_name_to_implicit_component(struct implicit_component_args *args, - const char *prefix, struct 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(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( - enum bt_component_class_type type, - struct implicit_component_args *impl_args, - struct bt_value *run_args) -{ - int ret = 0; - size_t i; - - if (!impl_args->exists) { - goto end; - } - - switch (type) { - case BT_COMPONENT_CLASS_TYPE_SOURCE: - if (bt_value_array_append_string(run_args, "--source")) { - print_err_oom(); - goto error; - } - break; - case BT_COMPONENT_CLASS_TYPE_FILTER: - if (bt_value_array_append_string(run_args, "--filter")) { - print_err_oom(); - goto error; - } - break; - case BT_COMPONENT_CLASS_TYPE_SINK: - if (bt_value_array_append_string(run_args, "--sink")) { - print_err_oom(); - goto error; - } - break; - default: - assert(false); - } - - if (bt_value_array_append_string(run_args, - impl_args->plugin_comp_cls_arg->str)) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(run_args, "--name")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(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(run_args, "--params")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(run_args, - impl_args->params_arg->str)) { - print_err_oom(); - goto error; - } - } - - for (i = 0; i < bt_value_array_size(impl_args->extra_args); i++) { - struct bt_value *elem; - const char *arg; - - elem = bt_value_array_get(impl_args->extra_args, i); - if (!elem) { - goto error; - } - - assert(bt_value_is_string(elem)); - if (bt_value_string_get(elem, &arg)) { - goto error; - } - - ret = bt_value_array_append_string(run_args, arg); - bt_put(elem); - if (ret) { - print_err_oom(); - goto error; - } - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -static -void destroy_implicit_component_args(struct implicit_component_args *args) -{ - assert(args); - - if (args->plugin_comp_cls_arg) { - g_string_free(args->plugin_comp_cls_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_put(args->extra_args); -} - -static -int init_implicit_component_args(struct implicit_component_args *args, - const char *plugin_comp_cls_arg, bool exists) -{ - int ret = 0; - - args->exists = exists; - args->plugin_comp_cls_arg = g_string_new(plugin_comp_cls_arg); - args->name_arg = g_string_new(NULL); - args->params_arg = g_string_new(NULL); - args->extra_args = bt_value_array_create(); - - if (!args->plugin_comp_cls_arg || !args->name_arg || - !args->params_arg || !args->extra_args) { - ret = -1; - destroy_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) -{ - assert(args); - assert(key); - assert(value); - append_param_arg(args->params_arg, key, value); -} - -static -int append_implicit_component_extra_arg(struct implicit_component_args *args, - const char *key, const char *value) -{ - int ret = 0; - - assert(args); - assert(key); - assert(value); - - if (bt_value_array_append_string(args->extra_args, "--key")) { - print_err_oom(); - ret = -1; - goto end; - } - - if (bt_value_array_append_string(args->extra_args, key)) { - print_err_oom(); - ret = -1; - goto end; - } - - if (bt_value_array_append_string(args->extra_args, "--value")) { - print_err_oom(); - ret = -1; - goto end; - } - - if (bt_value_array_append_string(args->extra_args, value)) { - print_err_oom(); - ret = -1; - goto end; - } - -end: - return ret; -} - -static -int convert_append_name_param(enum bt_config_component_dest dest, - GString *cur_name, GString *cur_name_prefix, - struct bt_value *run_args, struct 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 --source/--filter/--sink */ - 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_key(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(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(run_args, "--name")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(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: - assert(false); - } - - g_string_assign(cur_name_prefix, ""); - } - - goto end; - -error: - ret = -1; - -end: - return ret; -} - -/* - * Escapes `.`, `:`, and `\` of `input` with `\`. - */ -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. - */ -int append_connect_arg(struct 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(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(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. - */ -int convert_auto_connect(struct 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; - - assert(source_names); - assert(filter_names); - 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; - - 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; - } - - assert(begin); - 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); - - 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 omit_system_plugin_path, - bool omit_home_plugin_path, - struct bt_value *initial_plugin_paths) -{ - poptContext pc = NULL; - char *arg = NULL; - enum bt_config_component_dest cur_comp_dest; - int opt, ret = 0; - struct bt_config *cfg = NULL; - bool got_verbose_opt = false; - bool got_debug_opt = false; - bool got_input_format_opt = false; - bool got_output_format_opt = false; - bool trimmer_has_begin = false; - bool trimmer_has_end = 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; - struct bt_value *run_args = NULL; - struct bt_value *all_names = NULL; - GList *source_names = NULL; - GList *filter_names = NULL; - GList *sink_names = NULL; - struct implicit_component_args implicit_ctf_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 }; - struct bt_value *plugin_paths = bt_get(initial_plugin_paths); - char error_buf[256] = { 0 }; - size_t i; - struct bt_common_lttng_live_url_parts lttng_live_url_parts = { 0 }; - - *retcode = 0; - - if (argc <= 1) { - print_convert_usage(stdout); - *retcode = -1; - goto end; - } - - if (init_implicit_component_args(&implicit_ctf_args, "ctf.fs", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_lttng_live_args, - "ctf.lttng-live", false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_text_args, "text.text", - false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_dummy_args, "utils.dummy", - false)) { - goto error; - } - - if (init_implicit_component_args(&implicit_debug_info_args, - "lttng-utils.debug-info", true)) { - goto error; - } - - if (init_implicit_component_args(&implicit_muxer_args, "utils.muxer", - true)) { - goto error; - } - - if (init_implicit_component_args(&implicit_trimmer_args, - "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; - } - - /* - * 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 -> --key path --value PATH - * --url=URL -> --key url --value 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_SOURCE: - case OPT_FILTER: - case OPT_SINK: - /* 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); - if (!plugin_name || !comp_cls_name) { - printf_err("Invalid format for --source/--filter/--sink option's argument:\n %s\n", - arg); - goto error; - } - - if (name) { - g_string_assign(cur_name, name); - } else { - g_string_assign(cur_name, ""); - } - - switch (opt) { - case OPT_SOURCE: - cur_comp_dest = - BT_CONFIG_COMPONENT_DEST_SOURCE; - if (bt_value_array_append_string(run_args, - "--source")) { - print_err_oom(); - goto error; - } - break; - case OPT_FILTER: - cur_comp_dest = - BT_CONFIG_COMPONENT_DEST_FILTER; - if (bt_value_array_append_string(run_args, - "--filter")) { - print_err_oom(); - goto error; - } - break; - case OPT_SINK: - cur_comp_dest = - BT_CONFIG_COMPONENT_DEST_SINK; - if (bt_value_array_append_string(run_args, - "--sink")) { - print_err_oom(); - goto error; - } - break; - default: - assert(false); - } - - if (bt_value_array_append_string(run_args, arg)) { - print_err_oom(); - goto error; - } - - g_string_assign(cur_name_prefix, ""); - g_string_append(cur_name_prefix, plugin_name); - g_string_append_c(cur_name_prefix, '.'); - g_string_append(cur_name_prefix, 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(run_args, - "--params")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(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 (bt_value_array_append_string(run_args, "--key")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(run_args, "path")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(run_args, "--value")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(run_args, arg)) { - print_err_oom(); - 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 (bt_value_array_append_string(run_args, "--key")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(run_args, "url")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(run_args, "--value")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(run_args, arg)) { - print_err_oom(); - 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(run_args, "--name")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(run_args, arg)) { - print_err_oom(); - goto error; - } - - g_string_assign(cur_name, arg); - break; - case OPT_OMIT_HOME_PLUGIN_PATH: - if (bt_value_array_append_string(run_args, - "--omit-home-plugin-path")) { - print_err_oom(); - goto error; - } - break; - case OPT_OMIT_SYSTEM_PLUGIN_PATH: - if (bt_value_array_append_string(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(run_args, - "--plugin-path")) { - print_err_oom(); - goto error; - } - - if (bt_value_array_append_string(run_args, arg)) { - print_err_oom(); - goto error; - } - break; - case OPT_HELP: - print_convert_usage(stdout); - *retcode = -1; - BT_PUT(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_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_DEBUG_INFO: - 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_arg( - &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_arg( - &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_arg( - &implicit_trimmer_args, "begin", begin); - ret |= append_implicit_component_extra_arg( - &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, "force-correlate", "yes"); - break; - case OPT_CLOCK_GMT: - append_implicit_component_param( - &implicit_text_args, "clock-gmt", "yes"); - implicit_text_args.exists = true; - break; - case OPT_CLOCK_OFFSET: - ret = append_implicit_component_extra_arg( - &implicit_ctf_args, "clock-offset-cycles", arg); - implicit_ctf_args.exists = true; - if (ret) { - goto error; - } - break; - case OPT_CLOCK_OFFSET_NS: - ret = append_implicit_component_extra_arg( - &implicit_ctf_args, "clock-offset-ns", arg); - implicit_ctf_args.exists = true; - if (ret) { - goto error; - } - break; - case OPT_CLOCK_SECONDS: - append_implicit_component_param( - &implicit_text_args, "clock-seconds", "yes"); - implicit_text_args.exists = true; - break; - case OPT_COLOR: - ret = append_implicit_component_extra_arg( - &implicit_text_args, "color", arg); - implicit_text_args.exists = true; - if (ret) { - goto error; - } - break; - case OPT_NO_DEBUG_INFO: - implicit_debug_info_args.exists = false; - break; - case OPT_DEBUG_INFO_DIR: - ret = append_implicit_component_extra_arg( - &implicit_debug_info_args, "dir", arg); - if (ret) { - goto error; - } - break; - case OPT_DEBUG_INFO_FULL_PATH: - append_implicit_component_param( - &implicit_debug_info_args, "full-path", "yes"); - break; - case OPT_DEBUG_INFO_TARGET_PREFIX: - ret = append_implicit_component_extra_arg( - &implicit_debug_info_args, - "target-prefix", arg); - if (ret) { - goto error; - } - break; - case OPT_FIELDS: - { - struct bt_value *fields = fields_from_arg(arg); - - if (!fields) { - goto error; - } - - ret = insert_flat_params_from_array( - implicit_text_args.params_arg, - fields, "field"); - bt_put(fields); - if (ret) { - goto error; - } - break; - } - case OPT_NAMES: - { - struct bt_value *names = names_from_arg(arg); - - if (!names) { - goto error; - } - - ret = insert_flat_params_from_array( - implicit_text_args.params_arg, - names, "name"); - bt_put(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_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, "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: - ret = append_implicit_component_extra_arg( - &implicit_text_args, "path", arg); - implicit_text_args.exists = true; - if (ret) { - 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: - append_implicit_component_param(&implicit_ctf_args, - "stream-intersection", "yes"); - implicit_ctf_args.exists = true; - break; - case OPT_VERBOSE: - if (got_verbose_opt) { - printf_err("Duplicate -v/--verbose option\n"); - goto error; - } - - append_implicit_component_param(&implicit_text_args, - "verbose", "yes"); - implicit_text_args.exists = true; - got_verbose_opt = true; - break; - case OPT_DEBUG: - got_debug_opt = true; - 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; - } - - /* - * Append home and system plugin paths now that we possibly got - * --plugin-path. - */ - if (append_home_and_system_plugin_paths(plugin_paths, - omit_system_plugin_path, omit_home_plugin_path)) { - goto error; - } - - /* Consume leftover argument */ - leftover = poptGetArg(pc); - - if (poptPeekArg(pc)) { - printf_err("Unexpected argument:\n %s\n", - poptPeekArg(pc)); - goto error; - } - - /* Print CTF metadata or print LTTng-live sessions */ - if (print_ctf_metadata) { - if (!leftover) { - printf_err("--output-format=ctf-metadata specified without a path\n"); - goto error; - } - - cfg = bt_config_print_ctf_metadata_create(plugin_paths); - if (!cfg) { - goto error; - } - - cfg->debug = got_debug_opt; - cfg->verbose = got_verbose_opt; - g_string_assign(cfg->cmd_data.print_ctf_metadata.path, - leftover); - goto end; - } - - /* - * If -o dummy was not specified, and if there are no explicit - * sink components, then use an implicit `text.text` sink. - */ - if (!implicit_dummy_args.exists && !sink_names) { - implicit_text_args.exists = true; - } - - /* Decide where the leftover argument goes */ - if (leftover) { - if (implicit_lttng_live_args.exists) { - lttng_live_url_parts = - bt_common_parse_lttng_live_url(leftover, - error_buf, 256); - 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; - } - - cfg->debug = got_debug_opt; - cfg->verbose = got_verbose_opt; - g_string_assign(cfg->cmd_data.print_lttng_live_sessions.url, - leftover); - goto end; - } - - ret = append_implicit_component_extra_arg( - &implicit_lttng_live_args, "url", leftover); - if (ret) { - goto error; - } - } else { - ret = append_implicit_component_extra_arg( - &implicit_ctf_args, "path", leftover); - implicit_ctf_args.exists = true; - if (ret) { - goto error; - } - } - } - - /* - * Ensure mutual exclusion between implicit `ctf.fs` and - * `ctf.lttng-live` sources. - */ - if (implicit_ctf_args.exists && implicit_lttng_live_args.exists) { - printf_err("Cannot create both implicit `ctf.fs` and `ctf.lttng-live` source components\n"); - goto error; - } - - /* - * If the implicit `ctf.fs` or `ctf.lttng-live` source exists, - * make sure there's a leftover (which is the path or URL). - */ - if (implicit_ctf_args.exists && !leftover) { - printf_err("Missing path for implicit `ctf.fs` source component\n"); - goto error; - } - - if (implicit_lttng_live_args.exists && !leftover) { - printf_err("Missing URL for implicit `ctf.lttng-live` source component\n"); - goto error; - } - - /* Assign names to implicit components */ - ret = assign_name_to_implicit_component(&implicit_ctf_args, - "ctf", 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, - "text", 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, - "mux", all_names, NULL, false); - if (ret) { - goto error; - } - - ret = assign_name_to_implicit_component(&implicit_trimmer_args, - "trim", 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( - BT_COMPONENT_CLASS_TYPE_SOURCE, &implicit_ctf_args, run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component( - BT_COMPONENT_CLASS_TYPE_SOURCE, &implicit_lttng_live_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component( - BT_COMPONENT_CLASS_TYPE_SINK, &implicit_text_args, run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component( - BT_COMPONENT_CLASS_TYPE_SINK, &implicit_dummy_args, run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component( - BT_COMPONENT_CLASS_TYPE_FILTER, &implicit_muxer_args, run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component( - BT_COMPONENT_CLASS_TYPE_FILTER, &implicit_trimmer_args, - run_args); - if (ret) { - goto error; - } - - ret = append_run_args_for_implicit_component( - BT_COMPONENT_CLASS_TYPE_FILTER, &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) { - for (i = 0; i < bt_value_array_size(run_args); i++) { - struct bt_value *arg_value = - bt_value_array_get(run_args, i); - const char *arg; - GString *quoted = NULL; - const char *arg_to_print; - - assert(arg_value); - ret = bt_value_string_get(arg_value, &arg); - assert(ret == 0); - BT_PUT(arg_value); - - if (print_run_args) { - quoted = bt_common_shell_quote(arg); - 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_size(run_args) - 1) { - if (print_run_args) { - putchar(' '); - } else { - putchar('\0'); - } - } - } - - *retcode = -1; - BT_PUT(cfg); - goto end; - } - - cfg = bt_config_run_from_args_array(run_args, retcode, - omit_system_plugin_path, omit_home_plugin_path, - initial_plugin_paths); - goto end; - -error: - *retcode = 1; - BT_PUT(cfg); - -end: - if (pc) { - poptFreeContext(pc); - } - - free(arg); - - if (cur_name) { - g_string_free(cur_name, TRUE); - } - - if (cur_name_prefix) { - g_string_free(cur_name_prefix, TRUE); - } - - bt_put(run_args); - bt_put(all_names); - destroy_glist_of_gstring(source_names); - destroy_glist_of_gstring(filter_names); - destroy_glist_of_gstring(sink_names); - destroy_implicit_component_args(&implicit_ctf_args); - destroy_implicit_component_args(&implicit_lttng_live_args); - destroy_implicit_component_args(&implicit_dummy_args); - destroy_implicit_component_args(&implicit_text_args); - destroy_implicit_component_args(&implicit_debug_info_args); - destroy_implicit_component_args(&implicit_muxer_args); - destroy_implicit_component_args(&implicit_trimmer_args); - bt_put(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 Turn on debug mode\n"); - fprintf(fp, " -h --help Show this help and quit\n"); - fprintf(fp, " -v, --verbose Turn on verbose mode\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"); -} - -struct bt_config *bt_config_from_args(int argc, const char *argv[], - int *retcode, bool omit_system_plugin_path, - bool omit_home_plugin_path, - struct bt_value *initial_plugin_paths) -{ - struct bt_config *config = NULL; - bool verbose = false; - bool debug = false; - int i; - const char **command_argv = NULL; - int command_argc = -1; - const char *command_name = NULL; - - 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_get(initial_plugin_paths); - } - - if (argc <= 1) { - print_gen_usage(stdout); - goto end; - } - - for (i = 1; i < argc; i++) { - const char *cur_arg = argv[i]; - - if (strcmp(cur_arg, "-d") == 0 || - strcmp(cur_arg, "--debug") == 0) { - debug = true; - } else if (strcmp(cur_arg, "-v") == 0 || - strcmp(cur_arg, "--verbose") == 0) { - verbose = true; - } 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 { - bool has_command = true; - - /* - * First unknown argument: is it a known command - * name? - */ - 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 whole - * arguments are for the default convert - * command. - */ - command_type = COMMAND_TYPE_CONVERT; - command_argv = argv; - command_argc = argc; - has_command = false; - } - - if (has_command) { - command_argv = &argv[i]; - command_argc = argc - i; - command_name = cur_arg; - } - 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; - } - - assert(command_argv); - assert(command_argc >= 0); - - switch (command_type) { - case COMMAND_TYPE_RUN: - config = bt_config_run_from_args(command_argc, command_argv, - retcode, omit_system_plugin_path, - omit_home_plugin_path, initial_plugin_paths); - break; - case COMMAND_TYPE_CONVERT: - config = bt_config_convert_from_args(command_argc, command_argv, - retcode, omit_system_plugin_path, - omit_home_plugin_path, initial_plugin_paths); - break; - case COMMAND_TYPE_LIST_PLUGINS: - config = bt_config_list_plugins_from_args(command_argc, - command_argv, retcode, omit_system_plugin_path, - omit_home_plugin_path, initial_plugin_paths); - break; - case COMMAND_TYPE_HELP: - config = bt_config_help_from_args(command_argc, - command_argv, retcode, omit_system_plugin_path, - omit_home_plugin_path, initial_plugin_paths); - break; - case COMMAND_TYPE_QUERY: - config = bt_config_query_from_args(command_argc, - command_argv, retcode, omit_system_plugin_path, - omit_home_plugin_path, initial_plugin_paths); - break; - default: - assert(false); - } - - if (config) { - if (verbose) { - config->verbose = true; - } - - if (debug) { - config->debug = true; - } - - config->command_name = command_name; - } - -end: - bt_put(initial_plugin_paths); - return config; -} diff --git a/converter/babeltrace-cfg.h b/converter/babeltrace-cfg.h deleted file mode 100644 index ca2f9950..00000000 --- a/converter/babeltrace-cfg.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef BABELTRACE_CONVERTER_CFG_H -#define BABELTRACE_CONVERTER_CFG_H - -/* - * Babeltrace trace converter - configuration - * - * 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 -#include -#include -#include -#include - -struct bt_config_component { - struct bt_object base; - enum bt_component_class_type type; - GString *plugin_name; - GString *comp_cls_name; - struct bt_value *params; - GString *instance_name; -}; - -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 { - struct bt_object base; - bool debug; - bool verbose; - struct bt_value *plugin_paths; - bool omit_system_plugin_path; - bool omit_home_plugin_path; - bool command_needs_plugins; - const char *command_name; - 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; - } 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; - } print_ctf_metadata; - - /* BT_CONFIG_COMMAND_PRINT_LTTNG_LIVE_SESSIONS */ - struct { - GString *url; - } print_lttng_live_sessions; - } cmd_data; -}; - -static inline -struct bt_config_component *bt_config_get_component(GPtrArray *array, - size_t index) -{ - return bt_get(g_ptr_array_index(array, index)); -} - -struct bt_config *bt_config_from_args(int argc, const char *argv[], - int *retcode, bool omit_system_plugin_path, - bool omit_home_plugin_path, - struct bt_value *initial_plugin_paths); - -struct bt_config_component *bt_config_component_from_arg( - enum bt_component_class_type type, const char *arg); - -enum bt_value_status bt_config_append_plugin_paths( - struct bt_value *plugin_paths, const char *arg); - -#endif /* BABELTRACE_CONVERTER_CFG_H */ diff --git a/converter/babeltrace-log.c b/converter/babeltrace-log.c deleted file mode 100644 index 38cf8392..00000000 --- a/converter/babeltrace-log.c +++ /dev/null @@ -1,474 +0,0 @@ -/* - * babeltrace-log.c - * - * BabelTrace - Convert Text Log to CTF - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define NSEC_PER_USEC 1000UL -#define NSEC_PER_MSEC 1000000UL -#define NSEC_PER_SEC 1000000000ULL -#define USEC_PER_SEC 1000000UL - -bool babeltrace_debug, babeltrace_verbose; - -static char *s_outputname; -static int s_timestamp; -static int s_help; -static unsigned char s_uuid[BABELTRACE_UUID_LEN]; - -/* Metadata format string */ -static const char metadata_fmt[] = -"/* CTF 1.8 */\n" -"typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\n" -"typealias integer { size = 32; align = 32; signed = false; } := uint32_t;\n" -"typealias integer { size = 64; align = 64; signed = false; } := uint64_t;\n" -"\n" -"trace {\n" -" major = %u;\n" /* major (e.g. 0) */ -" minor = %u;\n" /* minor (e.g. 1) */ -" uuid = \"%s\";\n" /* UUID */ -" byte_order = %s;\n" /* be or le */ -" packet.header := struct {\n" -" uint32_t magic;\n" -" uint8_t uuid[16];\n" -" };\n" -"};\n" -"\n" -"stream {\n" -" packet.context := struct {\n" -" uint64_t content_size;\n" -" uint64_t packet_size;\n" -" };\n" -"%s" /* Stream event header (opt.) */ -"};\n" -"\n" -"event {\n" -" name = string;\n" -" fields := struct { string str; };\n" -"};\n"; - -static const char metadata_stream_event_header_timestamp[] = -" typealias integer { size = 64; align = 64; signed = false; } := uint64_t;\n" -" event.header := struct {\n" -" uint64_t timestamp;\n" -" };\n"; - -static -void print_metadata(FILE *fp) -{ - char uuid_str[BABELTRACE_UUID_STR_LEN]; - unsigned int major = 0, minor = 0; - int ret; - - ret = sscanf(VERSION, "%u.%u", &major, &minor); - if (ret != 2) - fprintf(stderr, "[warning] Incorrect babeltrace version format\n."); - bt_uuid_unparse(s_uuid, uuid_str); - fprintf(fp, metadata_fmt, - major, - minor, - uuid_str, - BYTE_ORDER == LITTLE_ENDIAN ? "le" : "be", - s_timestamp ? metadata_stream_event_header_timestamp : ""); -} - -static -void write_packet_header(struct ctf_stream_pos *pos, unsigned char *uuid) -{ - struct ctf_stream_pos dummy; - - /* magic */ - ctf_dummy_pos(pos, &dummy); - if (!ctf_align_pos(&dummy, sizeof(uint32_t) * CHAR_BIT)) - goto error; - if (!ctf_move_pos(&dummy, sizeof(uint32_t) * CHAR_BIT)) - goto error; - assert(!ctf_pos_packet(&dummy)); - - if (!ctf_align_pos(pos, sizeof(uint32_t) * CHAR_BIT)) - goto error; - *(uint32_t *) ctf_get_pos_addr(pos) = 0xC1FC1FC1; - if (!ctf_move_pos(pos, sizeof(uint32_t) * CHAR_BIT)) - goto error; - - /* uuid */ - ctf_dummy_pos(pos, &dummy); - if (!ctf_align_pos(&dummy, sizeof(uint8_t) * CHAR_BIT)) - goto error; - if (!ctf_move_pos(&dummy, 16 * CHAR_BIT)) - goto error; - assert(!ctf_pos_packet(&dummy)); - - if (!ctf_align_pos(pos, sizeof(uint8_t) * CHAR_BIT)) - goto error; - memcpy(ctf_get_pos_addr(pos), uuid, BABELTRACE_UUID_LEN); - if (!ctf_move_pos(pos, BABELTRACE_UUID_LEN * CHAR_BIT)) - goto error; - return; - -error: - fprintf(stderr, "[error] Out of packet bounds when writing packet header\n"); - abort(); -} - -static -void write_packet_context(struct ctf_stream_pos *pos) -{ - struct ctf_stream_pos dummy; - - /* content_size */ - ctf_dummy_pos(pos, &dummy); - if (!ctf_align_pos(&dummy, sizeof(uint64_t) * CHAR_BIT)) - goto error; - if (!ctf_move_pos(&dummy, sizeof(uint64_t) * CHAR_BIT)) - goto error; - assert(!ctf_pos_packet(&dummy)); - - if (!ctf_align_pos(pos, sizeof(uint64_t) * CHAR_BIT)) - goto error; - *(uint64_t *) ctf_get_pos_addr(pos) = ~0ULL; /* Not known yet */ - pos->content_size_loc = (uint64_t *) ctf_get_pos_addr(pos); - if (!ctf_move_pos(pos, sizeof(uint64_t) * CHAR_BIT)) - goto error; - - /* packet_size */ - ctf_dummy_pos(pos, &dummy); - if (!ctf_align_pos(&dummy, sizeof(uint64_t) * CHAR_BIT)) - goto error; - if (!ctf_move_pos(&dummy, sizeof(uint64_t) * CHAR_BIT)) - goto error; - assert(!ctf_pos_packet(&dummy)); - - if (!ctf_align_pos(pos, sizeof(uint64_t) * CHAR_BIT)) - goto error; - *(uint64_t *) ctf_get_pos_addr(pos) = pos->packet_size; - if (!ctf_move_pos(pos, sizeof(uint64_t) * CHAR_BIT)) - goto error; - return; - -error: - fprintf(stderr, "[error] Out of packet bounds when writing packet context\n"); - abort(); -} - -static -void write_event_header(struct ctf_stream_pos *pos, char *line, - char **tline, size_t len, size_t *tlen, - uint64_t *ts) -{ - if (!s_timestamp) - return; - - /* Only need to be executed on first pass (dummy) */ - if (pos->dummy) { - int has_timestamp = 0; - unsigned long sec, usec, msec; - unsigned int year, mon, mday, hour, min; - - /* Extract time from input line */ - if (sscanf(line, "[%lu.%lu] ", &sec, &usec) == 2) { - *ts = (uint64_t) sec * USEC_PER_SEC + (uint64_t) usec; - /* - * Default CTF clock has 1GHz frequency. Convert - * from usec to nsec. - */ - *ts *= NSEC_PER_USEC; - has_timestamp = 1; - } else if (sscanf(line, "[%u-%u-%u %u:%u:%lu.%lu] ", - &year, &mon, &mday, &hour, &min, - &sec, &msec) == 7) { - time_t ep_sec; - struct tm ti; - - memset(&ti, 0, sizeof(ti)); - ti.tm_year = year - 1900; /* from 1900 */ - ti.tm_mon = mon - 1; /* 0 to 11 */ - ti.tm_mday = mday; - ti.tm_hour = hour; - ti.tm_min = min; - ti.tm_sec = sec; - - ep_sec = bt_timegm(&ti); - if (ep_sec != (time_t) -1) { - *ts = (uint64_t) ep_sec * NSEC_PER_SEC - + (uint64_t) msec * NSEC_PER_MSEC; - } - has_timestamp = 1; - } - if (has_timestamp) { - *tline = strchr(line, ']'); - assert(*tline); - (*tline)++; - if ((*tline)[0] == ' ') { - (*tline)++; - } - *tlen = len + line - *tline; - } - } - /* timestamp */ - if (!ctf_align_pos(pos, sizeof(uint64_t) * CHAR_BIT)) - goto error; - if (!pos->dummy) - *(uint64_t *) ctf_get_pos_addr(pos) = *ts; - if (!ctf_move_pos(pos, sizeof(uint64_t) * CHAR_BIT)) - goto error; - return; - -error: - fprintf(stderr, "[error] Out of packet bounds when writing event header\n"); - abort(); -} - -static -void trace_string(char *line, struct ctf_stream_pos *pos, size_t len) -{ - struct ctf_stream_pos dummy; - int attempt = 0; - char *tline = line; /* tline is start of text, after timestamp */ - size_t tlen = len; - uint64_t ts = 0; - - printf_debug("read: %s\n", line); - - for (;;) { - int packet_filled = 0; - - ctf_dummy_pos(pos, &dummy); - write_event_header(&dummy, line, &tline, len, &tlen, &ts); - if (!ctf_align_pos(&dummy, sizeof(uint8_t) * CHAR_BIT)) - packet_filled = 1; - if (!ctf_move_pos(&dummy, tlen * CHAR_BIT)) - packet_filled = 1; - if (packet_filled || ctf_pos_packet(&dummy)) { - ctf_pos_pad_packet(pos); - write_packet_header(pos, s_uuid); - write_packet_context(pos); - if (attempt++ == 1) { - fprintf(stderr, "[Error] Line too large for packet size (%" PRIu64 "kB) (discarded)\n", - pos->packet_size / CHAR_BIT / 1024); - return; - } - continue; - } else { - break; - } - } - - write_event_header(pos, line, &tline, len, &tlen, &ts); - if (!ctf_align_pos(pos, sizeof(uint8_t) * CHAR_BIT)) - goto error; - memcpy(ctf_get_pos_addr(pos), tline, tlen); - if (!ctf_move_pos(pos, tlen * CHAR_BIT)) - goto error; - return; - -error: - fprintf(stderr, "[error] Out of packet bounds when writing event payload\n"); - abort(); -} - -static -void trace_text(FILE *input, int output) -{ - struct ctf_stream_pos pos; - ssize_t len; - char *line = NULL, *nl; - size_t linesize = 0; - int ret; - - memset(&pos, 0, sizeof(pos)); - ret = ctf_init_pos(&pos, NULL, output, O_RDWR); - if (ret) { - fprintf(stderr, "Error in ctf_init_pos\n"); - return; - } - ctf_packet_seek(&pos.parent, 0, SEEK_CUR); - write_packet_header(&pos, s_uuid); - write_packet_context(&pos); - for (;;) { - len = bt_getline(&line, &linesize, input); - if (len < 0) - break; - nl = strrchr(line, '\n'); - if (nl) { - *nl = '\0'; - trace_string(line, &pos, nl - line + 1); - } else { - trace_string(line, &pos, strlen(line) + 1); - } - } - ret = ctf_fini_pos(&pos); - if (ret) { - fprintf(stderr, "Error in ctf_fini_pos\n"); - } -} - -static -void usage(FILE *fp) -{ - fprintf(fp, "BabelTrace Log Converter %s\n", VERSION); - fprintf(fp, "\n"); - fprintf(fp, "Convert for a text log (read from standard input) to CTF.\n"); - fprintf(fp, "\n"); - fprintf(fp, "usage : babeltrace-log [OPTIONS] OUTPUT\n"); - fprintf(fp, "\n"); - fprintf(fp, " OUTPUT Output trace path\n"); - fprintf(fp, "\n"); - fprintf(fp, " -t With timestamps (format: [sec.usec] string\\n)\n"); - fprintf(fp, " (format: [YYYY-MM-DD HH:MM:SS.MS] string\\n)\n"); - fprintf(fp, "\n"); -} - -static -int parse_args(int argc, char **argv) -{ - int i; - - for (i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-t")) - s_timestamp = 1; - else if (!strcmp(argv[i], "-h")) { - s_help = 1; - return 0; - } else if (argv[i][0] == '-') - return -EINVAL; - else - s_outputname = argv[i]; - } - if (!s_outputname) - return -EINVAL; - return 0; -} - -int main(int argc, char **argv) -{ - int fd, metadata_fd, ret; - DIR *dir; - int dir_fd; - FILE *metadata_fp; - - ret = parse_args(argc, argv); - if (ret) { - fprintf(stderr, "Error: invalid argument.\n"); - usage(stderr); - goto error; - } - - if (s_help) { - usage(stdout); - exit(EXIT_SUCCESS); - } - - ret = g_mkdir(s_outputname, S_IRWXU|S_IRWXG); - if (ret) { - perror("g_mkdir"); - goto error; - } - - dir = opendir(s_outputname); - if (!dir) { - perror("opendir"); - goto error_rmdir; - } - dir_fd = bt_dirfd(dir); - if (dir_fd < 0) { - perror("dirfd"); - goto error_closedir; - } - - fd = openat(dir_fd, "datastream", O_RDWR|O_CREAT, - S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); - if (fd < 0) { - perror("openat"); - goto error_closedirfd; - } - - metadata_fd = openat(dir_fd, "metadata", O_RDWR|O_CREAT, - S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); - if (metadata_fd < 0) { - perror("openat"); - goto error_closedatastream; - } - metadata_fp = fdopen(metadata_fd, "w"); - if (!metadata_fp) { - perror("fdopen"); - goto error_closemetadatafd; - } - - bt_uuid_generate(s_uuid); - print_metadata(metadata_fp); - trace_text(stdin, fd); - - ret = close(fd); - if (ret) - perror("close"); - exit(EXIT_SUCCESS); - - /* error handling */ -error_closemetadatafd: - ret = close(metadata_fd); - if (ret) - perror("close"); -error_closedatastream: - ret = close(fd); - if (ret) - perror("close"); -error_closedirfd: - ret = close(dir_fd); - if (ret) - perror("close"); -error_closedir: - ret = closedir(dir); - if (ret) - perror("closedir"); -error_rmdir: - ret = rmdir(s_outputname); - if (ret) - perror("rmdir"); -error: - exit(EXIT_FAILURE); -} diff --git a/converter/babeltrace.c b/converter/babeltrace.c deleted file mode 100644 index cb8492f2..00000000 --- a/converter/babeltrace.c +++ /dev/null @@ -1,1290 +0,0 @@ -/* - * babeltrace.c - * - * Babeltrace Trace Converter - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "babeltrace-cfg.h" -#include "babeltrace-cfg-connect.h" -#include "default-cfg.h" - -GPtrArray *loaded_plugins; - -static -void init_loaded_plugins_array(void) -{ - loaded_plugins = g_ptr_array_new_full(8, bt_put); -} - -static -void fini_loaded_plugins_array(void) -{ - g_ptr_array_free(loaded_plugins, TRUE); -} - -static -struct bt_plugin *find_plugin(const char *name) -{ - int i; - struct bt_plugin *plugin = NULL; - - 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; - } - - return bt_get(plugin); -} - -static -struct bt_component_class *find_component_class(const char *plugin_name, - const char *comp_class_name, - enum bt_component_class_type comp_class_type) -{ - struct bt_component_class *comp_class = NULL; - struct bt_plugin *plugin = find_plugin(plugin_name); - - if (!plugin) { - goto end; - } - - comp_class = bt_plugin_get_component_class_by_name_and_type(plugin, - comp_class_name, comp_class_type); - BT_PUT(plugin); -end: - return comp_class; -} - -static -void print_indent(size_t indent) -{ - size_t i; - - for (i = 0; i < indent; i++) { - printf(" "); - } -} - -static char *escape_name_for_shell(const char *input) -{ - char *output = g_malloc0(strlen(input) * 5 + 1); - const char *in; - char *out = output; - - if (!output) { - goto end; - } - - for (in = input; *in != '\0'; in++) { - switch (*in) { - case '\\': - *out++ = '\\'; - *out++ = '\\'; - break; - case '\'': - *out++ = '\''; - *out++ = '"'; - *out++ = '\''; - *out++ = '"'; - *out++ = '\''; - break; - case '.': - *out++ = '\\'; - *out++ = '.'; - break; - default: - *out++ = *in; - break; - } - } - -end: - return output; -} - -static -const char *component_type_str(enum 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"; - case BT_COMPONENT_CLASS_TYPE_UNKNOWN: - default: - return "unknown"; - } -} - -static void print_plugin_comp_cls_opt(FILE *fh, const char *plugin_name, - const char *comp_cls_name, enum bt_component_class_type type) -{ - char *shell_plugin_name = NULL; - char *shell_comp_cls_name = NULL; - - shell_plugin_name = escape_name_for_shell(plugin_name); - if (!shell_plugin_name) { - goto end; - } - - shell_comp_cls_name = escape_name_for_shell(comp_cls_name); - if (!shell_comp_cls_name) { - goto end; - } - - fprintf(fh, "%s%s--%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_reset(), - bt_common_color_fg_default(), - bt_common_color_bold(), - bt_common_color_fg_blue(), - shell_plugin_name, - bt_common_color_fg_default(), - bt_common_color_fg_yellow(), - shell_comp_cls_name, - bt_common_color_reset()); - -end: - g_free(shell_plugin_name); - g_free(shell_comp_cls_name); -} - -static -void print_value(struct bt_value *, size_t); - -static -void print_value_rec(struct bt_value *, size_t); - -static -bool print_map_value(const char *key, struct bt_value *object, void *data) -{ - size_t *indent = data; - - print_indent(*indent); - printf("%s: ", key); - - if (bt_value_is_array(object) && - bt_value_array_is_empty(object)) { - printf("[ ]\n"); - return true; - } - - if (bt_value_is_map(object) && - bt_value_map_is_empty(object)) { - printf("{ }\n"); - return true; - } - - if (bt_value_is_array(object) || - bt_value_is_map(object)) { - printf("\n"); - } - - print_value_rec(object, *indent + 2); - return true; -} - -static -void print_value_rec(struct bt_value *value, size_t indent) -{ - bool bool_val; - int64_t int_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: - printf("%snull%s\n", bt_common_color_bold(), - bt_common_color_reset()); - break; - case BT_VALUE_TYPE_BOOL: - bt_value_bool_get(value, &bool_val); - printf("%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_INTEGER: - bt_value_integer_get(value, &int_val); - printf("%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_FLOAT: - bt_value_float_get(value, &dbl_val); - printf("%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: - bt_value_string_get(value, &str_val); - printf("%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_size(value); - assert(size >= 0); - - if (size == 0) { - print_indent(indent); - printf("[ ]\n"); - break; - } - - for (i = 0; i < size; i++) { - struct bt_value *element = - bt_value_array_get(value, i); - - assert(element); - print_indent(indent); - printf("- "); - - if (bt_value_is_array(element) && - bt_value_array_is_empty(element)) { - printf("[ ]\n"); - continue; - } - - if (bt_value_is_map(element) && - bt_value_map_is_empty(element)) { - printf("{ }\n"); - continue; - } - - if (bt_value_is_array(element) || - bt_value_is_map(element)) { - printf("\n"); - } - - print_value_rec(element, indent + 2); - BT_PUT(element); - } - break; - case BT_VALUE_TYPE_MAP: - if (bt_value_map_is_empty(value)) { - print_indent(indent); - printf("{ }\n"); - break; - } - - bt_value_map_foreach(value, print_map_value, &indent); - break; - default: - assert(false); - } -} - -static -void print_value(struct bt_value *value, size_t indent) -{ - if (!bt_value_is_array(value) && !bt_value_is_map(value)) { - print_indent(indent); - } - - print_value_rec(value, indent); -} - -static -void print_bt_config_component(struct bt_config_component *bt_config_component) -{ - printf(" "); - print_plugin_comp_cls_opt(stdout, bt_config_component->plugin_name->str, - bt_config_component->comp_cls_name->str, - bt_config_component->type); - printf(":\n"); - - if (bt_config_component->instance_name->len > 0) { - printf(" Name: %s\n", - bt_config_component->instance_name->str); - } - - printf(" Parameters:\n"); - print_value(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_PUT(cfg_component); - } -} - -static -void print_plugin_paths(struct bt_value *plugin_paths) -{ - printf(" Plugin paths:\n"); - print_value(plugin_paths, 4); -} - -static -void print_cfg_run(struct bt_config *cfg) -{ - size_t i; - - print_plugin_paths(cfg->plugin_paths); - printf(" Source component instances:\n"); - print_bt_config_components(cfg->cmd_data.run.sources); - - if (cfg->cmd_data.run.filters->len > 0) { - printf(" Filter component instances:\n"); - print_bt_config_components(cfg->cmd_data.run.filters); - } - - printf(" Sink component instances:\n"); - print_bt_config_components(cfg->cmd_data.run.sinks); - printf(" 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); - - printf(" %s%s%s -> %s%s%s\n", - cfg_connection->src_instance_name->str, - cfg_connection->src_port_name->len > 0 ? "." : "", - cfg_connection->src_port_name->str, - cfg_connection->dst_instance_name->str, - cfg_connection->dst_port_name->len > 0 ? "." : "", - cfg_connection->dst_port_name->str); - } -} - -static -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); - printf(" 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); - printf(" 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); - printf(" Object: `%s`\n", cfg->cmd_data.query.object->str); - printf(" Component class:\n"); - print_bt_config_component(cfg->cmd_data.query.cfg_component); -} - -static -void print_cfg(struct bt_config *cfg) -{ - if (!babeltrace_verbose) { - return; - } - - printf("Configuration:\n"); - printf(" Debug mode: %s\n", cfg->debug ? "yes" : "no"); - printf(" 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: - assert(false); - } -} - -static -struct bt_component *create_trimmer(struct bt_config_component *source_cfg) -{ - struct bt_component *trimmer = NULL; - struct bt_component_class *trimmer_class = NULL; - struct bt_value *trimmer_params = NULL; - struct bt_value *value; - - trimmer_params = bt_value_map_create(); - if (!trimmer_params) { - goto end; - } - - value = bt_value_map_get(source_cfg->params, "begin"); - if (value) { - enum bt_value_status ret; - - ret = bt_value_map_insert(trimmer_params, "begin", - value); - BT_PUT(value); - if (ret) { - goto end; - } - } - value = bt_value_map_get(source_cfg->params, "end"); - if (value) { - enum bt_value_status ret; - - ret = bt_value_map_insert(trimmer_params, "end", - value); - BT_PUT(value); - if (ret) { - goto end; - } - } - value = bt_value_map_get(source_cfg->params, "clock-gmt"); - if (value) { - enum bt_value_status ret; - - ret = bt_value_map_insert(trimmer_params, "clock-gmt", - value); - BT_PUT(value); - if (ret) { - goto end; - } - } - - trimmer_class = find_component_class("utils", "trimmer", - BT_COMPONENT_CLASS_TYPE_FILTER); - if (!trimmer_class) { - fprintf(stderr, "Could not find trimmer component class. Aborting...\n"); - goto end; - } - trimmer = bt_component_create(trimmer_class, "source_trimmer", - trimmer_params); - if (!trimmer) { - goto end; - } -end: - bt_put(trimmer_params); - bt_put(trimmer_class); - return trimmer; -} - -static -int connect_source_sink(struct bt_graph *graph, - struct bt_component *source, - struct bt_config_component *source_cfg, - struct bt_component *sink) -{ - int ret = 0; - struct bt_connection *connection = NULL; - struct bt_component *trimmer = NULL; - struct bt_port *source_port = - bt_component_source_get_default_output_port(source); - struct bt_port *sink_port = - bt_component_sink_get_default_input_port(sink); - struct bt_port *to_sink_port = NULL; - struct bt_port *trimmer_input_port = NULL; - - if (!source_port) { - fprintf(stderr, "Failed to find default source output port. Aborting...\n"); - ret = -1; - goto end; - } - if (!sink_port) { - fprintf(stderr, "Failed to find default sink input port. Aborting...\n"); - ret = -1; - goto end; - } - - if (bt_value_map_has_key(source_cfg->params, "begin") - || bt_value_map_has_key(source_cfg->params, "end")) { - /* A trimmer must be inserted in the graph. */ - trimmer = create_trimmer(source_cfg); - if (!trimmer) { - fprintf(stderr, "Failed to create trimmer component. Aborting...\n"); - ret = -1; - goto end; - } - - trimmer_input_port = bt_component_filter_get_default_input_port( - trimmer); - if (!trimmer_input_port) { - fprintf(stderr, "Failed to find trimmer input port. Aborting...\n"); - ret = -1; - goto end; - } - to_sink_port = bt_component_filter_get_default_output_port( - trimmer); - if (!to_sink_port) { - fprintf(stderr, "Failed to find trimmer output port. Aborting...\n"); - ret = -1; - goto end; - } - - connection = bt_graph_connect_ports(graph, source_port, - trimmer_input_port); - if (!connection) { - fprintf(stderr, "Failed to connect source to trimmer. Aborting...\n"); - ret = -1; - goto end; - } - BT_PUT(connection); - } else { - BT_MOVE(to_sink_port, source_port); - } - - connection = bt_graph_connect_ports(graph, to_sink_port, sink_port); - if (!connection) { - fprintf(stderr, "Failed to connect to sink. Aborting...\n"); - ret = -1; - goto end; - } -end: - bt_put(trimmer); - bt_put(source_port); - bt_put(sink_port); - bt_put(to_sink_port); - bt_put(connection); - return ret; -} - -static -void add_to_loaded_plugins(struct bt_plugin **plugins) -{ - while (*plugins) { - struct bt_plugin *plugin = *plugins; - /* Check if it's already loaded (from another path). */ - struct bt_plugin *loaded_plugin = - find_plugin(bt_plugin_get_name(plugin)); - - if (loaded_plugin) { - printf_verbose("Not loading plugin `%s`: already loaded from `%s`\n", - bt_plugin_get_path(plugin), - bt_plugin_get_path(loaded_plugin)); - BT_PUT(loaded_plugin); - BT_PUT(plugin); - } else { - /* Transfer ownership to global array. */ - g_ptr_array_add(loaded_plugins, plugin); - } - *(plugins++) = NULL; - } -} - -static -int load_dynamic_plugins(struct bt_value *plugin_paths) -{ - int nr_paths, i, ret = 0; - - nr_paths = bt_value_array_size(plugin_paths); - if (nr_paths < 0) { - ret = -1; - goto end; - } - - for (i = 0; i < nr_paths; i++) { - struct bt_value *plugin_path_value = NULL; - const char *plugin_path; - struct bt_plugin **plugins; - - plugin_path_value = bt_value_array_get(plugin_paths, i); - if (bt_value_string_get(plugin_path_value, - &plugin_path)) { - BT_PUT(plugin_path_value); - continue; - } - - plugins = bt_plugin_create_all_from_dir(plugin_path, false); - if (!plugins) { - printf_debug("Unable to dynamically load plugins from path %s.\n", - plugin_path); - BT_PUT(plugin_path_value); - continue; - } - - add_to_loaded_plugins(plugins); - free(plugins); - - BT_PUT(plugin_path_value); - } -end: - return ret; -} - -static -int load_static_plugins(void) -{ - int ret = 0; - struct bt_plugin **plugins; - - plugins = bt_plugin_create_all_from_static(); - if (!plugins) { - printf_debug("Unable to load static plugins.\n"); - ret = -1; - goto end; - } - - add_to_loaded_plugins(plugins); - free(plugins); -end: - return ret; -} - -static int load_all_plugins(struct bt_value *plugin_paths) -{ - int ret = 0; - - if (load_dynamic_plugins(plugin_paths)) { - fprintf(stderr, "Failed to load dynamic plugins.\n"); - ret = -1; - goto end; - } - - if (load_static_plugins()) { - fprintf(stderr, "Failed to load static plugins.\n"); - ret = -1; - goto end; - } - -end: - return ret; -} - -static void print_plugin_info(struct bt_plugin *plugin) -{ - unsigned int major, minor, patch; - const char *extra; - enum bt_plugin_status version_status; - 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_status = 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()); - printf(" %sPath%s: %s\n", bt_common_color_bold(), - bt_common_color_reset(), path ? path : "(None)"); - - if (version_status == BT_PLUGIN_STATUS_OK) { - 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; - struct bt_component_class *comp_cls = NULL; - struct bt_value *results = NULL; - - ret = load_all_plugins(cfg->plugin_paths); - if (ret) { - goto end; - } - - 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) { - 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; - } - - results = bt_component_class_query(comp_cls, - cfg->cmd_data.query.object->str, - cfg->cmd_data.query.cfg_component->params); - if (!results) { - 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\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - cfg->cmd_data.query.object->str, - bt_common_color_reset()); - ret = -1; - goto end; - } - - print_value(results, 0); - -end: - bt_put(comp_cls); - bt_put(results); - return ret; -} - -static int cmd_help(struct bt_config *cfg) -{ - int ret; - struct bt_plugin *plugin = NULL; - size_t i; - - ret = load_all_plugins(cfg->plugin_paths); - if (ret) { - goto end; - } - - plugin = find_plugin(cfg->cmd_data.help.cfg_component->plugin_name->str); - if (!plugin) { - 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(" %sComponent classes%s: %d\n", - bt_common_color_bold(), - bt_common_color_reset(), - bt_plugin_get_component_class_count(plugin)); - - - if (cfg->cmd_data.help.cfg_component->type != - BT_COMPONENT_CLASS_TYPE_UNKNOWN) { - struct bt_component_class *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) { - 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; - } - - bt_put(needed_comp_cls); - } - - for (i = 0; i < bt_plugin_get_component_class_count(plugin); i++) { - struct bt_component_class *comp_cls = - bt_plugin_get_component_class(plugin, i); - 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); - enum bt_component_class_type type = - bt_component_class_get_type(comp_cls); - - assert(comp_cls); - - if (cfg->cmd_data.help.cfg_component->type != - BT_COMPONENT_CLASS_TYPE_UNKNOWN) { - if (strcmp(cfg->cmd_data.help.cfg_component->comp_cls_name->str, - comp_class_name) != 0 && - type == - cfg->cmd_data.help.cfg_component->type) { - bt_put(comp_cls); - continue; - } - } - - printf("\n"); - print_plugin_comp_cls_opt(stdout, - cfg->cmd_data.help.cfg_component->plugin_name->str, - 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); - } - - bt_put(comp_cls); - } - -end: - bt_put(plugin); - return ret; -} - -static int cmd_list_plugins(struct bt_config *cfg) -{ - int ret; - int plugins_count, component_classes_count = 0, i; - - ret = load_all_plugins(cfg->plugin_paths); - if (ret) { - goto end; - } - - printf("From the following plugin paths:\n\n"); - print_value(cfg->plugin_paths, 2); - printf("\n"); - plugins_count = loaded_plugins->len; - if (plugins_count == 0) { - fprintf(stderr, "%s%sNo plugins found.%s\n", - bt_common_color_bold(), bt_common_color_fg_red(), - bt_common_color_reset()); - fprintf(stderr, "\n"); - fprintf(stderr, "Please make sure your plugin search path is set correctly. You can use\n"); - fprintf(stderr, "the --plugin-path command-line option or the BABELTRACE_PLUGIN_PATH\n"); - fprintf(stderr, "environment variable.\n"); - ret = -1; - goto end; - } - - for (i = 0; i < plugins_count; i++) { - struct bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); - - component_classes_count += bt_plugin_get_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++) { - int j; - struct bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); - - component_classes_count = - bt_plugin_get_component_class_count(plugin); - printf("\n"); - print_plugin_info(plugin); - - if (component_classes_count == 0) { - printf(" %sComponent classes%s: (None)\n", - bt_common_color_bold(), - bt_common_color_reset()); - } else { - printf(" %sComponent classes%s:\n", - bt_common_color_bold(), - bt_common_color_reset()); - } - - for (j = 0; j < component_classes_count; j++) { - struct bt_component_class *comp_class = - bt_plugin_get_component_class(plugin, j); - const char *comp_class_name = - bt_component_class_get_name(comp_class); - const char *comp_class_description = - bt_component_class_get_description(comp_class); - enum 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"); - bt_put(comp_class); - } - } - -end: - return ret; -} - -static int cmd_print_lttng_live_sessions(struct bt_config *cfg) -{ - printf("TODO\n"); - return -1; -} - -static int cmd_print_ctf_metadata(struct bt_config *cfg) -{ - int ret = 0; - struct bt_component_class *comp_cls = NULL; - struct bt_value *results = NULL; - struct bt_value *params = NULL; - struct 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 enum bt_component_class_type comp_cls_type = - BT_COMPONENT_CLASS_TYPE_SOURCE; - - assert(cfg->cmd_data.print_ctf_metadata.path); - comp_cls = find_component_class(plugin_name, comp_cls_name, - comp_cls_type); - if (!comp_cls) { - 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(params, "path", - cfg->cmd_data.print_ctf_metadata.path->str); - if (ret) { - ret = -1; - goto end; - } - - results = bt_component_class_query(comp_cls, "metadata-info", - params); - if (!results) { - ret = -1; - fprintf(stderr, "%s%sFailed to request metadata info%s\n", - bt_common_color_bold(), - bt_common_color_fg_red(), - bt_common_color_reset()); - goto end; - } - - metadata_text_value = bt_value_map_get(results, "text"); - if (!metadata_text_value) { - ret = -1; - goto end; - } - - ret = bt_value_string_get(metadata_text_value, &metadata_text); - assert(ret == 0); - printf("%s\n", metadata_text); - -end: - bt_put(results); - bt_put(params); - bt_put(metadata_text_value); - bt_put(comp_cls); - return 0; -} - -static int cmd_run(struct bt_config *cfg) -{ - int ret = 0; - struct bt_component_class *source_class = NULL; - struct bt_component_class *sink_class = NULL; - struct bt_component *source = NULL, *sink = NULL; - struct bt_value *source_params = NULL, *sink_params = NULL; - struct bt_config_component *source_cfg = NULL, *sink_cfg = NULL; - struct bt_graph *graph = NULL; - - ret = load_all_plugins(cfg->plugin_paths); - if (ret) { - goto end; - } - - /* TODO handle more than 1 source and 1 sink. */ - if (cfg->cmd_data.run.sources->len != 1 || - cfg->cmd_data.run.sinks->len != 1) { - fprintf(stderr, "Only one source and one sink component class are supported. Aborting...\n"); - ret = -1; - goto end; - } - - source_cfg = bt_config_get_component(cfg->cmd_data.run.sources, 0); - source_params = bt_get(source_cfg->params); - source_class = find_component_class(source_cfg->plugin_name->str, - source_cfg->comp_cls_name->str, - BT_COMPONENT_CLASS_TYPE_SOURCE); - if (!source_class) { - fprintf(stderr, "Could not find "); - print_plugin_comp_cls_opt(stderr, source_cfg->plugin_name->str, - source_cfg->comp_cls_name->str, BT_COMPONENT_CLASS_TYPE_SOURCE); - fprintf(stderr, ". Aborting...\n"); - ret = -1; - goto end; - } - - sink_cfg = bt_config_get_component(cfg->cmd_data.run.sinks, 0); - sink_params = bt_get(sink_cfg->params); - sink_class = find_component_class(sink_cfg->plugin_name->str, - sink_cfg->comp_cls_name->str, - BT_COMPONENT_CLASS_TYPE_SINK); - if (!sink_class) { - fprintf(stderr, "Could not find "); - print_plugin_comp_cls_opt(stderr, sink_cfg->plugin_name->str, - sink_cfg->comp_cls_name->str, BT_COMPONENT_CLASS_TYPE_SINK); - fprintf(stderr, ". Aborting...\n"); - ret = -1; - goto end; - } - - graph = bt_graph_create(); - if (!graph) { - ret = -1; - goto end; - } - - source = bt_component_create(source_class, "source", source_params); - if (!source) { - fprintf(stderr, "Failed to instantiate selected source component. Aborting...\n"); - ret = -1; - goto end; - } - - sink = bt_component_create(sink_class, "sink", sink_params); - if (!sink) { - fprintf(stderr, "Failed to instantiate selected output component. Aborting...\n"); - ret = -1; - goto end; - } - - ret = connect_source_sink(graph, source, source_cfg, sink); - if (ret) { - ret = -1; - goto end; - } - - while (true) { - enum bt_graph_status graph_status; - - graph_status = bt_graph_run(graph); - switch (graph_status) { - case BT_GRAPH_STATUS_AGAIN: - /* Wait for an arbitraty 500 ms. */ - usleep(500000); - break; - case BT_COMPONENT_STATUS_END: - goto end; - default: - fprintf(stderr, "Sink component returned an error, aborting...\n"); - ret = -1; - goto end; - } - } - -end: - bt_put(sink_class); - bt_put(source_class); - bt_put(source); - bt_put(sink); - bt_put(source_params); - bt_put(sink_params); - bt_put(sink_cfg); - bt_put(source_cfg); - bt_put(graph); - return ret; -} - -static void warn_command_name_and_directory_clash(struct bt_config *cfg) -{ - if (!cfg->command_name) { - 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); - } -} - -int main(int argc, const char **argv) -{ - int ret; - int retcode; - struct bt_config *cfg; - - init_loaded_plugins_array(); - cfg = bt_config_from_args_with_defaults(argc, argv, &retcode); - - if (retcode < 0) { - /* Quit without errors; typically usage/version */ - retcode = 0; - goto end; - } - - if (retcode > 0) { - goto end; - } - - if (!cfg) { - fprintf(stderr, "Failed to create Babeltrace configuration\n"); - retcode = 1; - goto end; - } - - babeltrace_debug = cfg->debug; - babeltrace_verbose = cfg->verbose; - print_cfg(cfg); - - if (cfg->command_needs_plugins) { - ret = load_all_plugins(cfg->plugin_paths); - if (ret) { - retcode = 1; - goto end; - } - } - - 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: - assert(false); - } - - warn_command_name_and_directory_clash(cfg); - retcode = ret ? 1 : 0; - -end: - BT_PUT(cfg); - fini_loaded_plugins_array(); - return retcode; -} diff --git a/converter/default-cfg.c b/converter/default-cfg.c deleted file mode 100644 index fb325431..00000000 --- a/converter/default-cfg.c +++ /dev/null @@ -1,75 +0,0 @@ -/* - * default-cfg.c - * - * Babeltrace Trace Converter - Default Configuration - * - * Copyright 2016 - Jérémie Galarneau - * - * Author: 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 -#include "babeltrace-cfg.h" -#include "default-cfg.h" -#include "config.h" - -#ifdef BT_SET_DEFAULT_IN_TREE_CONFIGURATION - -struct bt_config *bt_config_from_args_with_defaults(int argc, - const char *argv[], int *retcode) -{ - struct 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_from_args(argc, argv, retcode, true, true, - initial_plugin_paths); - goto end; - -error: - *retcode = 1; - BT_PUT(cfg); - -end: - bt_put(initial_plugin_paths); - return cfg; -} - -#else /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ - -struct bt_config *bt_config_from_args_with_defaults(int argc, - const char *argv[], int *retcode) -{ - return bt_config_from_args(argc, argv, retcode, false, false, NULL); -} - -#endif /* BT_SET_DEFAULT_IN_TREE_CONFIGURATION */ diff --git a/converter/default-cfg.h b/converter/default-cfg.h deleted file mode 100644 index d6b91888..00000000 --- a/converter/default-cfg.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef BABELTRACE_DEFAULT_CFG_H -#define BABELTRACE_DEFAULT_CFG_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_from_args_with_defaults(int argc, - const char *argv[], int *retcode); - -#endif /* BABELTRACE_DEFAULT_CFG_H */ diff --git a/tests/bin/intersection/test_intersection.in b/tests/bin/intersection/test_intersection.in index 689fbc1e..d70367f2 100644 --- a/tests/bin/intersection/test_intersection.in +++ b/tests/bin/intersection/test_intersection.in @@ -17,7 +17,7 @@ TESTDIR=@abs_top_srcdir@/tests -BABELTRACE_BIN=@abs_top_builddir@/converter/babeltrace +BABELTRACE_BIN=@abs_top_builddir@/cli/babeltrace CTF_TRACES=@abs_top_srcdir@/tests/ctf-traces source $TESTDIR/utils/tap/tap.sh diff --git a/tests/bin/test_convert_args b/tests/bin/test_convert_args index d134ea7e..e8df7a99 100755 --- a/tests/bin/test_convert_args +++ b/tests/bin/test_convert_args @@ -17,7 +17,7 @@ curdir="$(dirname $0)" testdir="$curdir/.." -babeltrace_bin="$curdir/../../converter/babeltrace" +babeltrace_bin="$curdir/../../cli/babeltrace" source $testdir/utils/tap/tap.sh diff --git a/tests/bin/test_formats.in b/tests/bin/test_formats.in index 0d91898b..4bd32ea0 100644 --- a/tests/bin/test_formats.in +++ b/tests/bin/test_formats.in @@ -18,7 +18,7 @@ CURDIR=$(dirname $0) TESTDIR=$CURDIR/.. -BABELTRACE_BIN=$CURDIR/../../converter/babeltrace +BABELTRACE_BIN=$CURDIR/../../cli/babeltrace source $TESTDIR/utils/tap/tap.sh diff --git a/tests/bin/test_packet_seq_num.in b/tests/bin/test_packet_seq_num.in index e8e291cb..640793b7 100644 --- a/tests/bin/test_packet_seq_num.in +++ b/tests/bin/test_packet_seq_num.in @@ -18,7 +18,7 @@ CURDIR=$(dirname $0) TESTDIR=$CURDIR/.. -BABELTRACE_BIN=$CURDIR/../../converter/babeltrace +BABELTRACE_BIN=$CURDIR/../../cli/babeltrace CTF_TRACES=@abs_top_srcdir@/tests/ctf-traces source $TESTDIR/utils/tap/tap.sh diff --git a/tests/bin/test_trace_read.in b/tests/bin/test_trace_read.in index 758e8ce9..1325eb39 100644 --- a/tests/bin/test_trace_read.in +++ b/tests/bin/test_trace_read.in @@ -18,7 +18,7 @@ CURDIR=$(dirname $0) TESTDIR=$CURDIR/.. -BABELTRACE_BIN=$CURDIR/../../converter/babeltrace +BABELTRACE_BIN=$CURDIR/../../cli/babeltrace CTF_TRACES=@abs_top_srcdir@/tests/ctf-traces diff --git a/tests/lib/test_ctf_writer_complete.in b/tests/lib/test_ctf_writer_complete.in index 219a2f9f..0911e09b 100755 --- a/tests/lib/test_ctf_writer_complete.in +++ b/tests/lib/test_ctf_writer_complete.in @@ -17,6 +17,6 @@ # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # -BTBIN="@abs_top_builddir@/converter/babeltrace" +BTBIN="@abs_top_builddir@/cli/babeltrace" "@abs_top_builddir@/tests/lib/test_ctf_writer" "$BTBIN"