X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng%2Fcommands%2Fenable_events.c;h=2187f702954c857518e91ed2fc21a4ed7df80dd1;hp=b00324b2947be0f61e193a55bb2ca0eb27695c7a;hb=2463b7879c00298daa79744cdaae82ac061a4ed8;hpb=8692d4e5805da0104c690972bb96b60b8cb52685 diff --git a/src/bin/lttng/commands/enable_events.c b/src/bin/lttng/commands/enable_events.c index b00324b29..2187f7029 100644 --- a/src/bin/lttng/commands/enable_events.c +++ b/src/bin/lttng/commands/enable_events.c @@ -1,33 +1,39 @@ /* - * Copyright (C) 2011 - David Goulet + * 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, version 2 only, - * as published by the Free Software Foundation. + * SPDX-License-Identifier: GPL-2.0-only * - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#define _GNU_SOURCE +#define _LGPL_SOURCE +#include #include #include #include -#include #include #include #include #include #include +#include +#include +#include +#include +#include + +#include +/* Mi dependancy */ +#include + +#include + #include "../command.h" -#include +#include "../uprobe.h" + +#if (LTTNG_SYMBOL_NAME_LEN == 256) +#define LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "255" +#endif static char *opt_event_list; static int opt_event_type; @@ -36,33 +42,40 @@ static int opt_loglevel_type; static int opt_kernel; static char *opt_session_name; static int opt_userspace; +static int opt_jul; +static int opt_log4j; +static int opt_python; static int opt_enable_all; static char *opt_probe; +static char *opt_userspace_probe; static char *opt_function; -static char *opt_function_entry_symbol; static char *opt_channel_name; static char *opt_filter; -#if 0 -/* Not implemented yet */ -static char *opt_cmd_name; -static pid_t opt_pid; +static char *opt_exclude; + +#ifdef LTTNG_EMBED_HELP +static const char help_msg[] = +#include +; #endif enum { OPT_HELP = 1, OPT_TRACEPOINT, OPT_PROBE, + OPT_USERSPACE_PROBE, OPT_FUNCTION, - OPT_FUNCTION_ENTRY, OPT_SYSCALL, OPT_USERSPACE, OPT_LOGLEVEL, OPT_LOGLEVEL_ONLY, OPT_LIST_OPTIONS, OPT_FILTER, + OPT_EXCLUDE, }; static struct lttng_handle *handle; +static struct mi_writer *writer; static struct poptOption long_options[] = { /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ @@ -72,149 +85,48 @@ static struct poptOption long_options[] = { {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, + {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0}, + {"log4j", 'l', POPT_ARG_VAL, &opt_log4j, 1, 0, 0}, + {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, {"tracepoint", 0, POPT_ARG_NONE, 0, OPT_TRACEPOINT, 0, 0}, {"probe", 0, POPT_ARG_STRING, &opt_probe, OPT_PROBE, 0, 0}, + {"userspace-probe",0, POPT_ARG_STRING, &opt_userspace_probe, OPT_USERSPACE_PROBE, 0, 0}, {"function", 0, POPT_ARG_STRING, &opt_function, OPT_FUNCTION, 0, 0}, -#if 0 - /* - * Currently removed from lttng kernel tracer. Removed from - * lttng UI to discourage its use. - */ - {"function:entry", 0, POPT_ARG_STRING, &opt_function_entry_symbol, OPT_FUNCTION_ENTRY, 0, 0}, -#endif {"syscall", 0, POPT_ARG_NONE, 0, OPT_SYSCALL, 0, 0}, {"loglevel", 0, POPT_ARG_STRING, 0, OPT_LOGLEVEL, 0, 0}, {"loglevel-only", 0, POPT_ARG_STRING, 0, OPT_LOGLEVEL_ONLY, 0, 0}, {"list-options", 0, POPT_ARG_NONE, NULL, OPT_LIST_OPTIONS, NULL, NULL}, {"filter", 'f', POPT_ARG_STRING, &opt_filter, OPT_FILTER, 0, 0}, + {"exclude", 'x', POPT_ARG_STRING, &opt_exclude, OPT_EXCLUDE, 0, 0}, {0, 0, 0, 0, 0, 0, 0} }; -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng enable-event NAME[,NAME2,...] [-k|-u] [OPTIONS] \n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Options:\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, " --list-options Simple listing of options\n"); - fprintf(ofp, " -s, --session NAME Apply to session name\n"); - fprintf(ofp, " -c, --channel NAME Apply to this channel\n"); - fprintf(ofp, " -a, --all Enable all tracepoints and syscalls\n"); - fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n"); - fprintf(ofp, " -u, --userspace Apply to the user-space tracer\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Event options:\n"); - fprintf(ofp, " --tracepoint Tracepoint event (default)\n"); - fprintf(ofp, " - userspace tracer supports wildcards at end of string.\n"); - fprintf(ofp, " Don't forget to quote to deal with bash expansion.\n"); - fprintf(ofp, " e.g.:\n"); - fprintf(ofp, " \"*\"\n"); - fprintf(ofp, " \"app_component:na*\"\n"); - fprintf(ofp, " --probe [addr | symbol | symbol+offset]\n"); - fprintf(ofp, " Dynamic probe.\n"); - fprintf(ofp, " Addr and offset can be octal (0NNN...),\n"); - fprintf(ofp, " decimal (NNN...) or hexadecimal (0xNNN...)\n"); - fprintf(ofp, " --function [addr | symbol | symbol+offset]\n"); - fprintf(ofp, " Dynamic function entry/return probe.\n"); - fprintf(ofp, " Addr and offset can be octal (0NNN...),\n"); - fprintf(ofp, " decimal (NNN...) or hexadecimal (0xNNN...)\n"); -#if 0 - fprintf(ofp, " --function:entry symbol\n"); - fprintf(ofp, " Function tracer event\n"); -#endif - fprintf(ofp, " --syscall System call event\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " --loglevel name\n"); - fprintf(ofp, " Tracepoint loglevel range from 0 to loglevel\n"); - fprintf(ofp, " --loglevel-only name\n"); - fprintf(ofp, " Tracepoint loglevel (only this loglevel)\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " The loglevel or loglevel-only options should be\n"); - fprintf(ofp, " combined with a tracepoint name or tracepoint\n"); - fprintf(ofp, " wildcard.\n"); - fprintf(ofp, " Available loglevels:\n"); - fprintf(ofp, " (higher value is more verbose)\n"); - fprintf(ofp, " TRACE_EMERG = 0\n"); - fprintf(ofp, " TRACE_ALERT = 1\n"); - fprintf(ofp, " TRACE_CRIT = 2\n"); - fprintf(ofp, " TRACE_ERR = 3\n"); - fprintf(ofp, " TRACE_WARNING = 4\n"); - fprintf(ofp, " TRACE_NOTICE = 5\n"); - fprintf(ofp, " TRACE_INFO = 6\n"); - fprintf(ofp, " TRACE_DEBUG_SYSTEM = 7\n"); - fprintf(ofp, " TRACE_DEBUG_PROGRAM = 8\n"); - fprintf(ofp, " TRACE_DEBUG_PROCESS = 9\n"); - fprintf(ofp, " TRACE_DEBUG_MODULE = 10\n"); - fprintf(ofp, " TRACE_DEBUG_UNIT = 11\n"); - fprintf(ofp, " TRACE_DEBUG_FUNCTION = 12\n"); - fprintf(ofp, " TRACE_DEBUG_LINE = 13\n"); - fprintf(ofp, " TRACE_DEBUG = 14\n"); - fprintf(ofp, " (shortcuts such as \"system\" are allowed)\n"); - fprintf(ofp, " --filter \'expression\'\n"); - fprintf(ofp, " Filter expression on event fields and context.\n"); - fprintf(ofp, " Event recording depends on evaluation.\n"); - fprintf(ofp, " Only specify on first activation of\n"); - fprintf(ofp, " a given event within a session.\n"); - fprintf(ofp, " Filter only allowed when enabling\n"); - fprintf(ofp, " events within a session before tracing\n"); - fprintf(ofp, " is started. If the filter fails to link\n"); - fprintf(ofp, " with the event within the traced domain,\n"); - fprintf(ofp, " the event will be discarded. Currently,\n"); - fprintf(ofp, " filter is only implemented for the user-space\n"); - fprintf(ofp, " tracer.\n"); - fprintf(ofp, " Expression examples:.\n"); - fprintf(ofp, " \n"); - fprintf(ofp, " 'intfield > 500 && intfield < 503'\n"); - fprintf(ofp, " '(stringfield == \"test\" || intfield != 10) && intfield > 33'\n"); - fprintf(ofp, " 'doublefield > 1.1 && intfield < 5.3'\n"); - fprintf(ofp, " \n"); - fprintf(ofp, " Wildcards are allowed at the end of strings:\n"); - fprintf(ofp, " 'seqfield1 == \"te*\"'\n"); - fprintf(ofp, " In string literals, the escape character is '\\'.\n"); - fprintf(ofp, " Use '\\*' for the '*' character, and '\\\\' for\n"); - fprintf(ofp, " the '\\' character. Wildcard match any sequence of,\n"); - fprintf(ofp, " characters including an empty sub-string (match 0 or\n"); - fprintf(ofp, " more characters).\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " Context information can be used for filtering. The\n"); - fprintf(ofp, " examples below show usage of context filtering on\n"); - fprintf(ofp, " process name (with a wildcard), process ID range, and\n"); - fprintf(ofp, " unique thread ID for filtering. The process and\n"); - fprintf(ofp, " thread ID of running applications can be found under\n"); - fprintf(ofp, " columns \"PID\" and \"LWP\" of the \"ps -eLf\" command.\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " '$ctx.procname == \"demo*\"'\n"); - fprintf(ofp, " '$ctx.vpid >= 4433 && $ctx.vpid < 4455'\n"); - fprintf(ofp, " '$ctx.vtid == 1234'\n"); - fprintf(ofp, "\n"); -} - /* * Parse probe options. */ static int parse_probe_opts(struct lttng_event *ev, char *opt) { - int ret; + int ret = CMD_SUCCESS; + int match; char s_hex[19]; +#define S_HEX_LEN_SCANF_IS_A_BROKEN_API "18" /* 18 is (19 - 1) (\0 is extra) */ char name[LTTNG_SYMBOL_NAME_LEN]; if (opt == NULL) { - ret = -1; + ret = CMD_ERROR; goto end; } /* Check for symbol+offset */ - ret = sscanf(opt, "%[^'+']+%s", name, s_hex); - if (ret == 2) { + match = sscanf(opt, "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API + "[^'+']+%" S_HEX_LEN_SCANF_IS_A_BROKEN_API "s", name, s_hex); + if (match == 2) { strncpy(ev->attr.probe.symbol_name, name, LTTNG_SYMBOL_NAME_LEN); ev->attr.probe.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; DBG("probe symbol %s", ev->attr.probe.symbol_name); if (*s_hex == '\0') { ERR("Invalid probe offset %s", s_hex); - ret = -1; + ret = CMD_ERROR; goto end; } ev->attr.probe.offset = strtoul(s_hex, NULL, 0); @@ -224,9 +136,10 @@ static int parse_probe_opts(struct lttng_event *ev, char *opt) } /* Check for symbol */ - if (isalpha(name[0])) { - ret = sscanf(opt, "%s", name); - if (ret == 1) { + if (isalpha(name[0]) || name[0] == '_') { + match = sscanf(opt, "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API "s", + name); + if (match == 1) { strncpy(ev->attr.probe.symbol_name, name, LTTNG_SYMBOL_NAME_LEN); ev->attr.probe.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; DBG("probe symbol %s", ev->attr.probe.symbol_name); @@ -238,11 +151,16 @@ static int parse_probe_opts(struct lttng_event *ev, char *opt) } /* Check for address */ - ret = sscanf(opt, "%s", s_hex); - if (ret > 0) { - if (*s_hex == '\0') { - ERR("Invalid probe address %s", s_hex); - ret = -1; + match = sscanf(opt, "%" S_HEX_LEN_SCANF_IS_A_BROKEN_API "s", s_hex); + if (match > 0) { + /* + * Return an error if the first character of the tentative + * address is NULL or not a digit. It can be "0" if the address + * is in hexadecimal and can be 1 to 9 if it's in decimal. + */ + if (*s_hex == '\0' || !isdigit(*s_hex)) { + ERR("Invalid probe description %s", s_hex); + ret = CMD_ERROR; goto end; } ev->attr.probe.addr = strtoul(s_hex, NULL, 0); @@ -253,21 +171,155 @@ static int parse_probe_opts(struct lttng_event *ev, char *opt) } /* No match */ - ret = -1; + ret = CMD_ERROR; end: return ret; } +/* + * Maps LOG4j loglevel from string to value + */ +LTTNG_HIDDEN +int loglevel_log4j_str_to_value(const char *inputstr) +{ + int i = 0; + char str[LTTNG_SYMBOL_NAME_LEN]; + + if (!inputstr || strlen(inputstr) == 0) { + return -1; + } + + /* + * Loop up to LTTNG_SYMBOL_NAME_LEN minus one because the NULL bytes is + * added at the end of the loop so a the upper bound we avoid the overflow. + */ + while (i < (LTTNG_SYMBOL_NAME_LEN - 1) && inputstr[i] != '\0') { + str[i] = toupper(inputstr[i]); + i++; + } + str[i] = '\0'; + + if (!strcmp(str, "LOG4J_OFF") || !strcmp(str, "OFF")) { + return LTTNG_LOGLEVEL_LOG4J_OFF; + } else if (!strcmp(str, "LOG4J_FATAL") || !strcmp(str, "FATAL")) { + return LTTNG_LOGLEVEL_LOG4J_FATAL; + } else if (!strcmp(str, "LOG4J_ERROR") || !strcmp(str, "ERROR")) { + return LTTNG_LOGLEVEL_LOG4J_ERROR; + } else if (!strcmp(str, "LOG4J_WARN") || !strcmp(str, "WARN")) { + return LTTNG_LOGLEVEL_LOG4J_WARN; + } else if (!strcmp(str, "LOG4J_INFO") || !strcmp(str, "INFO")) { + return LTTNG_LOGLEVEL_LOG4J_INFO; + } else if (!strcmp(str, "LOG4J_DEBUG") || !strcmp(str, "DEBUG")) { + return LTTNG_LOGLEVEL_LOG4J_DEBUG; + } else if (!strcmp(str, "LOG4J_TRACE") || !strcmp(str, "TRACE")) { + return LTTNG_LOGLEVEL_LOG4J_TRACE; + } else if (!strcmp(str, "LOG4J_ALL") || !strcmp(str, "ALL")) { + return LTTNG_LOGLEVEL_LOG4J_ALL; + } else { + return -1; + } +} + +/* + * Maps JUL loglevel from string to value + */ +LTTNG_HIDDEN +int loglevel_jul_str_to_value(const char *inputstr) +{ + int i = 0; + char str[LTTNG_SYMBOL_NAME_LEN]; + + if (!inputstr || strlen(inputstr) == 0) { + return -1; + } + + /* + * Loop up to LTTNG_SYMBOL_NAME_LEN minus one because the NULL bytes is + * added at the end of the loop so a the upper bound we avoid the overflow. + */ + while (i < (LTTNG_SYMBOL_NAME_LEN - 1) && inputstr[i] != '\0') { + str[i] = toupper(inputstr[i]); + i++; + } + str[i] = '\0'; + + if (!strcmp(str, "JUL_OFF") || !strcmp(str, "OFF")) { + return LTTNG_LOGLEVEL_JUL_OFF; + } else if (!strcmp(str, "JUL_SEVERE") || !strcmp(str, "SEVERE")) { + return LTTNG_LOGLEVEL_JUL_SEVERE; + } else if (!strcmp(str, "JUL_WARNING") || !strcmp(str, "WARNING")) { + return LTTNG_LOGLEVEL_JUL_WARNING; + } else if (!strcmp(str, "JUL_INFO") || !strcmp(str, "INFO")) { + return LTTNG_LOGLEVEL_JUL_INFO; + } else if (!strcmp(str, "JUL_CONFIG") || !strcmp(str, "CONFIG")) { + return LTTNG_LOGLEVEL_JUL_CONFIG; + } else if (!strcmp(str, "JUL_FINE") || !strcmp(str, "FINE")) { + return LTTNG_LOGLEVEL_JUL_FINE; + } else if (!strcmp(str, "JUL_FINER") || !strcmp(str, "FINER")) { + return LTTNG_LOGLEVEL_JUL_FINER; + } else if (!strcmp(str, "JUL_FINEST") || !strcmp(str, "FINEST")) { + return LTTNG_LOGLEVEL_JUL_FINEST; + } else if (!strcmp(str, "JUL_ALL") || !strcmp(str, "ALL")) { + return LTTNG_LOGLEVEL_JUL_ALL; + } else { + return -1; + } +} + +/* + * Maps Python loglevel from string to value + */ +LTTNG_HIDDEN +int loglevel_python_str_to_value(const char *inputstr) +{ + int i = 0; + char str[LTTNG_SYMBOL_NAME_LEN]; + + if (!inputstr || strlen(inputstr) == 0) { + return -1; + } + + /* + * Loop up to LTTNG_SYMBOL_NAME_LEN minus one because the NULL bytes is + * added at the end of the loop so a the upper bound we avoid the overflow. + */ + while (i < (LTTNG_SYMBOL_NAME_LEN - 1) && inputstr[i] != '\0') { + str[i] = toupper(inputstr[i]); + i++; + } + str[i] = '\0'; + + if (!strcmp(str, "PYTHON_CRITICAL") || !strcmp(str, "CRITICAL")) { + return LTTNG_LOGLEVEL_PYTHON_CRITICAL; + } else if (!strcmp(str, "PYTHON_ERROR") || !strcmp(str, "ERROR")) { + return LTTNG_LOGLEVEL_PYTHON_ERROR; + } else if (!strcmp(str, "PYTHON_WARNING") || !strcmp(str, "WARNING")) { + return LTTNG_LOGLEVEL_PYTHON_WARNING; + } else if (!strcmp(str, "PYTHON_INFO") || !strcmp(str, "INFO")) { + return LTTNG_LOGLEVEL_PYTHON_INFO; + } else if (!strcmp(str, "PYTNON_DEBUG") || !strcmp(str, "DEBUG")) { + return LTTNG_LOGLEVEL_PYTHON_DEBUG; + } else if (!strcmp(str, "PYTHON_NOTSET") || !strcmp(str, "NOTSET")) { + return LTTNG_LOGLEVEL_PYTHON_NOTSET; + } else { + return -1; + } +} + /* * Maps loglevel from string to value */ -static +LTTNG_HIDDEN int loglevel_str_to_value(const char *inputstr) { int i = 0; char str[LTTNG_SYMBOL_NAME_LEN]; + if (!inputstr || strlen(inputstr) == 0) { + return -1; + } + /* * Loop up to LTTNG_SYMBOL_NAME_LEN minus one because the NULL bytes is * added at the end of the loop so a the upper bound we avoid the overflow. @@ -312,24 +364,252 @@ int loglevel_str_to_value(const char *inputstr) } } +static +const char *print_channel_name(const char *name) +{ + return name ? : DEFAULT_CHANNEL_NAME; +} + +static +const char *print_raw_channel_name(const char *name) +{ + return name ? : ""; +} + +/* + * Mi print exlcusion list + */ +static +int mi_print_exclusion(char **names) +{ + int i, ret; + int count = names ? strutils_array_of_strings_len(names) : 0; + + assert(writer); + + if (count == 0) { + ret = 0; + goto end; + } + ret = mi_lttng_writer_open_element(writer, config_element_exclusions); + if (ret) { + goto end; + } + + for (i = 0; i < count; i++) { + ret = mi_lttng_writer_write_element_string(writer, + config_element_exclusion, names[i]); + if (ret) { + goto end; + } + } + + /* Close exclusions element */ + ret = mi_lttng_writer_close_element(writer); + +end: + return ret; +} + +/* + * Return allocated string for pretty-printing exclusion names. + */ +static +char *print_exclusions(char **names) +{ + int length = 0; + int i; + const char preamble[] = " excluding "; + char *ret; + int count = names ? strutils_array_of_strings_len(names) : 0; + + if (count == 0) { + return strdup(""); + } + + /* calculate total required length */ + for (i = 0; i < count; i++) { + length += strlen(names[i]) + 4; + } + + length += sizeof(preamble); + ret = zmalloc(length); + if (!ret) { + return NULL; + } + strncpy(ret, preamble, length); + for (i = 0; i < count; i++) { + strcat(ret, "\""); + strcat(ret, names[i]); + strcat(ret, "\""); + if (i != count - 1) { + strcat(ret, ", "); + } + } + + return ret; +} + +static +int check_exclusion_subsets(const char *event_name, const char *exclusion) +{ + bool warn = false; + int ret = 0; + const char *e = event_name; + const char *x = exclusion; + + /* Scan both the excluder and the event letter by letter */ + while (true) { + if (*e == '\\') { + if (*x != *e) { + warn = true; + goto end; + } + + e++; + x++; + goto cmp_chars; + } + + if (*x == '*') { + /* Event is a subset of the excluder */ + ERR("Event %s: %s excludes all events from %s", + event_name, exclusion, event_name); + goto error; + } + + if (*e == '*') { + /* + * Reached the end of the event name before the + * end of the exclusion: this is valid. + */ + goto end; + } + +cmp_chars: + if (*x != *e) { + warn = true; + break; + } + + x++; + e++; + } + + goto end; + +error: + ret = -1; + +end: + if (warn) { + WARN("Event %s: %s does not exclude any events from %s", + event_name, exclusion, event_name); + } + + return ret; +} + +/* + * FIXME: find a good place to declare this since add trigger also uses it + */ +LTTNG_HIDDEN +int create_exclusion_list_and_validate(const char *event_name, + const char *exclusions_arg, + char ***exclusion_list); + + +LTTNG_HIDDEN +int create_exclusion_list_and_validate(const char *event_name, + const char *exclusions_arg, + char ***exclusion_list) +{ + int ret = 0; + char **exclusions = NULL; + + /* Event name must be a valid globbing pattern to allow exclusions. */ + if (!strutils_is_star_glob_pattern(event_name)) { + ERR("Event %s: Exclusions can only be used with a globbing pattern", + event_name); + goto error; + } + + /* Split exclusions. */ + exclusions = strutils_split(exclusions_arg, ',', true); + if (!exclusions) { + goto error; + } + + /* + * If the event name is a star-at-end only globbing pattern, + * then we can validate the individual exclusions. Otherwise + * all exclusions are passed to the session daemon. + */ + if (strutils_is_star_at_the_end_only_glob_pattern(event_name)) { + char * const *exclusion; + + for (exclusion = exclusions; *exclusion; exclusion++) { + if (!strutils_is_star_glob_pattern(*exclusion) || + strutils_is_star_at_the_end_only_glob_pattern(*exclusion)) { + ret = check_exclusion_subsets(event_name, *exclusion); + if (ret) { + goto error; + } + } + } + } + + *exclusion_list = exclusions; + + goto end; + +error: + ret = -1; + strutils_free_null_terminated_array_of_strings(exclusions); + +end: + return ret; +} + +static void warn_on_truncated_exclusion_names(char * const *exclusion_list, + int *warn) +{ + char * const *exclusion; + + for (exclusion = exclusion_list; *exclusion; exclusion++) { + if (strlen(*exclusion) >= LTTNG_SYMBOL_NAME_LEN) { + WARN("Event exclusion \"%s\" will be truncated", + *exclusion); + *warn = 1; + } + } +} + /* * Enabling event using the lttng API. + * Note: in case of error only the last error code will be return. */ static int enable_events(char *session_name) { - int err, ret = CMD_SUCCESS, warn = 0; + int ret = CMD_SUCCESS, command_ret = CMD_SUCCESS; + int error_holder = CMD_SUCCESS, warn = 0, error = 0, success = 1; char *event_name, *channel_name = NULL; - struct lttng_event ev; + struct lttng_event *ev; struct lttng_domain dom; + char **exclusion_list = NULL; + struct lttng_userspace_probe_location *uprobe_loc = NULL; - memset(&ev, 0, sizeof(ev)); memset(&dom, 0, sizeof(dom)); + ev = lttng_event_create(); + if (!ev) { + ret = CMD_ERROR; + goto error; + } + if (opt_kernel) { - if (opt_filter) { - ERR("Filter not implement for kernel tracing yet"); - ret = CMD_ERROR; - goto error; + if (opt_loglevel) { + WARN("Kernel loglevels are not supported."); } } @@ -341,62 +621,167 @@ static int enable_events(char *session_name) dom.type = LTTNG_DOMAIN_UST; /* Default. */ dom.buf_type = LTTNG_BUFFER_PER_UID; + } else if (opt_jul) { + dom.type = LTTNG_DOMAIN_JUL; + /* Default. */ + dom.buf_type = LTTNG_BUFFER_PER_UID; + } else if (opt_log4j) { + dom.type = LTTNG_DOMAIN_LOG4J; + /* Default. */ + dom.buf_type = LTTNG_BUFFER_PER_UID; + } else if (opt_python) { + dom.type = LTTNG_DOMAIN_PYTHON; + /* Default. */ + dom.buf_type = LTTNG_BUFFER_PER_UID; } else { - ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); - ret = CMD_ERROR; - goto error; + /* Checked by the caller. */ + assert(0); } - if (opt_channel_name == NULL) { - err = asprintf(&channel_name, DEFAULT_CHANNEL_NAME); - if (err < 0) { - ret = CMD_FATAL; + if (opt_exclude) { + switch (dom.type) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + ERR("Event name exclusions are not yet implemented for %s events", + get_domain_str(dom.type)); + ret = CMD_ERROR; goto error; + case LTTNG_DOMAIN_UST: + /* Exclusions supported */ + break; + default: + assert(0); } - } else { - channel_name = opt_channel_name; } + /* + * Adding a filter to a probe, function or userspace-probe would be + * denied by the kernel tracer as it's not supported at the moment. We + * do an early check here to warn the user. + */ + if (opt_filter && opt_kernel) { + switch (opt_event_type) { + case LTTNG_EVENT_ALL: + case LTTNG_EVENT_TRACEPOINT: + case LTTNG_EVENT_SYSCALL: + break; + case LTTNG_EVENT_PROBE: + case LTTNG_EVENT_USERSPACE_PROBE: + case LTTNG_EVENT_FUNCTION: + ERR("Filter expressions are not supported for %s events", + get_event_type_str(opt_event_type)); + ret = CMD_ERROR; + goto error; + default: + ret = CMD_UNDEFINED; + goto error; + } + } + + channel_name = opt_channel_name; + handle = lttng_create_handle(session_name, &dom); if (handle == NULL) { ret = -1; goto error; } + /* Prepare Mi */ + if (lttng_opt_mi) { + /* Open a events element */ + ret = mi_lttng_writer_open_element(writer, config_element_events); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + if (opt_enable_all) { /* Default setup for enable all */ if (opt_kernel) { - ev.type = opt_event_type; - ev.name[0] = '\0'; + ev->type = opt_event_type; + strcpy(ev->name, "*"); /* kernel loglevels not implemented */ - ev.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + ev->loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; } else { - ev.type = LTTNG_EVENT_TRACEPOINT; - strcpy(ev.name, "*"); - ev.loglevel_type = opt_loglevel_type; + ev->type = LTTNG_EVENT_TRACEPOINT; + strcpy(ev->name, "*"); + ev->loglevel_type = opt_loglevel_type; if (opt_loglevel) { - ev.loglevel = loglevel_str_to_value(opt_loglevel); - if (ev.loglevel == -1) { + assert(opt_userspace || opt_jul || opt_log4j || opt_python); + if (opt_userspace) { + ev->loglevel = loglevel_str_to_value(opt_loglevel); + } else if (opt_jul) { + ev->loglevel = loglevel_jul_str_to_value(opt_loglevel); + } else if (opt_log4j) { + ev->loglevel = loglevel_log4j_str_to_value(opt_loglevel); + } else if (opt_python) { + ev->loglevel = loglevel_python_str_to_value(opt_loglevel); + } + if (ev->loglevel == -1) { ERR("Unknown loglevel %s", opt_loglevel); ret = -LTTNG_ERR_INVALID; goto error; } } else { - ev.loglevel = -1; + assert(opt_userspace || opt_jul || opt_log4j || opt_python); + if (opt_userspace) { + ev->loglevel = -1; + } else if (opt_jul) { + ev->loglevel = LTTNG_LOGLEVEL_JUL_ALL; + } else if (opt_log4j) { + ev->loglevel = LTTNG_LOGLEVEL_LOG4J_ALL; + } else if (opt_python) { + ev->loglevel = LTTNG_LOGLEVEL_PYTHON_DEBUG; + } } } + if (opt_exclude) { + ret = create_exclusion_list_and_validate("*", + opt_exclude, &exclusion_list); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + ev->exclusion = 1; + warn_on_truncated_exclusion_names(exclusion_list, + &warn); + } if (!opt_filter) { - ret = lttng_enable_event(handle, &ev, channel_name); + ret = lttng_enable_event_with_exclusions(handle, + ev, channel_name, + NULL, + exclusion_list ? strutils_array_of_strings_len(exclusion_list) : 0, + exclusion_list); if (ret < 0) { switch (-ret) { case LTTNG_ERR_KERN_EVENT_EXIST: WARN("Kernel events already enabled (channel %s, session %s)", - channel_name, session_name); + print_channel_name(channel_name), session_name); + warn = 1; + break; + case LTTNG_ERR_TRACE_ALREADY_STARTED: + { + const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; + ERR("Events: %s (channel %s, session %s)", + msg, + print_channel_name(channel_name), + session_name); + error = 1; break; + } default: ERR("Events: %s (channel %s, session %s)", - lttng_strerror(ret), channel_name, session_name); + lttng_strerror(ret), + ret == -LTTNG_ERR_NEED_CHANNEL_NAME + ? print_raw_channel_name(channel_name) + : print_channel_name(channel_name), + session_name); + error = 1; break; } goto end; @@ -404,30 +789,70 @@ static int enable_events(char *session_name) switch (opt_event_type) { case LTTNG_EVENT_TRACEPOINT: - if (opt_loglevel) { - MSG("All %s tracepoints are enabled in channel %s for loglevel %s", - opt_kernel ? "kernel" : "UST", channel_name, + if (opt_loglevel && dom.type != LTTNG_DOMAIN_KERNEL) { + char *exclusion_string = print_exclusions(exclusion_list); + + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + MSG("All %s tracepoints%s are enabled in channel %s for loglevel %s", + get_domain_str(dom.type), + exclusion_string, + print_channel_name(channel_name), opt_loglevel); + free(exclusion_string); } else { - MSG("All %s tracepoints are enabled in channel %s", - opt_kernel ? "kernel" : "UST", channel_name); + char *exclusion_string = print_exclusions(exclusion_list); + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + MSG("All %s tracepoints%s are enabled in channel %s", + get_domain_str(dom.type), + exclusion_string, + print_channel_name(channel_name)); + free(exclusion_string); } break; case LTTNG_EVENT_SYSCALL: if (opt_kernel) { - MSG("All kernel system calls are enabled in channel %s", - channel_name); + MSG("All %s system calls are enabled in channel %s", + get_domain_str(dom.type), + print_channel_name(channel_name)); } break; case LTTNG_EVENT_ALL: - if (opt_loglevel) { - MSG("All %s events are enabled in channel %s for loglevel %s", - opt_kernel ? "kernel" : "UST", channel_name, + if (opt_loglevel && dom.type != LTTNG_DOMAIN_KERNEL) { + char *exclusion_string = print_exclusions(exclusion_list); + + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + MSG("All %s events%s are enabled in channel %s for loglevel %s", + get_domain_str(dom.type), + exclusion_string, + print_channel_name(channel_name), opt_loglevel); + free(exclusion_string); } else { - MSG("All %s events are enabled in channel %s", - opt_kernel ? "kernel" : "UST", channel_name); + char *exclusion_string = print_exclusions(exclusion_list); + + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + MSG("All %s events%s are enabled in channel %s", + get_domain_str(dom.type), + exclusion_string, + print_channel_name(channel_name)); + free(exclusion_string); } break; default: @@ -438,27 +863,93 @@ static int enable_events(char *session_name) goto error; } } + if (opt_filter) { - ret = lttng_enable_event_with_filter(handle, &ev, channel_name, - opt_filter); - if (ret < 0) { - switch (-ret) { + command_ret = lttng_enable_event_with_exclusions(handle, ev, channel_name, + opt_filter, + exclusion_list ? strutils_array_of_strings_len(exclusion_list) : 0, + exclusion_list); + if (command_ret < 0) { + switch (-command_ret) { case LTTNG_ERR_FILTER_EXIST: - WARN("Filter on events is already enabled" + WARN("Filter on all events is already enabled" " (channel %s, session %s)", - channel_name, session_name); + print_channel_name(channel_name), session_name); + warn = 1; + break; + case LTTNG_ERR_TRACE_ALREADY_STARTED: + { + const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; + ERR("All events: %s (channel %s, session %s, filter \'%s\')", + msg, + print_channel_name(channel_name), + session_name, opt_filter); + error = 1; break; - case LTTNG_ERR_FILTER_INVAL: - case LTTNG_ERR_FILTER_NOMEM: + } default: - ERR("%s", lttng_strerror(ret)); + ERR("All events: %s (channel %s, session %s, filter \'%s\')", + lttng_strerror(command_ret), + command_ret == -LTTNG_ERR_NEED_CHANNEL_NAME + ? print_raw_channel_name(channel_name) + : print_channel_name(channel_name), + session_name, opt_filter); + error = 1; break; } - goto error; + error_holder = command_ret; } else { + ev->filter = 1; MSG("Filter '%s' successfully set", opt_filter); } } + + if (lttng_opt_mi) { + /* The wildcard * is used for kernel and ust domain to + * represent ALL. We copy * in event name to force the wildcard use + * for kernel domain + * + * Note: this is strictly for semantic and printing while in + * machine interface mode. + */ + strcpy(ev->name, "*"); + + /* If we reach here the events are enabled */ + if (!error && !warn) { + ev->enabled = 1; + } else { + ev->enabled = 0; + success = 0; + } + ret = mi_lttng_event(writer, ev, 1, handle->domain.type); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* print exclusion */ + ret = mi_print_exclusion(exclusion_list); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Close event element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } + goto end; } @@ -466,165 +957,387 @@ static int enable_events(char *session_name) event_name = strtok(opt_event_list, ","); while (event_name != NULL) { /* Copy name and type of the event */ - strncpy(ev.name, event_name, LTTNG_SYMBOL_NAME_LEN); - ev.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - ev.type = opt_event_type; + strncpy(ev->name, event_name, LTTNG_SYMBOL_NAME_LEN); + ev->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + ev->type = opt_event_type; /* Kernel tracer action */ if (opt_kernel) { DBG("Enabling kernel event %s for channel %s", - event_name, channel_name); + event_name, + print_channel_name(channel_name)); switch (opt_event_type) { - case LTTNG_EVENT_ALL: /* Default behavior is tracepoint */ - ev.type = LTTNG_EVENT_TRACEPOINT; - /* Fall-through */ + case LTTNG_EVENT_ALL: /* Enable tracepoints and syscalls */ + /* If event name differs from *, select tracepoint. */ + if (strcmp(ev->name, "*")) { + ev->type = LTTNG_EVENT_TRACEPOINT; + } + break; case LTTNG_EVENT_TRACEPOINT: break; case LTTNG_EVENT_PROBE: - ret = parse_probe_opts(&ev, opt_probe); - if (ret < 0) { + ret = parse_probe_opts(ev, opt_probe); + if (ret) { ERR("Unable to parse probe options"); - ret = 0; + ret = CMD_ERROR; goto error; } break; + case LTTNG_EVENT_USERSPACE_PROBE: + assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE); + + ret = parse_userspace_probe_opts(opt_userspace_probe, &uprobe_loc); + if (ret) { + switch (ret) { + case CMD_UNSUPPORTED: + /* + * Error message describing + * what is not supported was + * printed in the function. + */ + break; + case CMD_ERROR: + default: + ERR("Unable to parse userspace probe options"); + break; + } + goto error; + } + + ret = lttng_event_set_userspace_probe_location(ev, uprobe_loc); + if (ret) { + WARN("Failed to set probe location on event"); + ret = CMD_ERROR; + goto error; + } + + /* Ownership of the uprobe location was transferred to the event. */ + uprobe_loc = NULL; + break; case LTTNG_EVENT_FUNCTION: - ret = parse_probe_opts(&ev, opt_function); - if (ret < 0) { + ret = parse_probe_opts(ev, opt_function); + if (ret) { ERR("Unable to parse function probe options"); - ret = 0; + ret = CMD_ERROR; goto error; } break; - case LTTNG_EVENT_FUNCTION_ENTRY: - strncpy(ev.attr.ftrace.symbol_name, opt_function_entry_symbol, - LTTNG_SYMBOL_NAME_LEN); - ev.attr.ftrace.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - break; case LTTNG_EVENT_SYSCALL: - MSG("per-syscall selection not supported yet. Use \"-a\" " - "for all syscalls."); + ev->type = LTTNG_EVENT_SYSCALL; + break; default: ret = CMD_UNDEFINED; goto error; } - if (opt_loglevel) { - MSG("Kernel loglevels are not supported."); - ret = CMD_UNSUPPORTED; - goto error; - } - /* kernel loglevels not implemented */ - ev.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + ev->loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; } else if (opt_userspace) { /* User-space tracer action */ -#if 0 - if (opt_cmd_name != NULL || opt_pid) { - MSG("Only supporting tracing all UST processes (-u) for now."); - ret = CMD_UNDEFINED; - goto error; - } -#endif - DBG("Enabling UST event %s for channel %s, loglevel %s", event_name, - channel_name, opt_loglevel ? : ""); + print_channel_name(channel_name), opt_loglevel ? : ""); switch (opt_event_type) { case LTTNG_EVENT_ALL: /* Default behavior is tracepoint */ /* Fall-through */ case LTTNG_EVENT_TRACEPOINT: /* Copy name and type of the event */ - ev.type = LTTNG_EVENT_TRACEPOINT; - strncpy(ev.name, event_name, LTTNG_SYMBOL_NAME_LEN); - ev.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + ev->type = LTTNG_EVENT_TRACEPOINT; + strncpy(ev->name, event_name, LTTNG_SYMBOL_NAME_LEN); + ev->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; break; case LTTNG_EVENT_PROBE: case LTTNG_EVENT_FUNCTION: - case LTTNG_EVENT_FUNCTION_ENTRY: case LTTNG_EVENT_SYSCALL: + case LTTNG_EVENT_USERSPACE_PROBE: default: ERR("Event type not available for user-space tracing"); ret = CMD_UNSUPPORTED; goto error; } - ev.loglevel_type = opt_loglevel_type; + if (opt_exclude) { + ev->exclusion = 1; + if (opt_event_type != LTTNG_EVENT_ALL && opt_event_type != LTTNG_EVENT_TRACEPOINT) { + ERR("Exclusion option can only be used with tracepoint events"); + ret = CMD_ERROR; + goto error; + } + /* Free previously allocated items */ + strutils_free_null_terminated_array_of_strings( + exclusion_list); + exclusion_list = NULL; + ret = create_exclusion_list_and_validate( + event_name, opt_exclude, + &exclusion_list); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + warn_on_truncated_exclusion_names( + exclusion_list, &warn); + } + + ev->loglevel_type = opt_loglevel_type; + if (opt_loglevel) { + ev->loglevel = loglevel_str_to_value(opt_loglevel); + if (ev->loglevel == -1) { + ERR("Unknown loglevel %s", opt_loglevel); + ret = -LTTNG_ERR_INVALID; + goto error; + } + } else { + ev->loglevel = -1; + } + } else if (opt_jul || opt_log4j || opt_python) { + if (opt_event_type != LTTNG_EVENT_ALL && + opt_event_type != LTTNG_EVENT_TRACEPOINT) { + ERR("Event type not supported for domain."); + ret = CMD_UNSUPPORTED; + goto error; + } + + ev->loglevel_type = opt_loglevel_type; if (opt_loglevel) { - ev.loglevel = loglevel_str_to_value(opt_loglevel); - if (ev.loglevel == -1) { + if (opt_jul) { + ev->loglevel = loglevel_jul_str_to_value(opt_loglevel); + } else if (opt_log4j) { + ev->loglevel = loglevel_log4j_str_to_value(opt_loglevel); + } else if (opt_python) { + ev->loglevel = loglevel_python_str_to_value(opt_loglevel); + } + if (ev->loglevel == -1) { ERR("Unknown loglevel %s", opt_loglevel); ret = -LTTNG_ERR_INVALID; goto error; } } else { - ev.loglevel = -1; + if (opt_jul) { + ev->loglevel = LTTNG_LOGLEVEL_JUL_ALL; + } else if (opt_log4j) { + ev->loglevel = LTTNG_LOGLEVEL_LOG4J_ALL; + } else if (opt_python) { + ev->loglevel = LTTNG_LOGLEVEL_PYTHON_DEBUG; + } } + ev->type = LTTNG_EVENT_TRACEPOINT; + strncpy(ev->name, event_name, LTTNG_SYMBOL_NAME_LEN); + ev->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; } else { - ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); - ret = CMD_ERROR; - goto error; + assert(0); } if (!opt_filter) { - ret = lttng_enable_event(handle, &ev, channel_name); - if (ret < 0) { + char *exclusion_string; + + command_ret = lttng_enable_event_with_exclusions(handle, + ev, channel_name, + NULL, + exclusion_list ? strutils_array_of_strings_len(exclusion_list) : 0, + exclusion_list); + exclusion_string = print_exclusions(exclusion_list); + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + if (command_ret < 0) { /* Turn ret to positive value to handle the positive error code */ - switch (-ret) { + switch (-command_ret) { case LTTNG_ERR_KERN_EVENT_EXIST: - WARN("Kernel event %s already enabled (channel %s, session %s)", - event_name, channel_name, session_name); + WARN("Kernel event %s%s already enabled (channel %s, session %s)", + event_name, + exclusion_string, + print_channel_name(channel_name), session_name); + warn = 1; + break; + case LTTNG_ERR_TRACE_ALREADY_STARTED: + { + const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; + ERR("Event %s%s: %s (channel %s, session %s)", event_name, + exclusion_string, + msg, + print_channel_name(channel_name), + session_name); + error = 1; + break; + } + case LTTNG_ERR_SDT_PROBE_SEMAPHORE: + ERR("SDT probes %s guarded by semaphores are not supported (channel %s, session %s)", + event_name, print_channel_name(channel_name), + session_name); + error = 1; break; default: - ERR("Event %s: %s (channel %s, session %s)", event_name, - lttng_strerror(ret), channel_name, session_name); + ERR("Event %s%s: %s (channel %s, session %s)", event_name, + exclusion_string, + lttng_strerror(command_ret), + command_ret == -LTTNG_ERR_NEED_CHANNEL_NAME + ? print_raw_channel_name(channel_name) + : print_channel_name(channel_name), + session_name); + error = 1; break; } - warn = 1; + error_holder = command_ret; } else { - MSG("%s event %s created in channel %s", - opt_kernel ? "kernel": "UST", event_name, channel_name); + switch (dom.type) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_UST: + MSG("%s event %s%s created in channel %s", + get_domain_str(dom.type), + event_name, + exclusion_string, + print_channel_name(channel_name)); + break; + case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: + /* + * Don't print the default channel + * name for agent domains. + */ + MSG("%s event %s%s enabled", + get_domain_str(dom.type), + event_name, + exclusion_string); + break; + default: + assert(0); + } } + free(exclusion_string); } if (opt_filter) { - ret = lttng_enable_event_with_filter(handle, &ev, channel_name, - opt_filter); - if (ret < 0) { - switch (-ret) { + char *exclusion_string; + + /* Filter present */ + ev->filter = 1; + + command_ret = lttng_enable_event_with_exclusions(handle, ev, channel_name, + opt_filter, + exclusion_list ? strutils_array_of_strings_len(exclusion_list) : 0, + exclusion_list); + exclusion_string = print_exclusions(exclusion_list); + if (!exclusion_string) { + PERROR("Cannot allocate exclusion_string"); + error = 1; + goto end; + } + if (command_ret < 0) { + switch (-command_ret) { case LTTNG_ERR_FILTER_EXIST: - WARN("Filter on event %s is already enabled" + WARN("Filter on event %s%s is already enabled" " (channel %s, session %s)", - event_name, channel_name, session_name); + event_name, + exclusion_string, + print_channel_name(channel_name), session_name); + warn = 1; + break; + case LTTNG_ERR_TRACE_ALREADY_STARTED: + { + const char *msg = "The command tried to enable an event in a new domain for a session that has already been started once."; + ERR("Event %s%s: %s (channel %s, session %s, filter \'%s\')", ev->name, + exclusion_string, + msg, + print_channel_name(channel_name), + session_name, opt_filter); + error = 1; break; - case LTTNG_ERR_FILTER_INVAL: - case LTTNG_ERR_FILTER_NOMEM: - ERR("%s", lttng_strerror(ret)); + } default: - ERR("Setting filter for event %s: '%s'", ev.name, - opt_filter); + ERR("Event %s%s: %s (channel %s, session %s, filter \'%s\')", ev->name, + exclusion_string, + lttng_strerror(command_ret), + command_ret == -LTTNG_ERR_NEED_CHANNEL_NAME + ? print_raw_channel_name(channel_name) + : print_channel_name(channel_name), + session_name, opt_filter); + error = 1; break; } - goto error; + error_holder = command_ret; + } else { - MSG("Filter '%s' successfully set", opt_filter); + MSG("Event %s%s: Filter '%s' successfully set", + event_name, exclusion_string, + opt_filter); + } + free(exclusion_string); + } + + if (lttng_opt_mi) { + if (command_ret) { + success = 0; + ev->enabled = 0; + } else { + ev->enabled = 1; + } + + ret = mi_lttng_event(writer, ev, 1, handle->domain.type); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* print exclusion */ + ret = mi_print_exclusion(exclusion_list); + if (ret) { + ret = CMD_ERROR; + goto error; + } + + /* Success ? */ + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Close event element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; } } /* Next event */ event_name = strtok(NULL, ","); + /* Reset warn, error and success */ + success = 1; } end: + /* Close Mi */ + if (lttng_opt_mi) { + /* Close events element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto error; + } + } error: if (warn) { ret = CMD_WARNING; } - if (opt_channel_name == NULL) { - free(channel_name); + if (error) { + ret = CMD_ERROR; } lttng_destroy_handle(handle); + strutils_free_null_terminated_array_of_strings(exclusion_list); + lttng_userspace_probe_location_destroy(uprobe_loc); + + /* Overwrite ret with error_holder if there was an actual error with + * enabling an event. + */ + ret = error_holder ? error_holder : ret; + lttng_event_destroy(ev); return ret; } @@ -633,9 +1346,10 @@ error: */ int cmd_enable_events(int argc, const char **argv) { - int opt, ret = CMD_SUCCESS; + int opt, ret = CMD_SUCCESS, command_ret = CMD_SUCCESS, success = 1; static poptContext pc; char *session_name = NULL; + const char *leftover = NULL; int event_type = -1; pc = poptGetContext(NULL, argc, argv, long_options, 0); @@ -647,7 +1361,7 @@ int cmd_enable_events(int argc, const char **argv) while ((opt = poptGetNextOpt(pc)) != -1) { switch (opt) { case OPT_HELP: - usage(stdout); + SHOW_HELP(); goto end; case OPT_TRACEPOINT: opt_event_type = LTTNG_EVENT_TRACEPOINT; @@ -655,12 +1369,12 @@ int cmd_enable_events(int argc, const char **argv) case OPT_PROBE: opt_event_type = LTTNG_EVENT_PROBE; break; + case OPT_USERSPACE_PROBE: + opt_event_type = LTTNG_EVENT_USERSPACE_PROBE; + break; case OPT_FUNCTION: opt_event_type = LTTNG_EVENT_FUNCTION; break; - case OPT_FUNCTION_ENTRY: - opt_event_type = LTTNG_EVENT_FUNCTION_ENTRY; - break; case OPT_SYSCALL: opt_event_type = LTTNG_EVENT_SYSCALL; break; @@ -680,8 +1394,9 @@ int cmd_enable_events(int argc, const char **argv) goto end; case OPT_FILTER: break; + case OPT_EXCLUDE: + break; default: - usage(stderr); ret = CMD_UNDEFINED; goto end; } @@ -698,10 +1413,50 @@ int cmd_enable_events(int argc, const char **argv) } } + ret = print_missing_or_multiple_domains( + opt_kernel + opt_userspace + opt_jul + opt_log4j + + opt_python, + true); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Mi check */ + if (lttng_opt_mi) { + writer = mi_lttng_writer_create(fileno(stdout), lttng_opt_mi); + if (!writer) { + ret = -LTTNG_ERR_NOMEM; + goto end; + } + + /* Open command element */ + ret = mi_lttng_writer_command_open(writer, + mi_lttng_element_command_enable_event); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Open output element */ + ret = mi_lttng_writer_open_element(writer, + mi_lttng_element_command_output); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } + opt_event_list = (char*) poptGetArg(pc); if (opt_event_list == NULL && opt_enable_all == 0) { ERR("Missing event name(s).\n"); - usage(stderr); + ret = CMD_ERROR; + goto end; + } + + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); ret = CMD_ERROR; goto end; } @@ -709,20 +1464,60 @@ int cmd_enable_events(int argc, const char **argv) if (!opt_session_name) { session_name = get_session_name(); if (session_name == NULL) { - ret = CMD_ERROR; - goto end; + command_ret = CMD_ERROR; + success = 0; + goto mi_closing; } } else { session_name = opt_session_name; } - ret = enable_events(session_name); + command_ret = enable_events(session_name); + if (command_ret) { + success = 0; + goto mi_closing; + } + +mi_closing: + /* Mi closing */ + if (lttng_opt_mi) { + /* Close output element */ + ret = mi_lttng_writer_close_element(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + ret = mi_lttng_writer_write_element_bool(writer, + mi_lttng_element_command_success, success); + if (ret) { + ret = CMD_ERROR; + goto end; + } + + /* Command element close */ + ret = mi_lttng_writer_command_close(writer); + if (ret) { + ret = CMD_ERROR; + goto end; + } + } end: + /* Mi clean-up */ + if (writer && mi_lttng_writer_destroy(writer)) { + /* Preserve original error code */ + ret = ret ? ret : LTTNG_ERR_MI_IO_FAIL; + } + if (opt_session_name == NULL) { free(session_name); } + /* Overwrite ret if an error occurred in enable_events */ + ret = command_ret ? command_ret : ret; + poptFreeContext(pc); return ret; } +