From 290725f719d3d1a8896b73334736d47c5c935040 Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Thu, 9 Feb 2017 14:42:57 -0500 Subject: [PATCH] Make babeltrace(1)'s CLI Git-like and implement the list-plugins command MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This patch adds a command concept to the babeltrace(1) program, so that the usage becomes: babeltrace [GENERAL OPTIONS] [COMMAND] [COMMAND OPTIONS] The idea is to support multiple actions performed by the same program without having to deal with a single command-line option parsing phase. The default command is `convert`: it builds a trace conversion graph and runs it. Therefore the current behaviour without a command name remains the same. The general command-line options, which can apply to all commands, are: -d, --debug Enable debug mode -h --help Show general help and quit --help-legacy Show Babeltrace 1.x legacy help and quit -v, --verbose Enable verbose output -V, --version Show version and quit You can get the usage of a specific command with: babeltrace COMMAND --help Because there is a default command, the parsing of the general options is done manually to catch the first unknown option OR positional argument. If an unknown option is found, all the arguments are given back to the convert command. This is why the convert command also parses --verbose, for example, because of this situation: babeltrace --debug --sink=my.sink --verbose Here, the general options parsing stops at --sink because it's unknown, and no explicit command name was found so far, so everything starting with --debug is given to the convert command. I also implemented the `list-plugins` command which lists plugins, their component classes, and their properties: babeltrace list-plugins You can still specify a plugin path or use the BABELTRACE_PLUGIN_PATH environment variable with this command: babeltrace list-plugins --plugin-path=/path/to/other/plugins I added color support in libbabeltrace-common and used it in the `list-plugins` command. Color codes are only printed when color support is detected, that is, when the standard output is connected to a color-compatible terminal. I also improved the way bt_value objects are printed with print_value() in babeltrace.c to make it look more YAML-ish. This is easier on the eyes. This patch introduces a backward compatibility break in the very specific scenario where an explicit command name is used and a directory in the CWD exists with this name: babeltrace list-plugins If `list-plugins` is a directory in the CWD, the legacy behaviour is to recursively find CTF traces in it. With this patch, plugins are listed. However a message is printed at the end of the command in this case: NOTE: The `list-plugins` command was executed. If you meant to convert a trace located in the local `list-plugins` directory, please use: babeltrace convert list-plugins [OPTIONS] In other words, `babeltrace convert` (with 2.x) is a drop-in replacement of `babeltrace` (with 1.x). Signed-off-by: Philippe Proulx Signed-off-by: Jérémie Galarneau --- common/common.c | 143 ++++ converter/babeltrace-cfg.c | 1003 ++++++++++++++++++-------- converter/babeltrace-cfg.h | 48 +- converter/babeltrace.c | 506 ++++++++----- converter/default-cfg.c | 36 +- converter/default-cfg.h | 3 +- include/babeltrace/common-internal.h | 54 ++ 7 files changed, 1289 insertions(+), 504 deletions(-) diff --git a/common/common.c b/common/common.c index 47186e16..379b1301 100644 --- a/common/common.c +++ b/common/common.c @@ -22,6 +22,7 @@ * SOFTWARE. */ +#include #include #include #include @@ -163,3 +164,145 @@ error: end: return ret; } + +static bool supports_colors(void) +{ + static bool supports_colors = false; + static bool supports_colors_set = false; + const char *term; + + if (supports_colors_set) { + goto end; + } + + supports_colors_set = true; + + term = getenv("TERM"); + if (!term) { + goto end; + } + + if (strncmp(term, "xterm", 5) != 0 && + strncmp(term, "rxvt", 4) != 0 && + strncmp(term, "konsole", 7) != 0 && + strncmp(term, "gnome", 5) != 0) { + goto end; + } + + if (!isatty(1)) { + goto end; + } + + supports_colors = true; + +end: + return supports_colors; +} + +BT_HIDDEN +const char *bt_common_color_reset(void) +{ + return supports_colors() ? "\033[0m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_bold(void) +{ + return supports_colors() ? "\033[1m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_fg_default(void) +{ + return supports_colors() ? "\033[39m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_fg_red(void) +{ + return supports_colors() ? "\033[31m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_fg_green(void) +{ + return supports_colors() ? "\033[32m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_fg_yellow(void) +{ + return supports_colors() ? "\033[33m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_fg_blue(void) +{ + return supports_colors() ? "\033[34m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_fg_magenta(void) +{ + return supports_colors() ? "\033[35m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_fg_cyan(void) +{ + return supports_colors() ? "\033[36m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_fg_light_gray(void) +{ + return supports_colors() ? "\033[37m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_bg_default(void) +{ + return supports_colors() ? "\033[49m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_bg_red(void) +{ + return supports_colors() ? "\033[41m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_bg_green(void) +{ + return supports_colors() ? "\033[42m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_bg_yellow(void) +{ + return supports_colors() ? "\033[43m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_bg_blue(void) +{ + return supports_colors() ? "\033[44m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_bg_magenta(void) +{ + return supports_colors() ? "\033[45m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_bg_cyan(void) +{ + return supports_colors() ? "\033[46m" : ""; +} + +BT_HIDDEN +const char *bt_common_color_bg_light_gray(void) +{ + return supports_colors() ? "\033[47m" : ""; +} diff --git a/converter/babeltrace-cfg.c b/converter/babeltrace-cfg.c index f045bf94..1d4e7537 100644 --- a/converter/babeltrace-cfg.c +++ b/converter/babeltrace-cfg.c @@ -691,143 +691,6 @@ void print_version(void) puts("Babeltrace " VERSION); } -/* - * Prints the legacy, Babeltrace 1.x command usage. Those options are - * still compatible in Babeltrace 2.x, but it is recommended to use - * the more generic plugin/component parameters instead of those - * hard-coded option names. - */ -static -void print_legacy_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace [OPTIONS] INPUT...\n"); - fprintf(fp, "\n"); - fprintf(fp, "The following options are compatible with the Babeltrace 1.x options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " --help-legacy Show this help\n"); - fprintf(fp, " -V, --version Show version\n"); - fprintf(fp, " --clock-force-correlate Assume that clocks are inherently correlated\n"); - fprintf(fp, " across traces\n"); - fprintf(fp, " -d, --debug Enable debug mode\n"); - fprintf(fp, " -i, --input-format=FORMAT Input trace format (default: ctf)\n"); - fprintf(fp, " -l, --list List available formats\n"); - fprintf(fp, " -o, --output-format=FORMAT Output trace format (default: text)\n"); - fprintf(fp, " -v, --verbose Enable verbose output\n"); - fprintf(fp, "\n"); - fprintf(fp, " Available input formats: ctf, lttng-live, ctf-metadata\n"); - fprintf(fp, " Available output formats: text, dummy\n"); - fprintf(fp, "\n"); - fprintf(fp, "Input formats specific options:\n"); - fprintf(fp, "\n"); - fprintf(fp, " INPUT... Input trace file(s), directory(ies), or URLs\n"); - fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n"); - fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS nanoseconds\n"); - fprintf(fp, " --stream-intersection Only process events when all streams are active\n"); - fprintf(fp, "\n"); - fprintf(fp, "text output format specific 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 GMT time zone\n"); - fprintf(fp, " (default: local time zone)\n"); - fprintf(fp, " --clock-seconds Print the timestamps as [SEC.NS]\n"); - fprintf(fp, " (default format: [HH:MM:SS.NS])\n"); - fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n"); - fprintf(fp, " (default: `/usr/lib/debug`)\n"); - fprintf(fp, " --debug-info-full-path Show full debug info source and binary paths\n"); - fprintf(fp, " --debug-info-target-prefix=DIR Use directory DIR as a prefix when looking\n"); - fprintf(fp, " up executables during debug info analysis\n"); - fprintf(fp, " (default: `/usr/lib/debug`)\n"); - fprintf(fp, " -f, --fields=NAME[,NAME]... Print additional fields:\n"); - fprintf(fp, " all, trace, trace:hostname, trace:domain,\n"); - fprintf(fp, " trace:procname, trace:vpid, loglevel, emf\n"); - fprintf(fp, " (default: trace:hostname, trace:procname,\n"); - fprintf(fp, " trace:vpid)\n"); - fprintf(fp, " -n, --names=NAME[,NAME]... Print field names:\n"); - fprintf(fp, " payload (or arg or args)\n"); - fprintf(fp, " none, all, scope, header, context (or ctx)\n"); - fprintf(fp, " (default: payload, context)\n"); - fprintf(fp, " --no-delta Do not print time delta between consecutive\n"); - fprintf(fp, " events\n"); - fprintf(fp, " -w, --output=PATH Write output to PATH (default: standard output)\n"); -} - -/* - * Prints the Babeltrace 2.x usage. - */ -static -void print_usage(FILE *fp) -{ - fprintf(fp, "Usage: babeltrace [OPTIONS]\n"); - fprintf(fp, "\n"); - fprintf(fp, " -b, --base-params=PARAMS Set PARAMS as the current base parameters\n"); - fprintf(fp, " of the following source and sink component\n"); - fprintf(fp, " instances (see the exact format of PARAMS\n"); - fprintf(fp, " below)\n"); - fprintf(fp, " --begin=BEGIN Set beginning time to BEGIN\n"); - fprintf(fp, " (format: [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn])\n"); - fprintf(fp, " -d, --debug Enable debug mode\n"); - fprintf(fp, " --end=END Set end time to END\n"); - fprintf(fp, " (format: [YYYY-MM-DD [hh:mm:]]ss[.nnnnnnnnn])\n"); - fprintf(fp, " -l, --list List available plugins and their 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 Set the parameters of the latest source or\n"); - fprintf(fp, " sink component instance (in command-line \n"); - fprintf(fp, " order) to PARAMS (see the exact format of\n"); - fprintf(fp, " PARAMS below)\n"); - fprintf(fp, " -P, --path=PATH Set the `path` parameter of the latest source\n"); - fprintf(fp, " or sink component to PATH\n"); - fprintf(fp, " --plugin-path=PATH[:PATH]... Add PATH to the list of paths from which dynamic\n"); - fprintf(fp, " plugins can be loaded\n"); - fprintf(fp, " -r, --reset-base-params Reset the current base parameters of the\n"); - fprintf(fp, " following source and sink component\n"); - fprintf(fp, " instances to an empty map\n"); - fprintf(fp, " -o, --sink=PLUGIN.COMPCLS Instantiate a sink component from plugin\n"); - fprintf(fp, " PLUGIN and component class COMPCLS (may be\n"); - fprintf(fp, " repeated)\n"); - fprintf(fp, " -i, --source=PLUGIN.COMPCLS Instantiate a source component from plugin\n"); - fprintf(fp, " PLUGIN and component class COMPCLS (may be\n"); - fprintf(fp, " repeated)\n"); - fprintf(fp, " --timerange=TIMERANGE Set time range to TIMERANGE: BEGIN,END or\n"); - fprintf(fp, " [BEGIN,END] (where [ and ] are actual brackets)\n"); - fprintf(fp, " -h --help Show this help\n"); - fprintf(fp, " --help-legacy Show Babeltrace 1.x legacy options\n"); - fprintf(fp, " -v, --verbose Enable verbose output\n"); - fprintf(fp, " -V, --version Show version\n"); - fprintf(fp, "\n\n"); - fprintf(fp, "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 [:.-] characters), and\n"); - fprintf(fp, "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, "Whitespaces are allowed around individual `=` and `,` tokens.\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"); -} - /* * Destroys a component configuration. */ @@ -944,23 +807,35 @@ end: static void bt_config_destroy(struct bt_object *obj) { - struct bt_config *bt_config = + struct bt_config *cfg = container_of(obj, struct bt_config, base); if (!obj) { goto end; } - if (bt_config->sources) { - g_ptr_array_free(bt_config->sources, TRUE); - } + switch (cfg->command) { + case BT_CONFIG_COMMAND_CONVERT: + if (cfg->cmd_data.convert.sources) { + g_ptr_array_free(cfg->cmd_data.convert.sources, TRUE); + } - if (bt_config->sinks) { - g_ptr_array_free(bt_config->sinks, TRUE); + if (cfg->cmd_data.convert.sinks) { + g_ptr_array_free(cfg->cmd_data.convert.sinks, TRUE); + } + + BT_PUT(cfg->cmd_data.convert.plugin_paths); + break; + + case BT_CONFIG_COMMAND_LIST_PLUGINS: + BT_PUT(cfg->cmd_data.list_plugins.plugin_paths); + break; + + default: + assert(false); } - BT_PUT(bt_config->plugin_paths); - g_free(bt_config); + g_free(cfg); end: return; @@ -2022,7 +1897,7 @@ bool validate_cfg(struct bt_config *cfg, } /* Make sure no non-legacy sources are specified */ - if (cfg->sources->len != 0) { + if (cfg->cmd_data.convert.sources->len != 0) { print_input_legacy_to_sources(*legacy_input_format, legacy_input_paths, ctf_legacy_opts); goto error; @@ -2050,7 +1925,7 @@ bool validate_cfg(struct bt_config *cfg, } /* Make sure no non-legacy sinks are specified */ - if (cfg->sinks->len != 0) { + if (cfg->cmd_data.convert.sinks->len != 0) { print_output_legacy_to_sinks(*legacy_output_format, text_legacy_opts); goto error; @@ -2111,15 +1986,16 @@ enum { OPT_END, OPT_FIELDS, OPT_HELP, - OPT_HELP_LEGACY, OPT_INPUT_FORMAT, OPT_LIST, OPT_NAMES, OPT_NO_DELTA, + OPT_OMIT_HOME_PLUGIN_PATH, + OPT_OMIT_SYSTEM_PLUGIN_PATH, OPT_OUTPUT_FORMAT, OPT_OUTPUT_PATH, - OPT_PATH, OPT_PARAMS, + OPT_PATH, OPT_PLUGIN_PATH, OPT_RESET_BASE_PARAMS, OPT_SINK, @@ -2127,50 +2003,6 @@ enum { OPT_STREAM_INTERSECTION, OPT_TIMERANGE, OPT_VERBOSE, - OPT_VERSION, - OPT_OMIT_SYSTEM_PLUGIN_PATH, - OPT_OMIT_HOME_PLUGIN_PATH, -}; - -/* popt long option descriptions */ -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL }, - { "begin", '\0', 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 }, - { "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", '\0', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL }, - { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL }, - { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, - { "help-legacy", '\0', POPT_ARG_NONE, NULL, OPT_HELP_LEGACY, NULL, NULL }, - { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL }, - { "list", 'l', POPT_ARG_NONE, NULL, OPT_LIST, NULL, NULL }, - { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, NULL, NULL }, - { "no-delta", '\0', POPT_ARG_NONE, NULL, OPT_NO_DELTA, NULL, NULL }, - { "output", 'w', POPT_ARG_STRING, NULL, OPT_OUTPUT_PATH, NULL, NULL }, - { "output-format", 'o', POPT_ARG_STRING, NULL, OPT_OUTPUT_FORMAT, NULL, NULL }, - { "path", 'P', POPT_ARG_STRING, NULL, OPT_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", '\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 }, - { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL }, - { "version", 'V', POPT_ARG_NONE, NULL, OPT_VERSION, NULL, NULL }, - { "omit-system-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_SYSTEM_PLUGIN_PATH, NULL, NULL }, - { "omit-home-plugin-path", '\0', POPT_ARG_NONE, NULL, OPT_OMIT_HOME_PLUGIN_PATH, NULL, NULL }, - { NULL, 0, 0, NULL, 0, NULL, NULL }, }; /* @@ -2196,9 +2028,9 @@ static void add_cfg_comp(struct bt_config *cfg, enum bt_config_component_dest dest) { if (dest == BT_CONFIG_COMPONENT_DEST_SOURCE) { - g_ptr_array_add(cfg->sources, cfg_comp); + g_ptr_array_add(cfg->cmd_data.convert.sources, cfg_comp); } else { - g_ptr_array_add(cfg->sinks, cfg_comp); + g_ptr_array_add(cfg->cmd_data.convert.sinks, cfg_comp); } } @@ -2235,7 +2067,7 @@ not_found: return -1; } -static int add_env_var_plugin_paths(struct bt_config *cfg) +static int append_env_var_plugin_paths(struct bt_value *plugin_paths) { int ret = 0; const char *envvar; @@ -2250,17 +2082,18 @@ static int add_env_var_plugin_paths(struct bt_config *cfg) goto end; } - ret = bt_config_append_plugin_paths(cfg->plugin_paths, envvar); + ret = bt_config_append_plugin_paths(plugin_paths, envvar); end: return ret; } -static int append_home_and_system_plugin_paths(struct bt_config *cfg) +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 (!cfg->omit_home_plugin_path) { + if (!omit_home_plugin_path) { if (bt_common_is_setuid_setgid()) { printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); } else { @@ -2268,7 +2101,8 @@ static int append_home_and_system_plugin_paths(struct bt_config *cfg) bt_common_get_home_plugin_path(); if (home_plugin_dir) { - ret = bt_config_append_plugin_paths(cfg->plugin_paths, + ret = bt_config_append_plugin_paths( + plugin_paths, home_plugin_dir); free(home_plugin_dir); @@ -2280,8 +2114,8 @@ static int append_home_and_system_plugin_paths(struct bt_config *cfg) } } - if (!cfg->omit_system_plugin_path) { - if (bt_config_append_plugin_paths(cfg->plugin_paths, + 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; @@ -2316,7 +2150,7 @@ error: return -1; } -struct bt_config *bt_config_create(void) +static struct bt_config *bt_config_base_create(enum bt_config_command command) { struct bt_config *cfg; @@ -2328,113 +2162,167 @@ struct bt_config *bt_config_create(void) } bt_object_init(cfg, bt_config_destroy); - cfg->sources = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put); - if (!cfg->sources) { - print_err_oom(); - goto error; - } + cfg->command = command; + goto end; - cfg->sinks = g_ptr_array_new_with_free_func((GDestroyNotify) bt_put); - if (!cfg->sinks) { - print_err_oom(); - goto error; - } +error: + BT_PUT(cfg); - cfg->plugin_paths = bt_value_array_create(); - if (!cfg->plugin_paths) { - print_err_oom(); - goto error; - } end: return cfg; -error: - BT_PUT(cfg); - goto end; } -/* - * Initializes a created Babeltrace config object according to the - * command-line arguments found in argv. - * - * Return value is set to the appropriate exit code to use. - */ -int bt_config_init_from_args(struct bt_config *cfg, int argc, const char *argv[]) +static struct bt_config *bt_config_convert_create( + struct bt_value *initial_plugin_paths) { - poptContext pc = NULL; - char *arg = NULL; - struct ctf_legacy_opts ctf_legacy_opts; - struct text_legacy_opts text_legacy_opts; - enum legacy_input_format legacy_input_format = LEGACY_INPUT_FORMAT_NONE; - enum legacy_output_format legacy_output_format = - LEGACY_OUTPUT_FORMAT_NONE; - struct bt_value *legacy_input_paths = NULL; - struct bt_config_component *implicit_source_comp = NULL; - struct bt_config_component *cur_cfg_comp = NULL; - bool cur_is_implicit_source = false; - bool use_implicit_source = false; - enum bt_config_component_dest cur_cfg_comp_dest = - BT_CONFIG_COMPONENT_DEST_SOURCE; - struct bt_value *cur_base_params = NULL; - int opt, ret = 0; - - memset(&ctf_legacy_opts, 0, sizeof(ctf_legacy_opts)); - memset(&text_legacy_opts, 0, sizeof(text_legacy_opts)); + struct bt_config *cfg; - text_legacy_opts.output = g_string_new(NULL); - if (!text_legacy_opts.output) { + /* Create config */ + cfg = bt_config_base_create(BT_CONFIG_COMMAND_CONVERT); + if (!cfg) { print_err_oom(); goto error; } - text_legacy_opts.dbg_info_dir = g_string_new(NULL); - if (!text_legacy_opts.dbg_info_dir) { + cfg->cmd_data.convert.sources = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_put); + if (!cfg->cmd_data.convert.sources) { print_err_oom(); goto error; } - text_legacy_opts.dbg_info_target_prefix = g_string_new(NULL); - if (!text_legacy_opts.dbg_info_target_prefix) { + cfg->cmd_data.convert.sinks = g_ptr_array_new_with_free_func( + (GDestroyNotify) bt_put); + if (!cfg->cmd_data.convert.sinks) { print_err_oom(); goto error; } - cur_base_params = bt_value_map_create(); - if (!cur_base_params) { - print_err_oom(); - goto error; + if (initial_plugin_paths) { + cfg->cmd_data.convert.plugin_paths = + bt_get(initial_plugin_paths); + } else { + cfg->cmd_data.convert.plugin_paths = bt_value_array_create(); + if (!cfg->cmd_data.convert.plugin_paths) { + print_err_oom(); + goto error; + } } - legacy_input_paths = bt_value_array_create(); - if (!legacy_input_paths) { - print_err_oom(); - goto error; - } + goto end; - ret = add_env_var_plugin_paths(cfg); - if (ret) { - printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); +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); + if (!cfg) { + print_err_oom(); goto error; } - /* Note: implicit source never gets positional base params. */ - implicit_source_comp = bt_config_component_from_arg(DEFAULT_SOURCE_COMPONENT_NAME); - if (implicit_source_comp) { - cur_cfg_comp = implicit_source_comp; - cur_is_implicit_source = true; - use_implicit_source = true; + if (initial_plugin_paths) { + cfg->cmd_data.list_plugins.plugin_paths = + bt_get(initial_plugin_paths); } else { - printf_debug("Cannot find implicit source plugin \"%s\"", - DEFAULT_SOURCE_COMPONENT_NAME); - } - - /* Parse options */ - pc = poptGetContext(NULL, argc, (const char **) argv, long_options, 0); - if (!pc) { - printf_err("Cannot get popt context\n"); - goto error; + cfg->cmd_data.list_plugins.plugin_paths = + bt_value_array_create(); + if (!cfg->cmd_data.list_plugins.plugin_paths) { + print_err_oom(); + goto error; + } } - poptReadDefaultConfig(pc, 0); + goto end; + +error: + BT_PUT(cfg); + +end: + 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"); +} + +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. + */ +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) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.list_plugins.omit_system_plugin_path = omit_system_plugin_path; + cfg->cmd_data.list_plugins.omit_home_plugin_path = omit_home_plugin_path; + ret = append_env_var_plugin_paths( + cfg->cmd_data.list_plugins.plugin_paths); + if (ret) { + printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); + 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); @@ -2444,17 +2332,378 @@ int bt_config_init_from_args(struct bt_config *cfg, int argc, const char *argv[] if (bt_common_is_setuid_setgid()) { printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); } else { - if (bt_config_append_plugin_paths(cfg->plugin_paths, arg)) { + if (bt_config_append_plugin_paths( + cfg->cmd_data.list_plugins.plugin_paths, + arg)) { printf_err("Invalid --plugin-path option's argument\n"); goto error; } } break; case OPT_OMIT_SYSTEM_PLUGIN_PATH: - cfg->omit_system_plugin_path = true; + cfg->cmd_data.list_plugins.omit_system_plugin_path = true; break; case OPT_OMIT_HOME_PLUGIN_PATH: - cfg->omit_home_plugin_path = true; + cfg->cmd_data.list_plugins.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("Invalid argument: %s\n", leftover); + goto error; + } + + if (append_home_and_system_plugin_paths( + cfg->cmd_data.list_plugins.plugin_paths, + cfg->cmd_data.list_plugins.omit_system_plugin_path, + cfg->cmd_data.list_plugins.omit_home_plugin_path)) { + printf_err("Cannot append home and system plugin paths\n"); + goto error; + } + + goto end; + +error: + *retcode = 1; + BT_PUT(cfg); + +end: + if (pc) { + poptFreeContext(pc); + } + + free(arg); + return cfg; +} + + +/* + * Prints the legacy, Babeltrace 1.x command usage. Those options are + * still compatible in Babeltrace 2.x, but it is recommended to use + * the more generic plugin/component parameters instead of those + * hard-coded option names. + */ +static +void print_legacy_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace [OPTIONS] INPUT...\n"); + fprintf(fp, "\n"); + fprintf(fp, "The following options are compatible with the Babeltrace 1.x options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " --clock-force-correlate Assume that clocks are inherently correlated\n"); + fprintf(fp, " across traces\n"); + fprintf(fp, " -d, --debug Enable debug mode\n"); + fprintf(fp, " -i, --input-format=FORMAT Input trace format (default: ctf)\n"); + fprintf(fp, " -l, --list List available formats\n"); + fprintf(fp, " -o, --output-format=FORMAT Output trace format (default: text)\n"); + fprintf(fp, " -v, --verbose Enable verbose output\n"); + fprintf(fp, " --help-legacy Show this help and quit\n"); + fprintf(fp, " -V, --version Show version and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, " Available input formats: ctf, lttng-live, ctf-metadata\n"); + fprintf(fp, " Available output formats: text, dummy\n"); + fprintf(fp, "\n"); + fprintf(fp, "Input formats specific options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " INPUT... Input trace file(s), directory(ies), or URLs\n"); + fprintf(fp, " --clock-offset=SEC Set clock offset to SEC seconds\n"); + fprintf(fp, " --clock-offset-ns=NS Set clock offset to NS nanoseconds\n"); + fprintf(fp, " --stream-intersection Only process events when all streams are active\n"); + fprintf(fp, "\n"); + fprintf(fp, "text output format specific 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 GMT time zone\n"); + fprintf(fp, " (default: local time zone)\n"); + fprintf(fp, " --clock-seconds Print the timestamps as [SEC.NS]\n"); + fprintf(fp, " (default format: [HH:MM:SS.NS])\n"); + fprintf(fp, " --debug-info-dir=DIR Search for debug info in directory DIR\n"); + fprintf(fp, " (default: `/usr/lib/debug`)\n"); + fprintf(fp, " --debug-info-full-path Show full debug info source and binary paths\n"); + fprintf(fp, " --debug-info-target-prefix=DIR Use directory DIR as a prefix when looking\n"); + fprintf(fp, " up executables during debug info analysis\n"); + fprintf(fp, " (default: `/usr/lib/debug`)\n"); + fprintf(fp, " -f, --fields=NAME[,NAME]... Print additional fields:\n"); + fprintf(fp, " all, trace, trace:hostname, trace:domain,\n"); + fprintf(fp, " trace:procname, trace:vpid, loglevel, emf\n"); + fprintf(fp, " (default: trace:hostname, trace:procname,\n"); + fprintf(fp, " trace:vpid)\n"); + fprintf(fp, " -n, --names=NAME[,NAME]... Print field names:\n"); + fprintf(fp, " payload (or arg or args)\n"); + fprintf(fp, " none, all, scope, header, context (or ctx)\n"); + fprintf(fp, " (default: payload, context)\n"); + fprintf(fp, " --no-delta Do not print time delta between consecutive\n"); + fprintf(fp, " events\n"); + fprintf(fp, " -w, --output=PATH Write output to PATH (default: standard output)\n"); +} + +/* + * Prints the convert command usage. + */ +static +void print_convert_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] convert [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 the following component instances\n"); + fprintf(fp, " (see the expected format of PARAMS below)\n"); + fprintf(fp, " --begin=BEGIN Set the `begin` parameter of the latest\n"); + fprintf(fp, " source component instance to BEGIN\n"); + fprintf(fp, " (see the suggested format of BEGIN below)\n"); + fprintf(fp, " -d, --debug Enable debug mode\n"); + fprintf(fp, " --end=END Set the `end` parameter of the latest\n"); + fprintf(fp, " source component instance to END\n"); + fprintf(fp, " (see the suggested format of BEGIN below)\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 parameters of the latest component\n"); + fprintf(fp, " instance (in command-line order) to PARAMS\n"); + fprintf(fp, " (see the expected format of PARAMS below)\n"); + fprintf(fp, " -P, --path=PATH Set the `path` parameter of the latest\n"); + fprintf(fp, " component instance 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, " -r, --reset-base-params Reset the current base parameters of the\n"); + fprintf(fp, " following source and sink component\n"); + fprintf(fp, " instances to an empty map\n"); + fprintf(fp, " -o, --sink=PLUGIN.COMPCLS Instantiate a sink component from plugin\n"); + fprintf(fp, " PLUGIN and component class COMPCLS (may be\n"); + fprintf(fp, " repeated)\n"); + fprintf(fp, " -i, --source=PLUGIN.COMPCLS Instantiate a source component from plugin\n"); + fprintf(fp, " PLUGIN and component class COMPCLS (may be\n"); + fprintf(fp, " repeated)\n"); + fprintf(fp, " --timerange=TIMERANGE Set time range to TIMERANGE: BEGIN,END or\n"); + fprintf(fp, " [BEGIN,END] (literally `[` and `]`)\n"); + fprintf(fp, " (suggested format of BEGIN/END below)\n"); + fprintf(fp, " -v, --verbose Enable verbose output\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, "Suggested 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"); + 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 [:.-] characters), and\n"); + fprintf(fp, "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, "Whitespaces are allowed around individual `=` and `,` tokens.\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"); +} + +static struct poptOption convert_long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + { "base-params", 'b', POPT_ARG_STRING, NULL, OPT_BASE_PARAMS, NULL, NULL }, + { "begin", '\0', 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 }, + { "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", '\0', POPT_ARG_STRING, NULL, OPT_END, NULL, NULL }, + { "fields", 'f', POPT_ARG_STRING, NULL, OPT_FIELDS, NULL, NULL }, + { "help", 'h', POPT_ARG_NONE, NULL, OPT_HELP, NULL, NULL }, + { "input-format", 'i', POPT_ARG_STRING, NULL, OPT_INPUT_FORMAT, NULL, NULL }, + { "names", 'n', POPT_ARG_STRING, NULL, OPT_NAMES, 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_PATH, 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 }, + { "reset-base-params", 'r', POPT_ARG_NONE, NULL, OPT_RESET_BASE_PARAMS, 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 }, + { "verbose", 'v', POPT_ARG_NONE, NULL, OPT_VERBOSE, NULL, NULL }, + { NULL, 0, 0, NULL, 0, NULL, NULL }, +}; + +/* + * Creates a Babeltrace config object from the arguments of a convert + * command. + * + * *retcode is set to the appropriate exit code to use. + */ +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; + struct ctf_legacy_opts ctf_legacy_opts; + struct text_legacy_opts text_legacy_opts; + enum legacy_input_format legacy_input_format = LEGACY_INPUT_FORMAT_NONE; + enum legacy_output_format legacy_output_format = + LEGACY_OUTPUT_FORMAT_NONE; + struct bt_value *legacy_input_paths = NULL; + struct bt_config_component *implicit_source_comp = NULL; + struct bt_config_component *cur_cfg_comp = NULL; + bool cur_is_implicit_source = false; + bool use_implicit_source = false; + enum bt_config_component_dest cur_cfg_comp_dest = + BT_CONFIG_COMPONENT_DEST_SOURCE; + struct bt_value *cur_base_params = NULL; + int opt, ret = 0; + struct bt_config *cfg = NULL; + + *retcode = 0; + memset(&ctf_legacy_opts, 0, sizeof(ctf_legacy_opts)); + memset(&text_legacy_opts, 0, sizeof(text_legacy_opts)); + + if (argc <= 1) { + print_convert_usage(stdout); + *retcode = -1; + goto end; + } + + cfg = bt_config_convert_create(initial_plugin_paths); + if (!cfg) { + print_err_oom(); + goto error; + } + + cfg->cmd_data.convert.omit_system_plugin_path = omit_system_plugin_path; + cfg->cmd_data.convert.omit_home_plugin_path = omit_home_plugin_path; + text_legacy_opts.output = g_string_new(NULL); + if (!text_legacy_opts.output) { + print_err_oom(); + goto error; + } + + text_legacy_opts.dbg_info_dir = g_string_new(NULL); + if (!text_legacy_opts.dbg_info_dir) { + print_err_oom(); + goto error; + } + + text_legacy_opts.dbg_info_target_prefix = g_string_new(NULL); + if (!text_legacy_opts.dbg_info_target_prefix) { + print_err_oom(); + goto error; + } + + cur_base_params = bt_value_map_create(); + if (!cur_base_params) { + print_err_oom(); + goto error; + } + + legacy_input_paths = bt_value_array_create(); + if (!legacy_input_paths) { + print_err_oom(); + goto error; + } + + ret = append_env_var_plugin_paths(cfg->cmd_data.convert.plugin_paths); + if (ret) { + printf_err("Cannot append plugin paths from BABELTRACE_PLUGIN_PATH\n"); + goto error; + } + + /* Note: implicit source never gets positional base params. */ + implicit_source_comp = bt_config_component_from_arg(DEFAULT_SOURCE_COMPONENT_NAME); + if (implicit_source_comp) { + cur_cfg_comp = implicit_source_comp; + cur_is_implicit_source = true; + use_implicit_source = true; + } else { + printf_debug("Cannot find implicit source plugin \"%s\"", + DEFAULT_SOURCE_COMPONENT_NAME); + } + + /* Parse options */ + 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_PLUGIN_PATH: + if (bt_common_is_setuid_setgid()) { + printf_debug("Skipping non-system plugin paths for setuid/setgid binary\n"); + } else { + if (bt_config_append_plugin_paths( + cfg->cmd_data.convert.plugin_paths, + arg)) { + printf_err("Invalid --plugin-path option's argument\n"); + goto error; + } + } + break; + case OPT_OMIT_SYSTEM_PLUGIN_PATH: + cfg->cmd_data.convert.omit_system_plugin_path = true; + break; + case OPT_OMIT_HOME_PLUGIN_PATH: + cfg->cmd_data.convert.omit_home_plugin_path = true; break; case OPT_OUTPUT_PATH: if (text_legacy_opts.output->len > 0) { @@ -2528,7 +2777,7 @@ int bt_config_init_from_args(struct bt_config *cfg, int argc, const char *argv[] cur_cfg_comp->params = bt_value_copy(cur_base_params); if (!cur_cfg_comp->params) { print_err_oom(); - goto end; + goto error; } cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SOURCE; @@ -2590,7 +2839,7 @@ int bt_config_init_from_args(struct bt_config *cfg, int argc, const char *argv[] cur_cfg_comp->params = bt_value_copy(cur_base_params); if (!cur_cfg_comp->params) { print_err_oom(); - goto end; + goto error; } cur_cfg_comp_dest = BT_CONFIG_COMPONENT_DEST_SINK; @@ -2742,7 +2991,7 @@ int bt_config_init_from_args(struct bt_config *cfg, int argc, const char *argv[] ctf_legacy_opts.stream_intersection = true; break; case OPT_CLOCK_FORCE_CORRELATE: - cfg->force_correlate = true; + cfg->cmd_data.convert.force_correlate = true; break; case OPT_BEGIN: if (!cur_cfg_comp) { @@ -2806,16 +3055,9 @@ int bt_config_init_from_args(struct bt_config *cfg, int argc, const char *argv[] break; } case OPT_HELP: - print_usage(stdout); - goto end; - case OPT_HELP_LEGACY: - print_legacy_usage(stdout); - goto end; - case OPT_VERSION: - print_version(); - goto end; - case OPT_LIST: - cfg->do_list = true; + print_convert_usage(stdout); + *retcode = -1; + BT_PUT(cfg); goto end; case OPT_VERBOSE: cfg->verbose = true; @@ -2833,11 +3075,6 @@ int bt_config_init_from_args(struct bt_config *cfg, int argc, const char *argv[] arg = NULL; } - if (argc <= 1) { - print_usage(stdout); - goto end; - } - /* Check for option parsing error */ if (opt < -1) { printf_err("While parsing command-line options, at option %s: %s\n", @@ -2860,7 +3097,11 @@ int bt_config_init_from_args(struct bt_config *cfg, int argc, const char *argv[] } } - if (append_home_and_system_plugin_paths(cfg)) { + if (append_home_and_system_plugin_paths( + cfg->cmd_data.convert.plugin_paths, + cfg->cmd_data.convert.omit_system_plugin_path, + cfg->cmd_data.convert.omit_home_plugin_path)) { + printf_err("Cannot append home and system plugin paths\n"); goto error; } @@ -2883,13 +3124,15 @@ int bt_config_init_from_args(struct bt_config *cfg, int argc, const char *argv[] * component configurations. */ if (legacy_input_format) { - if (append_sources_from_legacy_opts(cfg->sources, + if (append_sources_from_legacy_opts( + cfg->cmd_data.convert.sources, legacy_input_format, &ctf_legacy_opts, legacy_input_paths)) { printf_err("Cannot convert legacy input format options to source component instance(s)\n"); goto error; } - if (append_sources_from_implicit_params(cfg->sources, + if (append_sources_from_implicit_params( + cfg->cmd_data.convert.sources, implicit_source_comp)) { printf_err("Cannot initialize legacy component parameters\n"); goto error; @@ -2914,14 +3157,14 @@ int bt_config_init_from_args(struct bt_config *cfg, int argc, const char *argv[] * component configurations. */ if (legacy_output_format) { - if (append_sinks_from_legacy_opts(cfg->sinks, + if (append_sinks_from_legacy_opts(cfg->cmd_data.convert.sinks, legacy_output_format, &text_legacy_opts)) { printf_err("Cannot convert legacy output format options to sink component instance(s)\n"); goto error; } } - if (cfg->sinks->len == 0) { + if (cfg->cmd_data.convert.sinks->len == 0) { /* Use implicit sink as default. */ cur_cfg_comp = bt_config_component_from_arg(DEFAULT_SINK_COMPONENT_NAME); if (!cur_cfg_comp) { @@ -2936,7 +3179,9 @@ int bt_config_init_from_args(struct bt_config *cfg, int argc, const char *argv[] goto end; error: - ret = 1; + *retcode = 1; + BT_PUT(cfg); + end: if (pc) { poptFreeContext(pc); @@ -2961,5 +3206,145 @@ end: BT_PUT(text_legacy_opts.names); BT_PUT(text_legacy_opts.fields); BT_PUT(legacy_input_paths); - return ret; + return cfg; +} + +/* + * Prints the Babeltrace 2.x general usage. + */ +static +void print_gen_usage(FILE *fp) +{ + fprintf(fp, "Usage: babeltrace [GENERAL OPTIONS] [COMMAND] [COMMAND OPTIONS]\n"); + fprintf(fp, "\n"); + fprintf(fp, "General options:\n"); + fprintf(fp, "\n"); + fprintf(fp, " -d, --debug Enable debug mode\n"); + fprintf(fp, " -h --help Show this help and quit\n"); + fprintf(fp, " --help-legacy Show Babeltrace 1.x legacy help and quit\n"); + fprintf(fp, " -v, --verbose Enable verbose output\n"); + fprintf(fp, " -V, --version Show version and quit\n"); + fprintf(fp, "\n"); + fprintf(fp, "Available commands:\n"); + fprintf(fp, "\n"); + fprintf(fp, " convert Build a trace conversion graph and run it (default)\n"); + fprintf(fp, " list-plugins List available plugins and their content\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; + enum bt_config_command command = -1; + const char **command_argv = NULL; + int command_argc = -1; + const char *command_name = NULL; + + *retcode = -1; + + 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 if (strcmp(cur_arg, "--help-legacy") == 0) { + print_legacy_usage(stdout); + goto end; + } else { + /* + * First unknown argument: is it a known command + * name? + */ + if (strcmp(cur_arg, "convert") == 0) { + command = BT_CONFIG_COMMAND_CONVERT; + command_argv = &argv[i]; + command_argc = argc - i; + command_name = cur_arg; + } else if (strcmp(cur_arg, "list-plugins") == 0) { + command = BT_CONFIG_COMMAND_LIST_PLUGINS; + command_argv = &argv[i]; + command_argc = argc - i; + command_name = cur_arg; + } else { + /* + * Unknown argument, but not a known + * command name: assume the whole + * arguments are for the default convert + * command. + */ + command = BT_CONFIG_COMMAND_CONVERT; + command_argv = argv; + command_argc = argc; + } + break; + } + } + + if ((int) command < 0) { + /* + * 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) { + case BT_CONFIG_COMMAND_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 BT_CONFIG_COMMAND_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; + default: + assert(false); + } + + if (config) { + if (verbose) { + config->verbose = true; + } + + if (debug) { + config->debug = true; + } + + config->command_name = command_name; + } + +end: + return config; } diff --git a/converter/babeltrace-cfg.h b/converter/babeltrace-cfg.h index f32bf07a..20c16066 100644 --- a/converter/babeltrace-cfg.h +++ b/converter/babeltrace-cfg.h @@ -40,22 +40,40 @@ struct bt_config_component { struct bt_value *params; }; +enum bt_config_command { + BT_CONFIG_COMMAND_CONVERT, + BT_CONFIG_COMMAND_LIST_PLUGINS, +}; + struct bt_config { struct bt_object base; - struct bt_value *plugin_paths; + bool debug; + bool verbose; + const char *command_name; + enum bt_config_command command; + union { + /* BT_CONFIG_COMMAND_CONVERT */ + struct { + struct bt_value *plugin_paths; - /* Array of pointers to struct bt_config_component */ - GPtrArray *sources; + /* Array of pointers to struct bt_config_component */ + GPtrArray *sources; - /* Array of pointers to struct bt_config_component */ - GPtrArray *sinks; + /* Array of pointers to struct bt_config_component */ + GPtrArray *sinks; - bool debug; - bool verbose; - bool do_list; - bool force_correlate; - bool omit_system_plugin_path; - bool omit_home_plugin_path; + bool force_correlate; + bool omit_system_plugin_path; + bool omit_home_plugin_path; + } convert; + + /* BT_CONFIG_COMMAND_LIST_PLUGINS */ + struct { + struct bt_value *plugin_paths; + bool omit_system_plugin_path; + bool omit_home_plugin_path; + } list_plugins; + } cmd_data; }; static inline @@ -65,10 +83,10 @@ struct bt_config_component *bt_config_get_component(GPtrArray *array, return bt_get(g_ptr_array_index(array, index)); } -struct bt_config *bt_config_create(void); - -int bt_config_init_from_args(struct bt_config *cfg, int argc, - const char *argv[]); +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); enum bt_value_status bt_config_append_plugin_paths( struct bt_value *plugin_paths, const char *arg); diff --git a/converter/babeltrace.c b/converter/babeltrace.c index 14954f1d..65d2ec93 100644 --- a/converter/babeltrace.c +++ b/converter/babeltrace.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -97,102 +98,6 @@ end: return comp_class; } -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_component_classes_found(void) -{ - int plugins_count, component_classes_count = 0, i; - - if (!babeltrace_verbose) { - return; - } - - plugins_count = loaded_plugins->len; - if (plugins_count == 0) { - fprintf(stderr, "No plugins found. Please make sure your plug-in search path is set correctly.\n"); - return; - } - - 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 %d component classes in %d plugins.\n", - component_classes_count, plugins_count); - - for (i = 0; i < plugins_count; i++) { - int j; - struct bt_plugin *plugin = g_ptr_array_index(loaded_plugins, i); - unsigned int major, minor, patch; - const char *extra; - enum bt_plugin_status version_status; - - component_classes_count = - bt_plugin_get_component_class_count(plugin); - version_status = bt_plugin_get_version(plugin, &major, &minor, - &patch, &extra); - - for (j = 0; j < component_classes_count; j++) { - struct bt_component_class *comp_class = - bt_plugin_get_component_class(plugin, j); - const char *plugin_name = bt_plugin_get_name(plugin); - const char *comp_class_name = - bt_component_class_get_name(comp_class); - const char *path = bt_plugin_get_path(plugin); - const char *author = bt_plugin_get_author(plugin); - const char *license = bt_plugin_get_license(plugin); - const char *plugin_description = - bt_plugin_get_description(plugin); - 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("[%s - %s (%s)]\n", plugin_name, - comp_class_name, component_type_str(type)); - printf("\tpath: %s\n", path ? path : "None"); - printf("\tauthor: %s\n", - author ? author : "Unknown"); - printf("\tlicense: %s\n", - license ? license : "Unknown"); - printf("\tplugin description: %s\n", - plugin_description ? plugin_description : "None"); - - if (version_status == BT_PLUGIN_STATUS_OK) { - printf("\tplugin version: %u.%u.%u", - major, minor, patch); - - if (extra) { - printf("%s", extra); - } - - printf("\n"); - } - - printf("\tcomponent description: %s\n", - comp_class_description ? comp_class_description : "None"); - bt_put(comp_class); - } - } -} - static void print_indent(size_t indent) { @@ -204,22 +109,39 @@ void print_indent(size_t indent) } static -void print_value(struct bt_value *, size_t, bool); +void print_value(struct bt_value *, size_t); static bool print_map_value(const char *key, struct bt_value *object, void *data) { - size_t indent = (size_t) 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; + } - print_indent(indent); - printf("\"%s\": ", key); - print_value(object, indent, false); + if (bt_value_is_array(object) || + bt_value_is_map(object)) { + printf("\n"); + } + print_value(object, *indent + 2); return true; } static -void print_value(struct bt_value *value, size_t indent, bool do_indent) +void print_value(struct bt_value *value, size_t indent) { bool bool_val; int64_t int_val; @@ -232,17 +154,13 @@ void print_value(struct bt_value *value, size_t indent, bool do_indent) return; } - if (do_indent) { - print_indent(indent); - } - switch (bt_value_get_type(value)) { case BT_VALUE_TYPE_NULL: printf("null\n"); break; case BT_VALUE_TYPE_BOOL: bt_value_bool_get(value, &bool_val); - printf("%s\n", bool_val ? "true" : "false"); + printf("%s\n", bool_val ? "yes" : "no"); break; case BT_VALUE_TYPE_INTEGER: bt_value_integer_get(value, &int_val); @@ -254,34 +172,55 @@ void print_value(struct bt_value *value, size_t indent, bool do_indent) break; case BT_VALUE_TYPE_STRING: bt_value_string_get(value, &str_val); - printf("\"%s\"\n", str_val); + printf("%s\n", str_val); break; case BT_VALUE_TYPE_ARRAY: size = bt_value_array_size(value); - printf("[\n"); + 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); - print_value(element, indent + 2, true); + 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(element, indent + 2); BT_PUT(element); } - - print_indent(indent); - printf("]\n"); break; case BT_VALUE_TYPE_MAP: if (bt_value_map_is_empty(value)) { - printf("{}\n"); - return; + print_indent(indent); + printf("{ }\n"); + break; } - printf("{\n"); - bt_value_map_foreach(value, print_map_value, - (void *) (indent + 2)); - print_indent(indent); - printf("}\n"); + bt_value_map_foreach(value, print_map_value, &indent); break; default: assert(false); @@ -291,10 +230,10 @@ void print_value(struct bt_value *value, size_t indent, bool do_indent) static void print_bt_config_component(struct bt_config_component *bt_config_component) { - printf(" %s.%s\n", bt_config_component->plugin_name->str, + printf(" %s.%s:\n", bt_config_component->plugin_name->str, bt_config_component->component_name->str); - printf(" params:\n"); - print_value(bt_config_component->params, 6, true); + printf(" Parameters:\n"); + print_value(bt_config_component->params, 8); } static @@ -310,6 +249,31 @@ void print_bt_config_components(GPtrArray *array) } } +static +void print_plugin_paths(struct bt_value *plugin_paths) +{ + printf(" Plugin paths:\n"); + print_value(plugin_paths, 4); +} + +static +void print_cfg_convert(struct bt_config *cfg) +{ + printf(" Force correlate: %s\n", + cfg->cmd_data.convert.force_correlate ? "yes" : "no"); + print_plugin_paths(cfg->cmd_data.convert.plugin_paths); + printf(" Source component instances:\n"); + print_bt_config_components(cfg->cmd_data.convert.sources); + printf(" Sink component instances:\n"); + print_bt_config_components(cfg->cmd_data.convert.sinks); +} + +static +void print_cfg_list_plugins(struct bt_config *cfg) +{ + print_plugin_paths(cfg->cmd_data.list_plugins.plugin_paths); +} + static void print_cfg(struct bt_config *cfg) { @@ -317,16 +281,20 @@ void print_cfg(struct bt_config *cfg) return; } - printf("debug: %d\n", cfg->debug); - printf("verbose: %d\n", cfg->verbose); - printf("do list: %d\n", cfg->do_list); - printf("force correlate: %d\n", cfg->force_correlate); - printf("plugin paths:\n"); - print_value(cfg->plugin_paths, 2, true); - printf("sources:\n"); - print_bt_config_components(cfg->sources); - printf("sinks:\n"); - print_bt_config_components(cfg->sinks); + 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_CONVERT: + print_cfg_convert(cfg); + break; + case BT_CONFIG_COMMAND_LIST_PLUGINS: + print_cfg_list_plugins(cfg); + break; + default: + assert(false); + } } static @@ -479,11 +447,11 @@ void add_to_loaded_plugins(struct bt_plugin **plugins) } static -int load_dynamic_plugins(struct bt_config *cfg) +int load_dynamic_plugins(struct bt_value *plugin_paths) { int nr_paths, i, ret = 0; - nr_paths = bt_value_array_size(cfg->plugin_paths); + nr_paths = bt_value_array_size(plugin_paths); if (nr_paths < 0) { ret = -1; goto end; @@ -494,7 +462,7 @@ int load_dynamic_plugins(struct bt_config *cfg) const char *plugin_path; struct bt_plugin **plugins; - plugin_path_value = bt_value_array_get(cfg->plugin_paths, i); + 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); @@ -537,62 +505,192 @@ end: return ret; } -int main(int argc, const char **argv) +static +const char *component_type_str(enum bt_component_class_type type) { - int ret; - 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 *cfg; - enum bt_component_status sink_status; - struct bt_config_component *source_cfg = NULL, *sink_cfg = NULL; + 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"; + } +} - init_loaded_plugins_array(); +static int load_all_plugins(struct bt_value *plugin_paths) +{ + int ret = 0; - cfg = bt_config_create(); - if (!cfg) { - fprintf(stderr, "Failed to create Babeltrace configuration\n"); - ret = 1; + if (load_dynamic_plugins(plugin_paths)) { + fprintf(stderr, "Failed to load dynamic plugins.\n"); + ret = -1; goto end; } - ret = set_default_config(cfg); - if (ret) { + if (load_static_plugins()) { + fprintf(stderr, "Failed to load static plugins.\n"); + ret = -1; goto end; } - ret = bt_config_init_from_args(cfg, argc, argv); - if (ret == 0) { - babeltrace_verbose = cfg->verbose; - babeltrace_debug = cfg->debug; - print_cfg(cfg); - } else { +end: + 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->cmd_data.list_plugins.plugin_paths); + if (ret) { goto end; } - /* TODO handle more than 1 source and 1 sink. */ - if (cfg->sources->len != 1 || cfg->sinks->len != 1) { + 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; } - printf_verbose("Verbose mode active.\n"); - printf_debug("Debug mode active.\n"); + 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); + } - if (load_dynamic_plugins(cfg)) { - fprintf(stderr, "Failed to load dynamic plugins.\n"); + 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); + unsigned int major, minor, patch; + const char *extra; + enum bt_plugin_status version_status; + const char *plugin_name = bt_plugin_get_name(plugin); + const char *path = bt_plugin_get_path(plugin); + const char *author = bt_plugin_get_author(plugin); + const char *license = bt_plugin_get_license(plugin); + const char *plugin_description = + bt_plugin_get_description(plugin); + + component_classes_count = + bt_plugin_get_component_class_count(plugin); + version_status = bt_plugin_get_version(plugin, &major, &minor, + &patch, &extra); + + printf("\n%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)"); + + 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(" %s%s--%s%s %s%s%s.%s%s%s", + bt_common_color_bold(), + bt_common_color_fg_cyan(), + component_type_str(type), + bt_common_color_fg_default(), + bt_common_color_fg_blue(), + plugin_name, + bt_common_color_fg_default(), + bt_common_color_fg_yellow(), + comp_class_name, + bt_common_color_reset()); + + if (comp_class_description) { + printf(": %s", comp_class_description); + } + + printf("\n"); + bt_put(comp_class); + } + } + +end: + return ret; +} + +static int cmd_convert(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; + enum bt_component_status sink_status; + struct bt_config_component *source_cfg = NULL, *sink_cfg = NULL; + + /* TODO handle more than 1 source and 1 sink. */ + if (cfg->cmd_data.convert.sources->len != 1 || + cfg->cmd_data.convert.sinks->len != 1) { ret = -1; goto end; } - if (load_static_plugins()) { - fprintf(stderr, "Failed to load static plugins.\n"); + ret = load_all_plugins(cfg->cmd_data.convert.plugin_paths); + if (ret) { goto end; } - print_component_classes_found(); - source_cfg = bt_config_get_component(cfg->sources, 0); + source_cfg = bt_config_get_component(cfg->cmd_data.convert.sources, 0); source_params = bt_get(source_cfg->params); source_class = find_component_class(source_cfg->plugin_name->str, source_cfg->component_name->str, @@ -605,7 +703,7 @@ int main(int argc, const char **argv) goto end; } - sink_cfg = bt_config_get_component(cfg->sinks, 0); + sink_cfg = bt_config_get_component(cfg->cmd_data.convert.sinks, 0); sink_params = bt_get(sink_cfg->params); sink_class = find_component_class(sink_cfg->plugin_name->str, sink_cfg->component_name->str, @@ -634,6 +732,7 @@ int main(int argc, const char **argv) ret = connect_source_sink(source, source_cfg, sink); if (ret) { + ret = -1; goto end; } @@ -654,6 +753,7 @@ int main(int argc, const char **argv) goto end; } } + end: BT_PUT(sink_class); BT_PUT(source_class); @@ -661,9 +761,73 @@ end: BT_PUT(sink); BT_PUT(source_params); BT_PUT(sink_params); - BT_PUT(cfg); BT_PUT(sink_cfg); BT_PUT(source_cfg); + 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"); + goto end; + } + + babeltrace_debug = cfg->debug; + babeltrace_verbose = cfg->verbose; + print_cfg(cfg); + + switch (cfg->command) { + case BT_CONFIG_COMMAND_CONVERT: + ret = cmd_convert(cfg); + break; + case BT_CONFIG_COMMAND_LIST_PLUGINS: + ret = cmd_list_plugins(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 ret ? 1 : 0; + return retcode; } diff --git a/converter/default-cfg.c b/converter/default-cfg.c index 5368c7a0..fb325431 100644 --- a/converter/default-cfg.c +++ b/converter/default-cfg.c @@ -33,23 +33,43 @@ #ifdef BT_SET_DEFAULT_IN_TREE_CONFIGURATION -int set_default_config(struct bt_config *cfg) +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; - cfg->omit_system_plugin_path = true; - cfg->omit_home_plugin_path = true; - ret = bt_config_append_plugin_paths(cfg->plugin_paths, + 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); - return ret; + 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 */ -int set_default_config(struct bt_config *cfg) +struct bt_config *bt_config_from_args_with_defaults(int argc, + const char *argv[], int *retcode) { - /* Nothing to set. */ - return 0; + 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 index 9d9cd6bf..d6b91888 100644 --- a/converter/default-cfg.h +++ b/converter/default-cfg.h @@ -27,6 +27,7 @@ #include "babeltrace-cfg.h" -int set_default_config(struct bt_config *cfg); +struct bt_config *bt_config_from_args_with_defaults(int argc, + const char *argv[], int *retcode); #endif /* BABELTRACE_DEFAULT_CFG_H */ diff --git a/include/babeltrace/common-internal.h b/include/babeltrace/common-internal.h index 64299e9d..1beb9d9b 100644 --- a/include/babeltrace/common-internal.h +++ b/include/babeltrace/common-internal.h @@ -15,4 +15,58 @@ char *bt_common_get_home_plugin_path(void); BT_HIDDEN int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs); +BT_HIDDEN +const char *bt_common_color_reset(void); + +BT_HIDDEN +const char *bt_common_color_bold(void); + +BT_HIDDEN +const char *bt_common_color_fg_default(void); + +BT_HIDDEN +const char *bt_common_color_fg_red(void); + +BT_HIDDEN +const char *bt_common_color_fg_green(void); + +BT_HIDDEN +const char *bt_common_color_fg_yellow(void); + +BT_HIDDEN +const char *bt_common_color_fg_blue(void); + +BT_HIDDEN +const char *bt_common_color_fg_magenta(void); + +BT_HIDDEN +const char *bt_common_color_fg_cyan(void); + +BT_HIDDEN +const char *bt_common_color_fg_light_gray(void); + +BT_HIDDEN +const char *bt_common_color_bg_default(void); + +BT_HIDDEN +const char *bt_common_color_bg_red(void); + +BT_HIDDEN +const char *bt_common_color_bg_green(void); + +BT_HIDDEN +const char *bt_common_color_bg_yellow(void); + +BT_HIDDEN +const char *bt_common_color_bg_blue(void); + +BT_HIDDEN +const char *bt_common_color_bg_magenta(void); + +BT_HIDDEN +const char *bt_common_color_bg_cyan(void); + +BT_HIDDEN +const char *bt_common_color_bg_light_gray(void); + #endif /* BABELTRACE_COMMON_INTERNAL_H */ -- 2.34.1