From 0c95f5b2ec754e905a0263cafc6e453f451d0ded Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 7 Feb 2012 14:13:20 -0500 Subject: [PATCH] Add the lttng view command This adds "view" to the lttng command line interface. It's used to visualize traces with a given viewers (default: babeltrace). The -e, --viewer options is available to override the default viewer settings or add arguments to the viewer. For example: $ lttng view -e "babeltrace -n scope --no-delta --clock-raw" The trace directory path is automatically appended using the current session or the given session name to lttng view. The default behavior is to simply spawn babeltrace with "-n all". $ lttng view [...] LTTv support is now disabled but is planned for the stable release. Signed-off-by: David Goulet --- configure.ac | 18 ++ extras/lttng-bash_completion | 11 + src/bin/lttng/Makefile.am | 3 +- src/bin/lttng/command.h | 1 + src/bin/lttng/commands/view.c | 388 ++++++++++++++++++++++++++++++++++ src/bin/lttng/lttng.c | 6 +- 6 files changed, 425 insertions(+), 2 deletions(-) create mode 100644 src/bin/lttng/commands/view.c diff --git a/configure.ac b/configure.ac index 42ecd040e..e8c0e73fa 100644 --- a/configure.ac +++ b/configure.ac @@ -14,6 +14,22 @@ AC_CHECK_HEADERS([ \ getopt.h sys/ipc.h sys/shm.h popt.h grp.h \ ]) +# Babeltrace viewer check +AC_ARG_WITH([babeltrace-bin], + AS_HELP_STRING([--with-babeltrace-bin], + [Location of the babeltrace viewer executable (including the filename)]), + [BABELTRACE_BIN="$withval"], + [BABELTRACE_BIN='']) +AC_SUBST([BABELTRACE_BIN]) + +# lttv-gui +AC_ARG_WITH([lttv-gui-bin], + AS_HELP_STRING([--with-lttv-gui-bin], + [Location of the lttv GUI viewer executable (including the filename)]), + [LTTV_GUI_BIN="$withval"], + [LTTV_GUI_BIN='']) +AC_SUBST([LTTV_GUI_BIN]) + AC_ARG_WITH([consumerd32-bin], AS_HELP_STRING([--with-consumerd32-bin], [Location of the 32-bit consumerd executable (including the filename)]), @@ -46,6 +62,8 @@ AC_DEFINE_UNQUOTED([CONFIG_CONSUMERD32_BIN], "$CONSUMERD32_BIN", [Location of th AC_DEFINE_UNQUOTED([CONFIG_CONSUMERD64_BIN], "$CONSUMERD64_BIN", [Location of the 64-bit consumerd executable]) AC_DEFINE_UNQUOTED([CONFIG_CONSUMERD32_LIBDIR], "$CONSUMERD32_LIBDIR", [Search for consumerd 32-bit libraries in this location.]) AC_DEFINE_UNQUOTED([CONFIG_CONSUMERD64_LIBDIR], "$CONSUMERD64_LIBDIR", [Search for consumerd 64-bit libraries in this location.]) +AC_DEFINE_UNQUOTED([CONFIG_BABELTRACE_BIN], "$BABELTRACE_BIN", [Location of the babeltrace viewer executable.]) +AC_DEFINE_UNQUOTED([CONFIG_LTTV_GUI_BIN], "$LTTV_GUI_BIN", [Location of the lttv GUI viewer executable.]) # Check for pthread AC_CHECK_LIB([pthread], [pthread_create], [], diff --git a/extras/lttng-bash_completion b/extras/lttng-bash_completion index ac57e4d83..9aacb0294 100644 --- a/extras/lttng-bash_completion +++ b/extras/lttng-bash_completion @@ -249,6 +249,17 @@ _lttng_cmd_calibrate() { esac } +_lttng_cmd_view() { + local view_opts + view_opts=$(lttng view --list-options) + + case $cur in + -*) + COMPREPLY=( $(compgen -W "${view_opts}" -- $cur) ) + ;; + esac +} + _lttng_opts() { local opts opts=$(lttng --list-options) diff --git a/src/bin/lttng/Makefile.am b/src/bin/lttng/Makefile.am index 282ef9a4e..e2040d8c3 100644 --- a/src/bin/lttng/Makefile.am +++ b/src/bin/lttng/Makefile.am @@ -8,6 +8,7 @@ lttng_SOURCES = command.h conf.c conf.h commands/start.c \ commands/disable_events.c commands/enable_channels.c \ commands/disable_channels.c commands/add_context.c \ commands/set_session.c commands/version.c \ - commands/calibrate.c utils.c utils.h lttng.c + commands/calibrate.c commands/view.c \ + utils.c utils.h lttng.c lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la diff --git a/src/bin/lttng/command.h b/src/bin/lttng/command.h index dcad0a138..fa3e21746 100644 --- a/src/bin/lttng/command.h +++ b/src/bin/lttng/command.h @@ -52,5 +52,6 @@ extern int cmd_add_context(int argc, const char **argv); extern int cmd_set_session(int argc, const char **argv); extern int cmd_version(int argc, const char **argv); extern int cmd_calibrate(int argc, const char **argv); +extern int cmd_view(int argc, const char **argv); #endif /* _LTTNG_CMD_H */ diff --git a/src/bin/lttng/commands/view.c b/src/bin/lttng/commands/view.c new file mode 100644 index 000000000..a146a91cf --- /dev/null +++ b/src/bin/lttng/commands/view.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "../command.h" +#include + +static char *opt_session_name; +static char *opt_viewer; +static const char *babeltrace_bin = CONFIG_BABELTRACE_BIN; +//static const char *lttv_gui_bin = CONFIG_LTTV_GUI_BIN; + +enum { + OPT_HELP = 1, + OPT_LIST_OPTIONS, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, + {"viewer", 'e', POPT_ARG_STRING, &opt_viewer, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * This is needed for each viewer since we are using execvp(). + */ +static const char *babeltrace_opts[] = { "babeltrace", "-n", "all", }; +//static const char *lttv_gui_opts[] = { "lttv-gui", "-t", }; + +/* + * Type is also use as the index in the viewers array. So please, make sure + * your enum value is in the right order in the array below. + */ +enum viewer_type { + VIEWER_BABELTRACE = 0, + VIEWER_LTTV_GUI = 1, + VIEWER_USER_DEFINED = 2, +}; + +/* + * NOTE: "lttv" is a shell command and it's not working for exec() family + * functions so we might think of removing this wrapper or using bash. + */ +static struct viewers { + const char *exec_name; + enum viewer_type type; +} viewers[] = { + { "babeltrace", VIEWER_BABELTRACE }, + { "lttv-gui", VIEWER_LTTV_GUI }, + { NULL, VIEWER_USER_DEFINED }, +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng view [SESSION_NAME] [OPTIONS]\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "By default, the babeltrace viewer will be used for text viewing\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Where SESSION_NAME is an optional session name. If not specified, lttng will\n"); + fprintf(ofp, "get it from the configuration file (.lttngrc).\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, " --list-options Simple listing of options\n"); + fprintf(ofp, " -e, --viewer CMD Specify viewer and/or options to use\n"); + fprintf(ofp, " This will completely override the default viewers so\n"); + fprintf(ofp, " please make sure to specify the full command.\n"); + fprintf(ofp, "\n"); +} + +static struct viewers *parse_options(void) +{ + if (opt_viewer == NULL) { + /* Default is babeltrace */ + return &(viewers[VIEWER_BABELTRACE]); + } + +#if 0 + if (strstr(opt_viewer, viewers[VIEWER_LTTV_GUI].exec_name) == 0) { + return &(viewers[VIEWER_LTTV_GUI]); + } +#endif + + /* + * This means that if -e, --viewers is used, we just override everything + * with it. For supported viewers like lttv, we could simply detect if "-t" + * is passed and if not, add the trace directory to it. + */ + return &(viewers[VIEWER_USER_DEFINED]); +} + +/* + * Alloc an array of string pointer from a simple string having all options + * seperated by spaces. Also adds the trace path to the arguments. + * + * The returning pointer is ready to be passed to execvp(). + */ +static char **alloc_argv_from_user_opts(char *opts, const char *trace_path) +{ + int i = 0, ignore_space = 0; + unsigned int num_opts = 1; + char **argv, *token = opts; + + /* Count number of arguments. */ + do { + if (*token == ' ') { + /* Use to ignore consecutive spaces */ + if (!ignore_space) { + num_opts++; + } + ignore_space = 1; + } else { + ignore_space = 0; + } + token++; + } while (*token != '\0'); + + /* Add two here for the NULL terminating element and trace path */ + argv = malloc(sizeof(char *) * (num_opts + 2)); + if (argv == NULL) { + goto error; + } + + token = strtok(opts, " "); + while (token != NULL) { + argv[i] = strdup(token); + token = strtok(NULL, " "); + i++; + } + + argv[num_opts] = (char *) trace_path; + argv[num_opts + 1] = NULL; + + return argv; + +error: + return NULL; +} + +/* + * Alloc an array of string pointer from an array of strings. It also adds + * the trace path to the argv. + * + * The returning pointer is ready to be passed to execvp(). + */ +static char **alloc_argv_from_local_opts(const char **opts, size_t opts_len, + const char *trace_path) +{ + char **argv; + size_t size; + + size = sizeof(char *) * opts_len; + + /* Add two here for the trace_path and the NULL terminating element. */ + argv = malloc(size + 2); + if (argv == NULL) { + goto error; + } + + memcpy(argv, opts, size); + + argv[opts_len] = (char *)trace_path; + argv[opts_len + 1] = NULL; + +error: + return argv; +} + +/* + * Spawn viewer with the trace directory path. + */ +static int spawn_viewer(const char *trace_path) +{ + int ret = 0; + pid_t pid; + struct stat status; + const char *viewer_bin = NULL; + struct viewers *viewer; + char **argv = NULL; + + /* Check for --viewer options */ + viewer = parse_options(); + if (viewer == NULL) { + ret = CMD_ERROR; + goto error; + } + + pid = fork(); + if (pid == 0) { + switch (viewer->type) { + case VIEWER_BABELTRACE: + if (stat(babeltrace_bin, &status) == 0) { + viewer_bin = babeltrace_bin; + } else { + viewer_bin = viewer->exec_name; + } + argv = alloc_argv_from_local_opts(babeltrace_opts, + ARRAY_SIZE(babeltrace_opts), trace_path); + break; +#if 0 + case VIEWER_LTTV_GUI: + if (stat(lttv_gui_bin, &status) == 0) { + viewer_bin = lttv_gui_bin; + } else { + viewer_bin = viewer->exec_name; + } + argv = alloc_argv_from_local_opts(lttv_gui_opts, + ARRAY_SIZE(lttv_gui_opts), trace_path); + break; +#endif + case VIEWER_USER_DEFINED: + argv = alloc_argv_from_user_opts(opt_viewer, trace_path); + if (argv) { + viewer_bin = argv[0]; + } + break; + default: + viewer_bin = viewers[VIEWER_BABELTRACE].exec_name; + argv = alloc_argv_from_local_opts(babeltrace_opts, + ARRAY_SIZE(babeltrace_opts), trace_path); + break; + } + + if (argv == NULL) { + ret = CMD_FATAL; + goto error; + } + + DBG("Using %s viewer", viewer_bin); + + ret = execvp(viewer_bin, argv); + if (ret) { + PERROR("exec: %s", viewer_bin); + free(argv); + ret = CMD_FATAL; + goto error; + } + } else if (pid > 0) { + ret = CMD_SUCCESS; + } else { + PERROR("Fork trace viewer"); + ret = CMD_FATAL; + } + +error: + return ret; +} + +/* + * Exec viewer if found and use session name path. + */ +static int view_trace(void) +{ + int ret, count, i, found = 0; + char *session_name; + struct lttng_session *sessions = NULL; + + /* + * Safety net. If lttng is suid at some point for *any* useless reasons, + * this prevent any bad execution of binraries. + */ + if (getuid() != 0) { + if (getuid() != geteuid()) { + ERR("UID does not match effective UID."); + goto error; + } else if (getgid() != getegid()) { + ERR("GID does not match effective GID."); + goto error; + } + } + + if (opt_session_name == NULL) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto error; + } + } else { + session_name = opt_session_name; + } + + DBG("Viewing trace for session %s", session_name); + + /* Getting all sessions */ + count = lttng_list_sessions(&sessions); + if (count < 0) { + ERR("Unable to list sessions. Session name %s not found.", + session_name); + MSG("Is there a session daemon running?"); + ret = CMD_ERROR; + goto free_error; + } + + /* Find our session listed by the session daemon */ + for (i = 0; i < count; i++) { + if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { + found = 1; + break; + } + } + + if (!found) { + MSG("Session name %s not found", session_name); + goto free_sessions; + } + + MSG("Trace directory: %s\n", sessions[i].path); + + ret = spawn_viewer(sessions[i].path); + if (ret < 0) { + /* Don't set ret so lttng can interpret the sessiond error. */ + goto free_sessions; + } + + ret = CMD_SUCCESS; + +free_sessions: + if (sessions) { + free(sessions); + } +free_error: + if (opt_session_name == NULL) { + free(session_name); + } +error: + return ret; +} + +/* + * The 'view ' first level command + */ +int cmd_view(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stdout); + goto end; + case OPT_LIST_OPTIONS: + list_cmd_options(stdout, long_options); + goto end; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_session_name = (char*) poptGetArg(pc); + + ret = view_trace(); + +end: + poptFreeContext(pc); + return ret; +} diff --git a/src/bin/lttng/lttng.c b/src/bin/lttng/lttng.c index f84ba63d7..1c1d5a826 100644 --- a/src/bin/lttng/lttng.c +++ b/src/bin/lttng/lttng.c @@ -76,6 +76,7 @@ static struct cmd_struct commands[] = { { "set-session", cmd_set_session}, { "version", cmd_version}, { "calibrate", cmd_calibrate}, + { "view", cmd_view}, { NULL, NULL} /* Array closure */ }; @@ -108,6 +109,7 @@ static void usage(FILE *ofp) fprintf(ofp, " start Start tracing\n"); fprintf(ofp, " stop Stop tracing\n"); fprintf(ofp, " version Show version information\n"); + fprintf(ofp, " view Start trace viewer\n"); fprintf(ofp, "\n"); fprintf(ofp, "Each command also has its own -h, --help option.\n"); fprintf(ofp, "\n"); @@ -381,6 +383,7 @@ static int check_sessiond(void) } end: + printf("HIT essiond %d\n", ret); return ret; } @@ -399,7 +402,8 @@ static int check_args_no_sessiond(int argc, char **argv) strncmp(argv[i], "--h", sizeof("--h")) == 0 || strncmp(argv[i], "--list-options", sizeof("--list-options")) == 0 || strncmp(argv[i], "--list-commands", sizeof("--list-commands")) == 0 || - strncmp(argv[i], "version", sizeof("version")) == 0) { + strncmp(argv[i], "version", sizeof("version")) == 0 || + strncmp(argv[i], "view", sizeof("view")) == 0) { return 1; } } -- 2.34.1