From: Mathieu Desnoyers Date: Tue, 11 Nov 2014 21:31:40 +0000 (-0500) Subject: Implement kernel filter support X-Git-Tag: v2.7.0-rc1~53 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=00a620843422e5c972aee0ada2181b811fc81b92;p=lttng-tools.git Implement kernel filter support Signed-off-by: Mathieu Desnoyers Signed-off-by: Jérémie Galarneau --- diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 7eea28121..95e371d7b 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -1485,18 +1485,48 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, switch (event->type) { case LTTNG_EVENT_ALL: { + char *filter_expression_a = NULL; + struct lttng_filter_bytecode *filter_a = NULL; + + /* + * We need to duplicate filter_expression and filter, + * because ownership is passed to first enable + * event. + */ + if (filter_expression) { + filter_expression_a = strdup(filter_expression); + if (!filter_expression_a) { + ret = LTTNG_ERR_FATAL; + goto error; + } + } + if (filter) { + filter_a = zmalloc(sizeof(*filter_a) + filter->len); + if (!filter_a) { + free(filter_expression_a); + ret = LTTNG_ERR_FATAL; + goto error; + } + memcpy(filter_a, filter, sizeof(*filter_a) + filter->len); + } event->type = LTTNG_EVENT_TRACEPOINT; /* Hack */ - ret = event_kernel_enable_event(kchan, event); + ret = event_kernel_enable_event(kchan, event, + filter_expression, filter); if (ret != LTTNG_OK) { if (channel_created) { /* Let's not leak a useless channel. */ kernel_destroy_channel(kchan); } + free(filter_expression_a); + free(filter_a); goto error; } event->type = LTTNG_EVENT_SYSCALL; /* Hack */ - ret = event_kernel_enable_event(kchan, event); + ret = event_kernel_enable_event(kchan, event, + filter_expression_a, filter_a); if (ret != LTTNG_OK) { + free(filter_expression_a); + free(filter_a); goto error; } break; @@ -1505,7 +1535,8 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, case LTTNG_EVENT_FUNCTION: case LTTNG_EVENT_FUNCTION_ENTRY: case LTTNG_EVENT_TRACEPOINT: - ret = event_kernel_enable_event(kchan, event); + ret = event_kernel_enable_event(kchan, event, + filter_expression, filter); if (ret != LTTNG_OK) { if (channel_created) { /* Let's not leak a useless channel. */ @@ -1515,7 +1546,8 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, } break; case LTTNG_EVENT_SYSCALL: - ret = event_kernel_enable_event(kchan, event); + ret = event_kernel_enable_event(kchan, event, + filter_expression, filter); if (ret != LTTNG_OK) { goto error; } diff --git a/src/bin/lttng-sessiond/event.c b/src/bin/lttng-sessiond/event.c index 0efdc9253..32efcdbf2 100644 --- a/src/bin/lttng-sessiond/event.c +++ b/src/bin/lttng-sessiond/event.c @@ -129,7 +129,8 @@ int event_kernel_disable_event_all(struct ltt_kernel_channel *kchan) * We own filter_expression and filter. */ int event_kernel_enable_event(struct ltt_kernel_channel *kchan, - struct lttng_event *event) + struct lttng_event *event, char *filter_expression, + struct lttng_filter_bytecode *filter) { int ret; struct ltt_kernel_event *kevent; @@ -137,10 +138,11 @@ int event_kernel_enable_event(struct ltt_kernel_channel *kchan, assert(kchan); assert(event); - kevent = trace_kernel_get_event_by_name(event->name, kchan, - event->type); + kevent = trace_kernel_find_event(event->name, kchan, + event->type, filter); if (kevent == NULL) { - ret = kernel_create_event(event, kchan); + ret = kernel_create_event(event, kchan, + filter_expression, filter); if (ret < 0) { switch (-ret) { case EEXIST: diff --git a/src/bin/lttng-sessiond/event.h b/src/bin/lttng-sessiond/event.h index 536b2be70..cc4a3fa02 100644 --- a/src/bin/lttng-sessiond/event.h +++ b/src/bin/lttng-sessiond/event.h @@ -28,7 +28,8 @@ int event_kernel_disable_event_type(struct ltt_kernel_channel *kchan, int event_kernel_disable_event_all(struct ltt_kernel_channel *kchan); int event_kernel_enable_event(struct ltt_kernel_channel *kchan, - struct lttng_event *event); + struct lttng_event *event, char *filter_expression, + struct lttng_filter_bytecode *filter); int event_ust_enable_tracepoint(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan, struct lttng_event *event, diff --git a/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c index bf9f8b623..45d958546 100644 --- a/src/bin/lttng-sessiond/kernel.c +++ b/src/bin/lttng-sessiond/kernel.c @@ -177,7 +177,9 @@ error: * We own filter_expression and filter. */ int kernel_create_event(struct lttng_event *ev, - struct ltt_kernel_channel *channel) + struct ltt_kernel_channel *channel, + char *filter_expression, + struct lttng_filter_bytecode *filter) { int ret; struct ltt_kernel_event *event; @@ -185,7 +187,8 @@ int kernel_create_event(struct lttng_event *ev, assert(ev); assert(channel); - event = trace_kernel_create_event(ev); + event = trace_kernel_create_event(ev, filter_expression, + filter); if (event == NULL) { ret = -1; goto error; @@ -217,6 +220,26 @@ int kernel_create_event(struct lttng_event *ev, PERROR("fcntl session fd"); } + if (filter) { + ret = kernctl_filter(event->fd, filter); + if (ret) { + goto filter_error; + } + } + + ret = kernctl_enable(event->fd); + if (ret < 0) { + switch (errno) { + case EEXIST: + ret = LTTNG_ERR_KERN_EVENT_EXIST; + break; + default: + PERROR("enable kernel event"); + break; + } + goto enable_error; + } + /* Add event to event list */ cds_list_add(&event->list, &channel->events_list.head); channel->event_count++; @@ -225,6 +248,16 @@ int kernel_create_event(struct lttng_event *ev, return 0; +enable_error: +filter_error: + { + int closeret; + + closeret = close(event->fd); + if (closeret) { + PERROR("close event fd"); + } + } free_event: free(event); error: diff --git a/src/bin/lttng-sessiond/kernel.h b/src/bin/lttng-sessiond/kernel.h index 2f7cc15fb..2a9a76f92 100644 --- a/src/bin/lttng-sessiond/kernel.h +++ b/src/bin/lttng-sessiond/kernel.h @@ -37,7 +37,8 @@ int kernel_add_channel_context(struct ltt_kernel_channel *chan, int kernel_create_session(struct ltt_session *session, int tracer_fd); int kernel_create_channel(struct ltt_kernel_session *session, struct lttng_channel *chan); -int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel); +int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel, + char *filter_expression, struct lttng_filter_bytecode *filter); int kernel_disable_channel(struct ltt_kernel_channel *chan); int kernel_disable_event(struct ltt_kernel_event *event); int kernel_enable_event(struct ltt_kernel_event *event); diff --git a/src/bin/lttng-sessiond/save.c b/src/bin/lttng-sessiond/save.c index 6cb220c2f..0c6894b0a 100644 --- a/src/bin/lttng-sessiond/save.c +++ b/src/bin/lttng-sessiond/save.c @@ -510,7 +510,13 @@ int save_kernel_syscall(struct config_writer *writer, struct ltt_kernel_event *kevent; /* Create a temporary kevent in order to save it. */ - kevent = trace_kernel_create_event(&events[i]); + /* + * TODO: struct lttng_event does not really work for a filter, + * but unfortunately, it is exposed as external API (and used as + * internal representation. Using NULL meanwhile. + */ + kevent = trace_kernel_create_event(&events[i], + NULL, NULL); if (!kevent) { ret = -ENOMEM; goto end; diff --git a/src/bin/lttng-sessiond/trace-kernel.c b/src/bin/lttng-sessiond/trace-kernel.c index ab3282a17..33a9e7e55 100644 --- a/src/bin/lttng-sessiond/trace-kernel.c +++ b/src/bin/lttng-sessiond/trace-kernel.c @@ -58,6 +58,49 @@ struct ltt_kernel_channel *trace_kernel_get_channel_by_name( return NULL; } +/* + * Find the event for the given channel. + */ +struct ltt_kernel_event *trace_kernel_find_event( + char *name, struct ltt_kernel_channel *channel, + enum lttng_event_type type, + struct lttng_filter_bytecode *filter) +{ + struct ltt_kernel_event *ev; + int found = 0; + + assert(name); + assert(channel); + + cds_list_for_each_entry(ev, &channel->events_list.head, list) { + if (type != LTTNG_EVENT_ALL && ev->type != type) { + continue; + } + if (strcmp(name, ev->event->name)) { + continue; + } + if ((ev->filter && !filter) || (!ev->filter && filter)) { + continue; + } + if (ev->filter && filter) { + if (ev->filter->len != filter->len || + memcmp(ev->filter->data, filter->data, + filter->len) != 0) { + continue; + } + } + found = 1; + break; + } + if (found) { + DBG("Found event %s for channel %s", name, + channel->channel->name); + return ev; + } else { + return NULL; + } +} + /* * Find the event name for the given channel. */ @@ -66,21 +109,28 @@ struct ltt_kernel_event *trace_kernel_get_event_by_name( enum lttng_event_type type) { struct ltt_kernel_event *ev; + int found = 0; assert(name); assert(channel); cds_list_for_each_entry(ev, &channel->events_list.head, list) { - if (type != LTTNG_EVENT_ALL && ev->type != type) + if (type != LTTNG_EVENT_ALL && ev->type != type) { continue; - if (strcmp(name, ev->event->name) == 0) { - DBG("Found event by name %s for channel %s", name, - channel->channel->name); - return ev; } + if (strcmp(name, ev->event->name)) { + continue; + } + found = 1; + break; + } + if (found) { + DBG("Found event %s for channel %s", name, + channel->channel->name); + return ev; + } else { + return NULL; } - - return NULL; } /* @@ -211,7 +261,8 @@ error: * * Return pointer to structure or NULL. */ -struct ltt_kernel_event *trace_kernel_create_event(struct lttng_event *ev) +struct ltt_kernel_event *trace_kernel_create_event(struct lttng_event *ev, + char *filter_expression, struct lttng_filter_bytecode *filter) { struct ltt_kernel_event *lke; struct lttng_kernel_event *attr; @@ -270,6 +321,8 @@ struct ltt_kernel_event *trace_kernel_create_event(struct lttng_event *ev) lke->fd = -1; lke->event = attr; lke->enabled = 1; + lke->filter_expression = filter_expression; + lke->filter = filter; return lke; @@ -401,6 +454,9 @@ void trace_kernel_destroy_event(struct ltt_kernel_event *event) /* Remove from event list */ cds_list_del(&event->list); + free(event->filter_expression); + free(event->filter); + free(event->event); free(event); } diff --git a/src/bin/lttng-sessiond/trace-kernel.h b/src/bin/lttng-sessiond/trace-kernel.h index 5c518135d..9d50f4c79 100644 --- a/src/bin/lttng-sessiond/trace-kernel.h +++ b/src/bin/lttng-sessiond/trace-kernel.h @@ -54,6 +54,8 @@ struct ltt_kernel_event { enum lttng_event_type type; struct lttng_kernel_event *event; struct cds_list_head list; + char *filter_expression; + struct lttng_filter_bytecode *filter; }; /* Kernel channel */ @@ -125,6 +127,10 @@ struct ltt_kernel_session { struct ltt_kernel_event *trace_kernel_get_event_by_name( char *name, struct ltt_kernel_channel *channel, enum lttng_event_type type); +struct ltt_kernel_event *trace_kernel_find_event( + char *name, struct ltt_kernel_channel *channel, + enum lttng_event_type type, + struct lttng_filter_bytecode *filter); struct ltt_kernel_channel *trace_kernel_get_channel_by_name( char *name, struct ltt_kernel_session *session); @@ -134,7 +140,8 @@ struct ltt_kernel_channel *trace_kernel_get_channel_by_name( struct ltt_kernel_session *trace_kernel_create_session(void); struct ltt_kernel_channel *trace_kernel_create_channel( struct lttng_channel *chan); -struct ltt_kernel_event *trace_kernel_create_event(struct lttng_event *ev); +struct ltt_kernel_event *trace_kernel_create_event(struct lttng_event *ev, + char *filter_expression, struct lttng_filter_bytecode *filter); struct ltt_kernel_metadata *trace_kernel_create_metadata(void); struct ltt_kernel_stream *trace_kernel_create_stream(const char *name, unsigned int count); diff --git a/src/bin/lttng/commands/enable_events.c b/src/bin/lttng/commands/enable_events.c index 0de6c8d08..ab32994bd 100644 --- a/src/bin/lttng/commands/enable_events.c +++ b/src/bin/lttng/commands/enable_events.c @@ -713,11 +713,6 @@ static int enable_events(char *session_name) memset(&dom, 0, sizeof(dom)); 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."); } diff --git a/src/common/kernel-ctl/kernel-ctl.c b/src/common/kernel-ctl/kernel-ctl.c index 51d013413..28ed06cbd 100644 --- a/src/common/kernel-ctl/kernel-ctl.c +++ b/src/common/kernel-ctl/kernel-ctl.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "kernel-ctl.h" #include "kernel-ioctl.h" @@ -298,6 +299,25 @@ int kernctl_stop_session(int fd) LTTNG_KERNEL_SESSION_STOP); } +int kernctl_filter(int fd, struct lttng_filter_bytecode *filter) +{ + struct lttng_kernel_filter_bytecode *kb; + uint32_t len; + int ret; + + /* Translate bytecode to kernel bytecode */ + kb = zmalloc(sizeof(*kb) + filter->len); + if (!kb) + return -ENOMEM; + kb->len = len = filter->len; + kb->reloc_offset = filter->reloc_table_offset; + kb->seqnum = filter->seqnum; + memcpy(kb->data, filter->data, len); + ret = ioctl(fd, LTTNG_KERNEL_FILTER, kb); + free(kb); + return ret; +} + int kernctl_tracepoint_list(int fd) { return compat_ioctl_no_arg(fd, LTTNG_KERNEL_OLD_TRACEPOINT_LIST, diff --git a/src/common/kernel-ctl/kernel-ctl.h b/src/common/kernel-ctl/kernel-ctl.h index d8e69809e..b71b28530 100644 --- a/src/common/kernel-ctl/kernel-ctl.h +++ b/src/common/kernel-ctl/kernel-ctl.h @@ -22,6 +22,7 @@ #include #include #include +#include /* for struct lttng_filter_bytecode */ int kernctl_create_session(int fd); int kernctl_open_metadata(int fd, struct lttng_channel_attr *chops); @@ -35,6 +36,9 @@ int kernctl_disable(int fd); int kernctl_start_session(int fd); int kernctl_stop_session(int fd); +/* Apply on event FD */ +int kernctl_filter(int fd, struct lttng_filter_bytecode *filter); + int kernctl_tracepoint_list(int fd); int kernctl_syscall_list(int fd); int kernctl_tracer_version(int fd, struct lttng_kernel_tracer_version *v); diff --git a/src/common/kernel-ctl/kernel-ioctl.h b/src/common/kernel-ctl/kernel-ioctl.h index 671a76972..e469b5fe5 100644 --- a/src/common/kernel-ctl/kernel-ioctl.h +++ b/src/common/kernel-ctl/kernel-ioctl.h @@ -138,4 +138,7 @@ #define LTTNG_KERNEL_ENABLE _IO(0xF6, 0x82) #define LTTNG_KERNEL_DISABLE _IO(0xF6, 0x83) +/* Event FD ioctl */ +#define LTTNG_KERNEL_FILTER _IO(0xF6, 0x90) + #endif /* _LTT_KERNEL_IOCTL_H */ diff --git a/src/common/lttng-kernel.h b/src/common/lttng-kernel.h index 833624842..f1144de7f 100644 --- a/src/common/lttng-kernel.h +++ b/src/common/lttng-kernel.h @@ -152,4 +152,12 @@ struct lttng_kernel_channel { char padding[LTTNG_KERNEL_CHANNEL_PADDING1]; } LTTNG_PACKED; +#define KERNEL_FILTER_BYTECODE_MAX_LEN 65536 +struct lttng_kernel_filter_bytecode { + uint32_t len; + uint32_t reloc_offset; + uint64_t seqnum; + char data[0]; +} LTTNG_PACKED; + #endif /* _LTTNG_KERNEL_H */ diff --git a/src/lib/lttng-ctl/filter/filter-bytecode.h b/src/lib/lttng-ctl/filter/filter-bytecode.h index b9991284f..cdc334555 100644 --- a/src/lib/lttng-ctl/filter/filter-bytecode.h +++ b/src/lib/lttng-ctl/filter/filter-bytecode.h @@ -149,6 +149,10 @@ enum filter_op { FILTER_OP_GET_CONTEXT_REF_S64 = 72, FILTER_OP_GET_CONTEXT_REF_DOUBLE = 73, + /* load userspace field ref */ + FILTER_OP_LOAD_FIELD_REF_USER_STRING = 74, + FILTER_OP_LOAD_FIELD_REF_USER_SEQUENCE = 75, + NR_FILTER_OPS, }; diff --git a/tests/unit/test_kernel_data.c b/tests/unit/test_kernel_data.c index ca53c7d48..603d54cb2 100644 --- a/tests/unit/test_kernel_data.c +++ b/tests/unit/test_kernel_data.c @@ -139,7 +139,7 @@ static void test_create_kernel_event(void) ev.type = LTTNG_EVENT_TRACEPOINT; ev.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; - event = trace_kernel_create_event(&ev); + event = trace_kernel_create_event(&ev, NULL, NULL); ok(event != NULL, "Create kernel event"); ok(event->fd == -1 &&