X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fbin%2Flttng%2Fcommands%2Fenable_events.c;h=3d32cbf813bc2615b211888748018fbebb77b5d8;hp=1ed3a0ab77c435d5dbe80b5fbfb95778e9c0ec2c;hb=54185c70692f8a6d9c667214743e0ffd07a0afa3;hpb=4fc83d948cea6b10484e65f004a6c167e71ac440 diff --git a/src/bin/lttng/commands/enable_events.c b/src/bin/lttng/commands/enable_events.c index 1ed3a0ab7..3d32cbf81 100644 --- a/src/bin/lttng/commands/enable_events.c +++ b/src/bin/lttng/commands/enable_events.c @@ -26,9 +26,13 @@ #include #include -#include +#include #include +#include +#include +#include +#include /* Mi dependancy */ #include @@ -50,6 +54,7 @@ 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_channel_name; static char *opt_filter; @@ -65,6 +70,7 @@ enum { OPT_HELP = 1, OPT_TRACEPOINT, OPT_PROBE, + OPT_USERSPACE_PROBE, OPT_FUNCTION, OPT_SYSCALL, OPT_USERSPACE, @@ -91,6 +97,7 @@ static struct poptOption long_options[] = { {"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}, {"syscall", 0, POPT_ARG_NONE, 0, OPT_SYSCALL, 0, 0}, {"loglevel", 0, POPT_ARG_STRING, 0, OPT_LOGLEVEL, 0, 0}, @@ -172,6 +179,393 @@ end: return ret; } +/* + * Walk the directories in the PATH environment variable to find the target + * binary passed as parameter. + * + * On success, the full path of the binary is copied in binary_full_path out + * parameter. This buffer is allocated by the caller and must be at least + * LTTNG_PATH_MAX bytes long. + * On failure, returns -1; + */ +static int walk_command_search_path(const char *binary, char *binary_full_path) +{ + char *tentative_binary_path = NULL; + char *command_search_path = NULL; + char *curr_search_dir_end = NULL; + char *curr_search_dir = NULL; + struct stat stat_output; + int ret = 0; + + command_search_path = lttng_secure_getenv("PATH"); + if (!command_search_path) { + ret = -1; + goto end; + } + + /* + * Duplicate the $PATH string as the char pointer returned by getenv() should + * not be modified. + */ + command_search_path = strdup(command_search_path); + if (!command_search_path) { + ret = -1; + goto end; + } + + /* + * This char array is used to concatenate path to binary to look for + * the binary. + */ + tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); + if (!tentative_binary_path) { + ret = -1; + goto alloc_error; + } + + curr_search_dir = command_search_path; + do { + /* + * Split on ':'. The return value of this call points to the + * matching character. + */ + curr_search_dir_end = strchr(curr_search_dir, ':'); + if (curr_search_dir_end != NULL) { + /* + * Add a NULL byte to the end of the first token so it + * can be used as a string. + */ + curr_search_dir_end[0] = '\0'; + } + + /* Empty the tentative path */ + memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char)); + + /* + * Build the tentative path to the binary using the current + * search directory and the name of the binary. + */ + ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s", + curr_search_dir, binary); + if (ret < 0) { + goto free_binary_path; + } + if (ret < LTTNG_PATH_MAX) { + /* + * Use STAT(2) to see if the file exists. + */ + ret = stat(tentative_binary_path, &stat_output); + if (ret == 0) { + /* + * Verify that it is a regular file or a + * symlink and not a special file (e.g. + * device). + */ + if (S_ISREG(stat_output.st_mode) + || S_ISLNK(stat_output.st_mode)) { + /* + * Found a match, set the out parameter + * and return success. + */ + ret = lttng_strncpy(binary_full_path, + tentative_binary_path, + LTTNG_PATH_MAX); + if (ret == -1) { + ERR("Source path does not fit " + "in destination buffer."); + } + goto free_binary_path; + } + } + } + /* Go to the next entry in the $PATH variable. */ + curr_search_dir = curr_search_dir_end + 1; + } while (curr_search_dir_end != NULL); + +free_binary_path: + free(tentative_binary_path); +alloc_error: + free(command_search_path); +end: + return ret; +} + +/* + * Check if the symbol field passed by the user is in fact an address or an + * offset from a symbol. Those two instrumentation types are not supported yet. + * It's expected to be a common mistake because of the existing --probe option + * that does support these formats. + * + * Here are examples of these unsupported formats for the --userspace-probe + * option: + * elf:/path/to/binary:0x400430 + * elf:/path/to/binary:4194364 + * elf:/path/to/binary:my_symbol+0x323 + * elf:/path/to/binary:my_symbol+43 + */ +static int warn_userspace_probe_syntax(const char *symbol) +{ + int ret; + + /* Check if the symbol field is an hex address. */ + ret = sscanf(symbol, "0x%*x"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on address not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is an decimal address. */ + ret = sscanf(symbol, "%*u"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on address not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is symbol+hex_offset. */ + ret = sscanf(symbol, "%*[^+]+0x%*x"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on symbol+offset not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + /* Check if the symbol field is symbol+decimal_offset. */ + ret = sscanf(symbol, "%*[^+]+%*u"); + if (ret > 0) { + /* If there is a match, print a warning and return an error. */ + ERR("Userspace probe on symbol+offset not supported yet."); + ret = CMD_UNSUPPORTED; + goto error; + } + + ret = 0; + +error: + return ret; +} + +/* + * Parse userspace probe options + * Set the userspace probe fields in the lttng_event struct and set the + * target_path to the path to the binary. + */ +static int parse_userspace_probe_opts(struct lttng_event *ev, char *opt) +{ + int ret = CMD_SUCCESS; + int num_token; + char **tokens; + char *target_path = NULL; + char *unescaped_target_path = NULL; + char *real_target_path = NULL; + char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL; + struct lttng_userspace_probe_location *probe_location = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup_method = + NULL; + + if (opt == NULL) { + ret = CMD_ERROR; + goto end; + } + + switch (ev->type) { + case LTTNG_EVENT_USERSPACE_PROBE: + break; + default: + assert(0); + } + + /* + * userspace probe fields are separated by ':'. + */ + tokens = strutils_split(opt, ':', 1); + num_token = strutils_array_of_strings_len(tokens); + + /* + * Early sanity check that the number of parameter is between 2 and 4 + * inclusively. + * elf:PATH:SYMBOL + * std:PATH:PROVIDER_NAME:PROBE_NAME + * PATH:SYMBOL (same behavior as ELF) + */ + if (num_token < 2 || num_token > 4) { + ret = CMD_ERROR; + goto end_string; + } + + /* + * Looking up the first parameter will tell the technique to use to + * interpret the userspace probe/function description. + */ + switch (num_token) { + case 2: + /* When the probe type is omitted we assume ELF for now. */ + case 3: + if (num_token == 3 && strcmp(tokens[0], "elf") == 0) { + target_path = tokens[1]; + symbol_name = tokens[2]; + } else if (num_token == 2) { + target_path = tokens[0]; + symbol_name = tokens[1]; + } else { + ret = CMD_ERROR; + goto end_string; + } + lookup_method = + lttng_userspace_probe_location_lookup_method_function_elf_create(); + if (!lookup_method) { + WARN("Failed to create ELF lookup method"); + ret = CMD_ERROR; + goto end_string; + } + break; + case 4: + if (strcmp(tokens[0], "sdt") == 0) { + target_path = tokens[1]; + provider_name = tokens[2]; + probe_name = tokens[3]; + } else { + ret = CMD_ERROR; + goto end_string; + } + lookup_method = + lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(); + if (!lookup_method) { + WARN("Failed to create ELF lookup method"); + ret = CMD_ERROR; + goto end_string; + } + break; + default: + ret = CMD_ERROR; + goto end_string; + } + + /* strutils_unescape_string allocates a new char *. */ + unescaped_target_path = strutils_unescape_string(target_path, 0); + if (!unescaped_target_path) { + ret = CMD_ERROR; + goto end_destroy_lookup_method; + } + + /* + * If there is not forward slash in the path. Walk the $PATH else + * expand. + */ + if (strchr(unescaped_target_path, '/') == NULL) { + /* Walk the $PATH variable to find the targeted binary. */ + real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char)); + if (!real_target_path) { + PERROR("Error allocating path buffer"); + ret = CMD_ERROR; + goto end_destroy_lookup_method; + } + ret = walk_command_search_path(unescaped_target_path, real_target_path); + if (ret) { + ERR("Binary not found."); + ret = CMD_ERROR; + goto end_destroy_lookup_method; + } + } else { + /* + * Expand references to `/./` and `/../`. This function does not check + * if the file exists. This call returns an allocated buffer on + * success. + */ + real_target_path = utils_expand_path_keep_symlink(unescaped_target_path); + if (!real_target_path) { + ERR("Error expanding the path to binary."); + ret = CMD_ERROR; + goto end_destroy_lookup_method; + } + + /* + * Check if the file exists using access(2), If it does not, + * return an error. + */ + ret = access(real_target_path, F_OK); + if (ret) { + ERR("Cannot find binary at path: %s.", real_target_path); + ret = CMD_ERROR; + goto end_destroy_lookup_method; + } + } + + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + /* + * Check for common mistakes in userspace probe description syntax. + */ + ret = warn_userspace_probe_syntax(symbol_name); + if (ret) { + goto end_destroy_lookup_method; + } + + probe_location = lttng_userspace_probe_location_function_create( + real_target_path, symbol_name, lookup_method); + if (!probe_location) { + WARN("Failed to create function probe location"); + ret = CMD_ERROR; + goto end_destroy_lookup_method; + } + + /* Ownership transferred to probe_location. */ + lookup_method = NULL; + + ret = lttng_event_set_userspace_probe_location(ev, probe_location); + if (ret) { + WARN("Failed to set probe location on event"); + ret = CMD_ERROR; + goto end_destroy_location; + } + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + probe_location = lttng_userspace_probe_location_tracepoint_create( + real_target_path, provider_name, probe_name, lookup_method); + if (!probe_location) { + WARN("Failed to create function probe location"); + ret = CMD_ERROR; + goto end_destroy_lookup_method; + } + + /* Ownership transferred to probe_location. */ + lookup_method = NULL; + + ret = lttng_event_set_userspace_probe_location(ev, probe_location); + if (ret) { + WARN("Failed to set probe location on event"); + ret = CMD_ERROR; + goto end_destroy_location; + } + break; + default: + ret = CMD_ERROR; + goto end_destroy_lookup_method; + } + + /* Successful parsing, now clean up everything and return. */ + goto end_string; + +end_destroy_location: + lttng_userspace_probe_location_destroy(probe_location); +end_destroy_lookup_method: + lttng_userspace_probe_location_lookup_method_destroy(lookup_method); +end_string: + strutils_free_null_terminated_array_of_strings(tokens); + /* + * Freeing both char * here makes the error handling simplier. free() + * performs not action if the pointer is NULL. + */ + free(real_target_path); + free(unescaped_target_path); +end: + return ret; +} + /* * Maps LOG4j loglevel from string to value */ @@ -372,9 +766,10 @@ const char *print_raw_channel_name(const char *name) * Mi print exlcusion list */ static -int mi_print_exclusion(int count, char **names) +int mi_print_exclusion(char **names) { int i, ret; + int count = names ? strutils_array_of_strings_len(names) : 0; assert(writer); @@ -406,12 +801,13 @@ end: * Return allocated string for pretty-printing exclusion names. */ static -char *print_exclusions(int count, char **names) +char *print_exclusions(char **names) { int length = 0; int i; - const char *preamble = " excluding "; + const char preamble[] = " excluding "; char *ret; + int count = names ? strutils_array_of_strings_len(names) : 0; if (count == 0) { return strdup(""); @@ -419,141 +815,148 @@ char *print_exclusions(int count, char **names) /* calculate total required length */ for (i = 0; i < count; i++) { - length += strlen(names[i]) + 1; + length += strlen(names[i]) + 4; } - /* add length of preamble + one for NUL - one for last (missing) comma */ - length += strlen(preamble); + 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, ","); + strcat(ret, ", "); } } return ret; } -/* - * Compare list of exclusions against an event name. - * Return a list of legal exclusion names. - * Produce an error or a warning about others (depending on the situation) - */ static -int check_exclusion_subsets(const char *event_name, - const char *exclusions, - int *exclusion_count_ptr, - char ***exclusion_list_ptr) +int check_exclusion_subsets(const char *event_name, const char *exclusion) { - const char *excluder_ptr; - const char *event_ptr; - const char *next_excluder; - int excluder_length; - int exclusion_count = 0; - char **exclusion_list = NULL; - int ret = CMD_SUCCESS; + 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; + } - if (event_name[strlen(event_name) - 1] != '*') { - ERR("Event %s: Excluders can only be used with wildcarded events", event_name); - goto error; + 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++; } - next_excluder = exclusions; - while (*next_excluder != 0) { - event_ptr = event_name; - excluder_ptr = next_excluder; - excluder_length = strcspn(next_excluder, ","); + goto end; - /* Scan both the excluder and the event letter by letter */ - while (1) { - char e, x; +error: + ret = -1; - e = *event_ptr; - x = *excluder_ptr; +end: + if (warn) { + WARN("Event %s: %s does not exclude any events from %s", + event_name, exclusion, event_name); + } - if (x == '*') { - /* Event is a subset of the excluder */ - ERR("Event %s: %.*s excludes all events from %s", - event_name, - excluder_length, - next_excluder, - event_name); - goto error; - } - if (e == '*') { - char *string; - char **new_exclusion_list; - - /* Excluder is a proper subset of event */ - string = lttng_strndup(next_excluder, excluder_length); - if (!string) { - PERROR("lttng_strndup error"); - goto error; - } - new_exclusion_list = realloc(exclusion_list, - sizeof(char *) * (exclusion_count + 1)); - if (!new_exclusion_list) { - PERROR("realloc"); - free(string); + return ret; +} + +static +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 = new_exclusion_list; - exclusion_count++; - exclusion_list[exclusion_count - 1] = string; - break; } - if (x != e) { - /* Excluder and event sets have no common elements */ - WARN("Event %s: %.*s does not exclude any events from %s", - event_name, - excluder_length, - next_excluder, - event_name); - break; - } - excluder_ptr++; - event_ptr++; - } - /* next excluder */ - next_excluder += excluder_length; - if (*next_excluder == ',') { - next_excluder++; } } + + *exclusion_list = exclusions; + goto end; + error: - while (exclusion_count--) { - free(exclusion_list[exclusion_count]); - } - if (exclusion_list != NULL) { - free(exclusion_list); - } - exclusion_list = NULL; - exclusion_count = 0; - ret = CMD_ERROR; + ret = -1; + strutils_free_null_terminated_array_of_strings(exclusions); + end: - *exclusion_count_ptr = exclusion_count; - *exclusion_list_ptr = exclusion_list; return ret; } -static void warn_on_truncated_exclusion_names(char **exclusion_list, - int exclusion_count, int *warn) +static void warn_on_truncated_exclusion_names(char * const *exclusion_list, + int *warn) { - size_t i = 0; + char * const *exclusion; - for (i = 0; i < exclusion_count; ++i) { - const char *name = exclusion_list[i]; - size_t len = strlen(name); - - if (len >= LTTNG_SYMBOL_NAME_LEN) { + for (exclusion = exclusion_list; *exclusion; exclusion++) { + if (strlen(*exclusion) >= LTTNG_SYMBOL_NAME_LEN) { WARN("Event exclusion \"%s\" will be truncated", - name); + *exclusion); *warn = 1; } } @@ -568,14 +971,18 @@ static int enable_events(char *session_name) 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; - int exclusion_count = 0; char **exclusion_list = 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_loglevel) { WARN("Kernel loglevels are not supported."); @@ -625,6 +1032,30 @@ static int enable_events(char *session_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); @@ -646,26 +1077,26 @@ static int enable_events(char *session_name) if (opt_enable_all) { /* Default setup for enable all */ if (opt_kernel) { - ev.type = opt_event_type; - strcpy(ev.name, "*"); + 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) { assert(opt_userspace || opt_jul || opt_log4j || opt_python); if (opt_userspace) { - ev.loglevel = loglevel_str_to_value(opt_loglevel); + ev->loglevel = loglevel_str_to_value(opt_loglevel); } else if (opt_jul) { - ev.loglevel = loglevel_jul_str_to_value(opt_loglevel); + ev->loglevel = loglevel_jul_str_to_value(opt_loglevel); } else if (opt_log4j) { - ev.loglevel = loglevel_log4j_str_to_value(opt_loglevel); + ev->loglevel = loglevel_log4j_str_to_value(opt_loglevel); } else if (opt_python) { - ev.loglevel = loglevel_python_str_to_value(opt_loglevel); + ev->loglevel = loglevel_python_str_to_value(opt_loglevel); } - if (ev.loglevel == -1) { + if (ev->loglevel == -1) { ERR("Unknown loglevel %s", opt_loglevel); ret = -LTTNG_ERR_INVALID; goto error; @@ -673,33 +1104,35 @@ static int enable_events(char *session_name) } else { assert(opt_userspace || opt_jul || opt_log4j || opt_python); if (opt_userspace) { - ev.loglevel = -1; + ev->loglevel = -1; } else if (opt_jul) { - ev.loglevel = LTTNG_LOGLEVEL_JUL_ALL; + ev->loglevel = LTTNG_LOGLEVEL_JUL_ALL; } else if (opt_log4j) { - ev.loglevel = LTTNG_LOGLEVEL_LOG4J_ALL; + ev->loglevel = LTTNG_LOGLEVEL_LOG4J_ALL; } else if (opt_python) { - ev.loglevel = LTTNG_LOGLEVEL_PYTHON_DEBUG; + ev->loglevel = LTTNG_LOGLEVEL_PYTHON_DEBUG; } } } if (opt_exclude) { - ret = check_exclusion_subsets("*", opt_exclude, - &exclusion_count, &exclusion_list); - if (ret == CMD_ERROR) { + ret = create_exclusion_list_and_validate("*", + opt_exclude, &exclusion_list); + if (ret) { + ret = CMD_ERROR; goto error; } - ev.exclusion = 1; + ev->exclusion = 1; warn_on_truncated_exclusion_names(exclusion_list, - exclusion_count, &warn); + &warn); } if (!opt_filter) { ret = lttng_enable_event_with_exclusions(handle, - &ev, channel_name, + ev, channel_name, NULL, - exclusion_count, exclusion_list); + exclusion_list ? strutils_array_of_strings_len(exclusion_list) : 0, + exclusion_list); if (ret < 0) { switch (-ret) { case LTTNG_ERR_KERN_EVENT_EXIST: @@ -733,7 +1166,7 @@ static int enable_events(char *session_name) switch (opt_event_type) { case LTTNG_EVENT_TRACEPOINT: if (opt_loglevel && dom.type != LTTNG_DOMAIN_KERNEL) { - char *exclusion_string = print_exclusions(exclusion_count, exclusion_list); + char *exclusion_string = print_exclusions(exclusion_list); if (!exclusion_string) { PERROR("Cannot allocate exclusion_string"); @@ -747,7 +1180,7 @@ static int enable_events(char *session_name) opt_loglevel); free(exclusion_string); } else { - char *exclusion_string = print_exclusions(exclusion_count, exclusion_list); + char *exclusion_string = print_exclusions(exclusion_list); if (!exclusion_string) { PERROR("Cannot allocate exclusion_string"); @@ -770,7 +1203,7 @@ static int enable_events(char *session_name) break; case LTTNG_EVENT_ALL: if (opt_loglevel && dom.type != LTTNG_DOMAIN_KERNEL) { - char *exclusion_string = print_exclusions(exclusion_count, exclusion_list); + char *exclusion_string = print_exclusions(exclusion_list); if (!exclusion_string) { PERROR("Cannot allocate exclusion_string"); @@ -784,7 +1217,7 @@ static int enable_events(char *session_name) opt_loglevel); free(exclusion_string); } else { - char *exclusion_string = print_exclusions(exclusion_count, exclusion_list); + char *exclusion_string = print_exclusions(exclusion_list); if (!exclusion_string) { PERROR("Cannot allocate exclusion_string"); @@ -808,8 +1241,10 @@ static int enable_events(char *session_name) } if (opt_filter) { - command_ret = lttng_enable_event_with_exclusions(handle, &ev, channel_name, - opt_filter, exclusion_count, exclusion_list); + 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: @@ -840,7 +1275,7 @@ static int enable_events(char *session_name) } error_holder = command_ret; } else { - ev.filter = 1; + ev->filter = 1; MSG("Filter '%s' successfully set", opt_filter); } } @@ -853,23 +1288,23 @@ static int enable_events(char *session_name) * Note: this is strictly for semantic and printing while in * machine interface mode. */ - strcpy(ev.name, "*"); + strcpy(ev->name, "*"); /* If we reach here the events are enabled */ if (!error && !warn) { - ev.enabled = 1; + ev->enabled = 1; } else { - ev.enabled = 0; + ev->enabled = 0; success = 0; } - ret = mi_lttng_event(writer, &ev, 1, handle->domain.type); + ret = mi_lttng_event(writer, ev, 1, handle->domain.type); if (ret) { ret = CMD_ERROR; goto error; } /* print exclusion */ - ret = mi_print_exclusion(exclusion_count, exclusion_list); + ret = mi_print_exclusion(exclusion_list); if (ret) { ret = CMD_ERROR; goto error; @@ -898,9 +1333,9 @@ 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) { @@ -911,30 +1346,49 @@ static int enable_events(char *session_name) switch (opt_event_type) { case LTTNG_EVENT_ALL: /* Enable tracepoints and syscalls */ /* If event name differs from *, select tracepoint. */ - if (strcmp(ev.name, "*")) { - ev.type = LTTNG_EVENT_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); + 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: + ret = parse_userspace_probe_opts(ev, opt_userspace_probe); + 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; } break; case LTTNG_EVENT_FUNCTION: - ret = parse_probe_opts(&ev, opt_function); + 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_SYSCALL: - ev.type = LTTNG_EVENT_SYSCALL; + ev->type = LTTNG_EVENT_SYSCALL; break; default: ret = CMD_UNDEFINED; @@ -942,7 +1396,7 @@ static int enable_events(char *session_name) } /* 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 */ DBG("Enabling UST event %s for channel %s, loglevel %s", event_name, print_channel_name(channel_name), opt_loglevel ? : ""); @@ -952,13 +1406,14 @@ static int enable_events(char *session_name) /* 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_SYSCALL: + case LTTNG_EVENT_USERSPACE_PROBE: default: ERR("Event type not available for user-space tracing"); ret = CMD_UNSUPPORTED; @@ -966,41 +1421,38 @@ static int enable_events(char *session_name) } if (opt_exclude) { - ev.exclusion = 1; + 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 */ - if (exclusion_list != NULL) { - while (exclusion_count--) { - free(exclusion_list[exclusion_count]); - } - free(exclusion_list); - exclusion_list = NULL; - } - /* Check for proper subsets */ - ret = check_exclusion_subsets(event_name, opt_exclude, - &exclusion_count, &exclusion_list); - if (ret == CMD_ERROR) { + 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, exclusion_count, &warn); + exclusion_list, &warn); } - ev.loglevel_type = opt_loglevel_type; + ev->loglevel_type = opt_loglevel_type; if (opt_loglevel) { - ev.loglevel = loglevel_str_to_value(opt_loglevel); - if (ev.loglevel == -1) { + 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; + ev->loglevel = -1; } } else if (opt_jul || opt_log4j || opt_python) { if (opt_event_type != LTTNG_EVENT_ALL && @@ -1010,32 +1462,32 @@ static int enable_events(char *session_name) goto error; } - ev.loglevel_type = opt_loglevel_type; + ev->loglevel_type = opt_loglevel_type; if (opt_loglevel) { if (opt_jul) { - ev.loglevel = loglevel_jul_str_to_value(opt_loglevel); + ev->loglevel = loglevel_jul_str_to_value(opt_loglevel); } else if (opt_log4j) { - ev.loglevel = loglevel_log4j_str_to_value(opt_loglevel); + ev->loglevel = loglevel_log4j_str_to_value(opt_loglevel); } else if (opt_python) { - ev.loglevel = loglevel_python_str_to_value(opt_loglevel); + ev->loglevel = loglevel_python_str_to_value(opt_loglevel); } - if (ev.loglevel == -1) { + if (ev->loglevel == -1) { ERR("Unknown loglevel %s", opt_loglevel); ret = -LTTNG_ERR_INVALID; goto error; } } else { if (opt_jul) { - ev.loglevel = LTTNG_LOGLEVEL_JUL_ALL; + ev->loglevel = LTTNG_LOGLEVEL_JUL_ALL; } else if (opt_log4j) { - ev.loglevel = LTTNG_LOGLEVEL_LOG4J_ALL; + ev->loglevel = LTTNG_LOGLEVEL_LOG4J_ALL; } else if (opt_python) { - ev.loglevel = LTTNG_LOGLEVEL_PYTHON_DEBUG; + 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'; + ev->type = LTTNG_EVENT_TRACEPOINT; + strncpy(ev->name, event_name, LTTNG_SYMBOL_NAME_LEN); + ev->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; } else { assert(0); } @@ -1044,9 +1496,11 @@ static int enable_events(char *session_name) char *exclusion_string; command_ret = lttng_enable_event_with_exclusions(handle, - &ev, channel_name, - NULL, exclusion_count, exclusion_list); - exclusion_string = print_exclusions(exclusion_count, exclusion_list); + 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; @@ -1073,6 +1527,12 @@ static int enable_events(char *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: %s (channel %s, session %s)", event_name, exclusion_string, @@ -1118,11 +1578,13 @@ static int enable_events(char *session_name) char *exclusion_string; /* Filter present */ - ev.filter = 1; + ev->filter = 1; - command_ret = lttng_enable_event_with_exclusions(handle, &ev, channel_name, - opt_filter, exclusion_count, exclusion_list); - exclusion_string = print_exclusions(exclusion_count, exclusion_list); + 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; @@ -1141,7 +1603,7 @@ static int enable_events(char *session_name) 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, + ERR("Event %s%s: %s (channel %s, session %s, filter \'%s\')", ev->name, exclusion_string, msg, print_channel_name(channel_name), @@ -1150,7 +1612,7 @@ static int enable_events(char *session_name) break; } default: - ERR("Event %s%s: %s (channel %s, session %s, filter \'%s\')", ev.name, + 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 @@ -1173,19 +1635,19 @@ static int enable_events(char *session_name) if (lttng_opt_mi) { if (command_ret) { success = 0; - ev.enabled = 0; + ev->enabled = 0; } else { - ev.enabled = 1; + ev->enabled = 1; } - ret = mi_lttng_event(writer, &ev, 1, handle->domain.type); + ret = mi_lttng_event(writer, ev, 1, handle->domain.type); if (ret) { ret = CMD_ERROR; goto error; } /* print exclusion */ - ret = mi_print_exclusion(exclusion_count, exclusion_list); + ret = mi_print_exclusion(exclusion_list); if (ret) { ret = CMD_ERROR; goto error; @@ -1231,19 +1693,14 @@ error: ret = CMD_ERROR; } lttng_destroy_handle(handle); - - if (exclusion_list != NULL) { - while (exclusion_count--) { - free(exclusion_list[exclusion_count]); - } - free(exclusion_list); - } + strutils_free_null_terminated_array_of_strings(exclusion_list); /* 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; } @@ -1255,6 +1712,7 @@ int cmd_enable_events(int argc, const char **argv) 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); @@ -1274,6 +1732,9 @@ 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; @@ -1354,6 +1815,13 @@ int cmd_enable_events(int argc, const char **argv) goto end; } + leftover = poptGetArg(pc); + if (leftover) { + ERR("Unknown argument: %s", leftover); + ret = CMD_ERROR; + goto end; + } + if (!opt_session_name) { session_name = get_session_name(); if (session_name == NULL) { @@ -1413,3 +1881,4 @@ end: poptFreeContext(pc); return ret; } +