From: Francis Deslauriers Date: Fri, 29 Jun 2018 19:22:36 +0000 (-0400) Subject: Add --userspace-probe kernel event type X-Git-Url: http://git.efficios.com/?p=lttng-tools.git;a=commitdiff_plain;h=dcabc1905756b2999886313ec33705f7571a3fb9 Add --userspace-probe kernel event type This commit wires up all the elements necessary to use the userspace-probe feature. This event type is defined by the user with --userspace-probe option. At the moment, probing location can be specified by the user using two location types: ELF function and STD probe. It's important to note that userspace-probes are traced by the kernel tracer and the generated events will thus be recorded in the kernel trace. This is due to the fact that this feature uses the uprobe kernel interface to instrument binaries. A root session daemon is needed to load the kernel modules necessary to use this feature. To ensure that the file pointed by the provided path does not change while processing the user command, we use a fd-passing scheme. It consist of calling open of the executable path early on in the enable-event command and passing that file descriptor to the sessiond for ELF parsing and the kernel to do the instrumentation. This ensures that the inode backing that file is not reused for another file in the case of a file deletion-and-reuse race. Command syntax: * The syntax to enable an ELF function userspace probe is the following: `--userspace-probe=elf:/path/to/executable:my_target_symbol` Omitting the first element of the colon-separated list would also work as the ELF function location is the default location type. Here are two equivalent usage examples of this location type: lttng enable-enable --kernel --userspace-probe=/path/to/executable:my_symbol event_name lttng enable-enable --kernel --userspace-probe=elf:/path/to/executable:my_symbol event_name * SDT probes are DTrace-style tracepoint distributed in multiple libraries and applications. This implementation supports tracing of SDT probes that are NOT guarded by semaphore. The syntax to enable an SDT tracepoint userspace probe is the following: `--userspace-probe=sdt:/path/to/executable:provider:probe` Here is an usage example: lttng enable-enable --kernel --userspace-probe=sdt:/path/to/executable:provider:probe event_name Signed-off-by: Francis Deslauriers Signed-off-by: Jérémie Galarneau --- diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 7318d5d3d..f9e2bd686 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -2072,6 +2072,7 @@ static int _cmd_enable_event(struct ltt_session *session, break; } case LTTNG_EVENT_PROBE: + case LTTNG_EVENT_USERSPACE_PROBE: case LTTNG_EVENT_FUNCTION: case LTTNG_EVENT_FUNCTION_ENTRY: case LTTNG_EVENT_TRACEPOINT: diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index e13925e27..f5979c9b4 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -492,6 +492,13 @@ int kernel_create_event(struct lttng_event *ev, } } + if (ev->type == LTTNG_EVENT_USERSPACE_PROBE) { + ret = userspace_probe_add_callsites(ev, channel->session, event->fd); + if (ret) { + goto add_callsite_error; + } + } + err = kernctl_enable(event->fd); if (err < 0) { switch (-err) { @@ -514,6 +521,7 @@ int kernel_create_event(struct lttng_event *ev, return 0; +add_callsite_error: enable_error: filter_error: { diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 6d3ae1b58..958aa0d04 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include "lttng-sessiond.h" #include "buffer-registry.h" @@ -2930,6 +2932,112 @@ static unsigned int lttng_sessions_count(uid_t uid, gid_t gid) return i; } +static int receive_userspace_probe(struct command_ctx *cmd_ctx, int sock, + int *sock_error, struct lttng_event *event) +{ + int fd, ret; + struct lttng_userspace_probe_location *probe_location; + struct lttng_userspace_probe_location_lookup_method *lookup = NULL; + struct lttng_dynamic_buffer probe_location_buffer; + struct lttng_buffer_view buffer_view; + + /* + * Create a buffer to store the serialized version of the probe + * location. + */ + lttng_dynamic_buffer_init(&probe_location_buffer); + ret = lttng_dynamic_buffer_set_size(&probe_location_buffer, + cmd_ctx->lsm->u.enable.userspace_probe_location_len); + if (ret) { + ret = LTTNG_ERR_NOMEM; + goto error; + } + + /* + * Receive the probe location. + */ + ret = lttcomm_recv_unix_sock(sock, probe_location_buffer.data, + probe_location_buffer.size); + if (ret <= 0) { + DBG("Nothing recv() from client var len data... continuing"); + *sock_error = 1; + lttng_dynamic_buffer_reset(&probe_location_buffer); + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + buffer_view = lttng_buffer_view_from_dynamic_buffer( + &probe_location_buffer, 0, probe_location_buffer.size); + + /* + * Extract the probe location from the serialized version. + */ + ret = lttng_userspace_probe_location_create_from_buffer( + &buffer_view, &probe_location); + if (ret < 0) { + WARN("Failed to create a userspace probe location from the received buffer"); + lttng_dynamic_buffer_reset( &probe_location_buffer); + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * Receive the file descriptor to the target binary from the client. + */ + DBG("Receiving userspace probe target FD from client ..."); + ret = lttcomm_recv_fds_unix_sock(sock, &fd, 1); + if (ret <= 0) { + DBG("Nothing recv() from client userspace probe fd... continuing"); + *sock_error = 1; + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * Set the file descriptor received from the client through the unix + * socket in the probe location. + */ + lookup = lttng_userspace_probe_location_get_lookup_method(probe_location); + if (!lookup) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * From the kernel tracer's perspective, all userspace probe event types + * are all the same: a file and an offset. + */ + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + ret = lttng_userspace_probe_location_function_set_binary_fd( + probe_location, fd); + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + ret = lttng_userspace_probe_location_tracepoint_set_binary_fd( + probe_location, fd); + break; + default: + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + if (ret) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* Attach the probe location to the event. */ + ret = lttng_event_set_userspace_probe_location(event, probe_location); + if (ret) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + lttng_dynamic_buffer_reset(&probe_location_buffer); +error: + return ret; +} + /* * Check if the current kernel tracer supports the session rotation feature. * Return 1 if it does, 0 otherwise. @@ -3468,6 +3576,7 @@ error_add_context: } case LTTNG_ENABLE_EVENT: { + struct lttng_event *ev = NULL; struct lttng_event_exclusion *exclusion = NULL; struct lttng_filter_bytecode *bytecode = NULL; char *filter_expression = NULL; @@ -3519,7 +3628,7 @@ error_add_context: ret = lttcomm_recv_unix_sock(sock, filter_expression, expression_len); if (ret <= 0) { - DBG("Nothing recv() from client car len data... continuing"); + DBG("Nothing recv() from client var len data... continuing"); *sock_error = 1; free(filter_expression); free(exclusion); @@ -3551,7 +3660,7 @@ error_add_context: DBG("Receiving var len filter's bytecode from client ..."); ret = lttcomm_recv_unix_sock(sock, bytecode, bytecode_len); if (ret <= 0) { - DBG("Nothing recv() from client car len data... continuing"); + DBG("Nothing recv() from client var len data... continuing"); *sock_error = 1; free(filter_expression); free(bytecode); @@ -3569,11 +3678,30 @@ error_add_context: } } + ev = lttng_event_copy(&cmd_ctx->lsm->u.enable.event); + if (!ev) { + DBG("Failed to copy event: %s", + cmd_ctx->lsm->u.enable.event.name); + ret = LTTNG_ERR_NOMEM; + goto error; + } + + + if (cmd_ctx->lsm->u.enable.userspace_probe_location_len > 0) { + /* Expect a userspace probe description. */ + ret = receive_userspace_probe(cmd_ctx, sock, sock_error, ev); + if (ret) { + lttng_event_destroy(ev); + goto error; + } + } + ret = cmd_enable_event(cmd_ctx->session, &cmd_ctx->lsm->domain, cmd_ctx->lsm->u.enable.channel_name, - &cmd_ctx->lsm->u.enable.event, + ev, filter_expression, bytecode, exclusion, kernel_poll_pipe[1]); + lttng_event_destroy(ev); break; } case LTTNG_LIST_TRACEPOINTS: diff --git a/src/bin/lttng-sessiond/trace-kernel.c b/src/bin/lttng-sessiond/trace-kernel.c index f7bb98197..dcb1bd647 100644 --- a/src/bin/lttng-sessiond/trace-kernel.c +++ b/src/bin/lttng-sessiond/trace-kernel.c @@ -21,6 +21,11 @@ #include #include +#include +#include +#include +#include + #include #include @@ -300,6 +305,7 @@ enum lttng_error_code trace_kernel_create_event( enum lttng_error_code ret; struct lttng_kernel_event *attr; struct ltt_kernel_event *local_kernel_event; + struct lttng_userspace_probe_location *userspace_probe_location = NULL; assert(ev); @@ -320,6 +326,84 @@ enum lttng_error_code trace_kernel_create_event( ev->attr.probe.symbol_name, LTTNG_KERNEL_SYM_NAME_LEN); attr->u.kprobe.symbol_name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; break; + case LTTNG_EVENT_USERSPACE_PROBE: + { + struct lttng_userspace_probe_location* location = NULL; + struct lttng_userspace_probe_location_lookup_method *lookup = NULL; + + location = lttng_event_get_userspace_probe_location(ev); + if (!location) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * From this point on, the specific term 'uprobe' is used + * instead of the generic 'userspace probe' because it's the + * technology used at the moment for this instrumentation. + * LTTng currently implements userspace probes using uprobes. + * In the interactions with the kernel tracer, we use the + * uprobe term. + */ + attr->instrumentation = LTTNG_KERNEL_UPROBE; + + /* + * Only the elf lookup method is supported at the moment. + */ + lookup = lttng_userspace_probe_location_get_lookup_method( + location); + if (!lookup) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + + /* + * From the kernel tracer's perspective, all userspace probe + * event types are all the same: a file and an offset. + */ + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + /* Get the file descriptor on the target binary. */ + attr->u.uprobe.fd = + lttng_userspace_probe_location_function_get_binary_fd(location); + + /* + * Save a reference to the probe location used during + * the listing of events. Close its FD since it won't + * be needed for listing. + */ + userspace_probe_location = + lttng_userspace_probe_location_copy(location); + ret = lttng_userspace_probe_location_function_set_binary_fd( + userspace_probe_location, -1); + if (ret) { + goto error; + } + break; + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: + /* Get the file descriptor on the target binary. */ + attr->u.uprobe.fd = + lttng_userspace_probe_location_tracepoint_get_binary_fd(location); + + /* + * Save a reference to the probe location used during the listing of + * events. Close its FD since it won't be needed for listing. + */ + userspace_probe_location = + lttng_userspace_probe_location_copy(location); + ret = lttng_userspace_probe_location_tracepoint_set_binary_fd( + userspace_probe_location, -1); + if (ret) { + goto error; + } + break; + default: + DBG("Unsupported lookup method type"); + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto error; + } + break; + } case LTTNG_EVENT_FUNCTION: attr->instrumentation = LTTNG_KERNEL_KRETPROBE; attr->u.kretprobe.addr = ev->attr.probe.addr; @@ -359,6 +443,7 @@ enum lttng_error_code trace_kernel_create_event( local_kernel_event->enabled = 1; local_kernel_event->filter_expression = filter_expression; local_kernel_event->filter = filter; + local_kernel_event->userspace_probe_location = userspace_probe_location; *kernel_event = local_kernel_event; diff --git a/src/bin/lttng/commands/enable_events.c b/src/bin/lttng/commands/enable_events.c index f4403c25e..62f99a1fc 100644 --- a/src/bin/lttng/commands/enable_events.c +++ b/src/bin/lttng/commands/enable_events.c @@ -54,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; @@ -69,6 +70,7 @@ enum { OPT_HELP = 1, OPT_TRACEPOINT, OPT_PROBE, + OPT_USERSPACE_PROBE, OPT_FUNCTION, OPT_SYSCALL, OPT_USERSPACE, @@ -95,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}, @@ -287,6 +290,214 @@ end: 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; + } + + /* + * 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 = -LTTNG_ERR_INVALID; + goto end_string; + } + + /* + * If there is not forward slash in the path. Walk the $PATH else + * expand. + */ + if (strchr(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_unescaped_string; + } + ret = walk_command_search_path(target_path, real_target_path); + if (ret) { + ERR("Binary not found."); + ret = CMD_ERROR; + goto end_free_path; + } + } else { + /* + * Expand references to `/./` and `/../`. This function does not check + * if the file exists. + */ + real_target_path = utils_expand_path_keep_symlink(target_path); + if (!real_target_path) { + ERR("Error expanding the path to binary."); + ret = CMD_ERROR; + goto end_free_path; + } + + /* + * Check if the file exists using access(2). If it does not, walk the + * $PATH. + */ + ret = access(real_target_path, F_OK); + if (ret) { + ret = CMD_ERROR; + goto end_free_path; + } + } + + switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) { + case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: + 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_string; + } + + switch (ev->type) { + case LTTNG_EVENT_USERSPACE_PROBE: + break; + default: + assert(0); + } + + goto end; + +end_destroy_location: + lttng_userspace_probe_location_destroy(probe_location); +end_destroy_lookup_method: + lttng_userspace_probe_location_lookup_method_destroy(lookup_method); +end_free_path: + /* + * Free path that was allocated by the call to realpath() or when walking + * the PATH. + */ + free(real_target_path); +end_unescaped_string: + free(unescaped_target_path); +end_string: + strutils_free_null_terminated_array_of_strings(tokens); +end: + return ret; +} + /* * Maps LOG4j loglevel from string to value */ @@ -1058,6 +1269,14 @@ static int enable_events(char *session_name) goto error; } break; + case LTTNG_EVENT_USERSPACE_PROBE: + ret = parse_userspace_probe_opts(ev, opt_userspace_probe); + if (ret) { + ERR("Unable to parse userspace probe options"); + ret = CMD_ERROR; + goto error; + } + break; case LTTNG_EVENT_FUNCTION: ret = parse_probe_opts(ev, opt_function); if (ret) { @@ -1092,6 +1311,7 @@ static int enable_events(char *session_name) 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; @@ -1205,6 +1425,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, @@ -1404,6 +1630,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; diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index 134e8ddc7..ee4a37b7f 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -90,7 +90,8 @@ KERN_DATA_TRACE=$(top_builddir)/src/bin/lttng-sessiond/trace-kernel.$(OBJEXT) \ $(top_builddir)/src/common/libcommon.la \ $(top_builddir)/src/common/health/libhealth.la \ $(top_builddir)/src/bin/lttng-sessiond/notification-thread-commands.$(OBJEXT) \ - $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la + $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \ + $(LIBLTTNG_CTL) test_kernel_data_SOURCES = test_kernel_data.c test_kernel_data_LDADD = $(LIBTAP) $(LIBCOMMON) $(LIBRELAYD) $(LIBSESSIOND_COMM) \