From ac471a4e4b112ac1eba7979862ff51874e363f47 Mon Sep 17 00:00:00 2001 From: Francis Deslauriers Date: Fri, 24 Mar 2017 16:26:39 -0400 Subject: [PATCH 1/2] Add `gen_kernel_events` procfile for on-demand kernel tracepoint Signed-off-by: Francis Deslauriers --- .../events/lttng-module/lttng-test.h | 16 + tests/probes/lttng-test.c | 307 ++++++++++++++++++ 2 files changed, 323 insertions(+) diff --git a/instrumentation/events/lttng-module/lttng-test.h b/instrumentation/events/lttng-module/lttng-test.h index 05d028ca..5a0a37c5 100644 --- a/instrumentation/events/lttng-module/lttng-test.h +++ b/instrumentation/events/lttng-module/lttng-test.h @@ -48,6 +48,22 @@ LTTNG_TRACEPOINT_EVENT(lttng_test_filter_event, ) ) +LTTNG_TRACEPOINT_EVENT(lttng_test_event_a, + TP_PROTO(int id), + TP_ARGS(id), + TP_FIELDS( + ctf_integer(int, id, id) + ) +) + +LTTNG_TRACEPOINT_EVENT(lttng_test_event_b, + TP_PROTO(int id), + TP_ARGS(id), + TP_FIELDS( + ctf_integer(int, id, id) + ) +) + #endif /* LTTNG_TRACE_LTTNG_TEST_H */ /* This part must be outside protection */ diff --git a/tests/probes/lttng-test.c b/tests/probes/lttng-test.c index 54be4c7c..b7e85c42 100644 --- a/tests/probes/lttng-test.c +++ b/tests/probes/lttng-test.c @@ -20,9 +20,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include #include +#include +#include #include #include @@ -39,12 +42,37 @@ #include DEFINE_TRACE(lttng_test_filter_event); +DEFINE_TRACE(lttng_test_event_a); +DEFINE_TRACE(lttng_test_event_b); #define LTTNG_TEST_FILTER_EVENT_FILE "lttng-test-filter-event" +#define LTTNG_TEST_PROCFS_DIR "lttng-test" +#define LTTNG_TEST_GENERATE_EVENTS "gen-kernel-events" #define LTTNG_WRITE_COUNT_MAX 64 +#define LTTNG_TEST_SYM_LEN 40 + +/* Max number of events to generate in one call to avoid DoS */ +#define LTTNG_TEST_EVENT_MAX 100 + +/* The max command length is 3 symbols and 2 spaces */ +#define LTTNG_TEST_CMD_LEN ((LTTNG_TEST_SYM_LEN * 3) + 2) static struct proc_dir_entry *lttng_test_filter_event_dentry; +static struct proc_dir_entry *lttng_test_dentry; +static struct proc_dir_entry *lttng_test_gen_events_dentry; + +/** + * lttng_test_method: defines a method, its argument and a run callback. + * method_name: Name of the method + * arg_name: Name of the argument (for usage message) + * (*run): Function to call + */ +struct lttng_test_method { + char method_name[LTTNG_TEST_SYM_LEN]; + char arg_name[LTTNG_TEST_SYM_LEN]; + int (*run)(char *arg, int id); +}; static void trace_test_event(unsigned int nr_iter) @@ -94,6 +122,255 @@ static const struct file_operations lttng_test_filter_event_operations = { .write = lttng_test_filter_event_write, }; +/** + * event_a: triggers $arg lttng_test_event_a events + * @arg: string containing the number of event to generate + * @id: Unique identifier of the caller + * + * Returns 0 on success and a negative value on parsing error + */ +static int event_a(char *arg, int id) +{ + int i, ret; + unsigned long iter; + + ret = kstrtoul(arg, 10, &iter); + if (ret) { + printk(KERN_ERR "Failed to parse %s arg", __func__); + goto error; + } + + if (iter > LTTNG_TEST_EVENT_MAX) { + printk(KERN_ERR "Number of iteration (%lu) exceeding maximum ", iter); + ret = -EDOM; + goto error; + } + + ret = 0; + for (i = 0; i < iter; i++) { + trace_lttng_test_event_a(id); + } + +error: + return ret; +} + +/** + * event_b: triggers $arg lttng_test_event_b events + * @arg: string containing the number of event to generate + * @id: Unique identifier of the caller + * + * Returns 0 on success and a negative value on parsing error + */ +static int event_b(char *arg, int id) +{ + int i, ret; + unsigned long iter; + + ret = kstrtoul(arg, 10, &iter); + if (ret) { + printk(KERN_ERR "Failed to parse %s arg", __func__); + goto error; + } + + if (iter > LTTNG_TEST_EVENT_MAX) { + printk(KERN_ERR "Number of iteration (%lu) exceeding maximum ", iter); + ret = -EDOM; + goto error; + } + + ret = 0; + for (i = 0; i < iter; i++) { + trace_lttng_test_event_b(id); + } + +error: + return ret; +} + +/** + * Array of method descriptions. + */ +static struct lttng_test_method test_methods[] = { + {"event_a", "count", &event_a}, + {"event_b", "count", &event_b}, +}; + +/* + * Returns the number of space-delimited words in a given string. Returns -1, if + * non-ascii byte is found. + */ +static int count_word_in_str(char *str, size_t len) +{ + int i, count, prev_is_space; + + count = 0; + prev_is_space = 1; + for (i = 0; i < len && str[i] != '\0'; i++) { + if (!isascii(str[i])) { + count = -1; + break; + } + + switch (str[i]) { + case ' ': + prev_is_space = 1; + break; + default: + if (prev_is_space) { + count++; + prev_is_space = 0; + } + break; + } + } + return count; +} + +/** + * lttng_test_gen_events_write: run the speficied method with the specified + * argument. The accepted syntax is:` ` + * @file: file pointer + * @user_buf: user string + * @count: length to copy + * @ppos: current position in the file + * + * Return -1 on error, with EFAULT errno. Returns count on success to report a + * successful write to the caller. + * + */ +static +ssize_t lttng_test_gen_events_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *buf, *ptr; + char *user_method, *user_arg, *user_id; + int i, ret; + size_t buf_len; + unsigned long id; + + if (count > LTTNG_TEST_CMD_LEN) { + ret = -EINVAL; + goto kmalloc_err; + } + buf_len = count; + + buf = kmalloc(sizeof(char) * count, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto kmalloc_err; + } + ret = copy_from_user(buf, user_buf, count); + if (ret) { + printk(KERN_ERR "copy from user error\n"); + ret = -EFAULT; + goto error; + } + + buf[count-1] = '\0'; + + /* Check that the string is of the right format */ + if (count_word_in_str(buf, count) != 3) { + printk(KERN_ERR "lttng_gen_kernel_event wrong input format"); + ret = -EINVAL; + goto error; + } + + /* Extract the three fields for the user request */ + ptr = buf; + user_method = strsep(&ptr, " "); + if (!user_method) { + printk(KERN_ERR "strsep returned NULL"); + ret = -EINVAL; + goto error; + } + + if (strlen(user_method) >= LTTNG_TEST_SYM_LEN) { + ret = -EINVAL; + goto error; + } + + user_arg = strsep(&ptr, " "); + if (!user_arg) { + printk(KERN_ERR "strsep returned NULL"); + ret = -EINVAL; + goto error; + } + + if (strlen(user_arg) >= LTTNG_TEST_SYM_LEN) { + ret = -EINVAL; + goto error; + } + + user_id = ptr; + if (strlen(user_id) >= (LTTNG_TEST_SYM_LEN)) { + ret = -EINVAL; + goto error; + } + /* Convert the uuid from string to a int */ + ret = kstrtoul(user_id, 10, &id); + if (ret) { + printk(KERN_ERR "Failed to parse method uuid"); + ret = -EINVAL; + goto error; + } + + /* + * Iterate over all the methods and compare the name of the + * method and the string from the user + */ + ret = -EINVAL; + for (i = 0; i < ARRAY_SIZE(test_methods); i++) { + if (strcmp(user_method, test_methods[i].method_name) != 0) { + continue; + } + + /* + * Once a match is found, the method is called + * and the function returns + */ + ret = test_methods[i].run(user_arg, id); + + /* + * If the call is successful, return the number + * of count passed to write to return success to + * the caller. + */ + if (!ret) { + ret = count; + } + break; + } +error: + kfree(buf); +kmalloc_err: + return ret; +} + +static int lttng_test_gen_events_show(struct seq_file *m, void *v) +{ + int i; + seq_printf(m, "Available methods:\n"); + for (i = 0; i < ARRAY_SIZE(test_methods); i++) { + seq_printf(m, "%s %s unique-id\n", test_methods[i].method_name, + test_methods[i].arg_name); + } + return 0; +} + +static int lttng_test_gen_events_open(struct inode *inode, struct file *file) +{ + return single_open(file, lttng_test_gen_events_show, NULL); +} + +static const struct file_operations lttng_test_gen_events_operations = { + .write = lttng_test_gen_events_write, + .open = lttng_test_gen_events_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int __init lttng_test_init(void) { @@ -101,6 +378,10 @@ int __init lttng_test_init(void) (void) wrapper_lttng_fixup_sig(THIS_MODULE); wrapper_vmalloc_sync_all(); + /* + * The lttng_test_filter_event file is left in `/proc` + * for backward compatibility reason. + */ lttng_test_filter_event_dentry = proc_create_data(LTTNG_TEST_FILTER_EVENT_FILE, S_IRUGO | S_IWUGO, NULL, @@ -110,12 +391,34 @@ int __init lttng_test_init(void) ret = -ENOMEM; goto error; } + /* Create lttng-test proc directory */ + lttng_test_dentry = proc_mkdir(LTTNG_TEST_PROCFS_DIR, NULL); + if (!lttng_test_dentry) { + printk(KERN_ERR "Error creating LTTng test directory\n"); + ret = -ENOMEM; + goto error_proc_mkdir; + } + + lttng_test_gen_events_dentry = + proc_create_data(LTTNG_TEST_GENERATE_EVENTS, + S_IRUGO | S_IWUGO, lttng_test_dentry, + <tng_test_gen_events_operations, NULL); + if (!lttng_test_gen_events_dentry) { + printk(KERN_ERR "Error creating LTTng gen-events file\n"); + ret = -ENOMEM; + goto error_proc_gen_events; + } + ret = __lttng_events_init__lttng_test(); if (ret) goto error_events; return ret; error_events: + remove_proc_entry(LTTNG_TEST_GENERATE_EVENTS, lttng_test_dentry); +error_proc_gen_events: + remove_proc_entry(LTTNG_TEST_PROCFS_DIR, NULL); +error_proc_mkdir: remove_proc_entry(LTTNG_TEST_FILTER_EVENT_FILE, NULL); error: return ret; @@ -129,6 +432,10 @@ void __exit lttng_test_exit(void) __lttng_events_exit__lttng_test(); if (lttng_test_filter_event_dentry) remove_proc_entry(LTTNG_TEST_FILTER_EVENT_FILE, NULL); + if (lttng_test_gen_events_dentry && lttng_test_dentry) + remove_proc_entry(LTTNG_TEST_GENERATE_EVENTS, lttng_test_dentry); + if (lttng_test_dentry) + remove_proc_entry(LTTNG_TEST_PROCFS_DIR, NULL); } module_exit(lttng_test_exit); From 2c9e08861767763024588349983b43396adf5230 Mon Sep 17 00:00:00 2001 From: Francis Deslauriers Date: Thu, 23 Mar 2017 09:13:42 -0400 Subject: [PATCH 2/2] Add support for exclusion of kernel event Signed-off-by: Francis Deslauriers --- lttng-abi.c | 12 ++++++ lttng-abi.h | 7 ++++ lttng-events.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++- lttng-events.h | 12 ++++++ 4 files changed, 131 insertions(+), 2 deletions(-) diff --git a/lttng-abi.c b/lttng-abi.c index f2b207cb..7dec9dbc 100644 --- a/lttng-abi.c +++ b/lttng-abi.c @@ -1462,6 +1462,18 @@ long lttng_event_ioctl(struct file *file, unsigned int cmd, unsigned long arg) (struct lttng_kernel_filter_bytecode __user *) arg); } + } + case LTTNG_KERNEL_EXCLUSION: + switch (*evtype) { + case LTTNG_TYPE_EVENT: + return -EINVAL; + case LTTNG_TYPE_ENABLER: + { + enabler = file->private_data; + return lttng_enabler_attach_exclusion(enabler, + (struct lttng_kernel_event_exclusion __user *) arg); + } + } default: return -ENOIOCTLCMD; diff --git a/lttng-abi.h b/lttng-abi.h index dac86584..bff80eea 100644 --- a/lttng-abi.h +++ b/lttng-abi.h @@ -173,6 +173,12 @@ struct lttng_kernel_filter_bytecode { uint64_t seqnum; char data[0]; } __attribute__((packed)); +#define LTTNG_KERNEL_EXCLUSION_PADDING 32 +struct lttng_kernel_event_exclusion { + uint32_t count; + char padding[LTTNG_KERNEL_EXCLUSION_PADDING]; + char names[LTTNG_KERNEL_SYM_NAME_LEN][0]; +} __attribute__((packed)); /* LTTng file descriptor ioctl */ #define LTTNG_KERNEL_SESSION _IO(0xF6, 0x45) @@ -224,6 +230,7 @@ struct lttng_kernel_filter_bytecode { /* Event FD ioctl */ #define LTTNG_KERNEL_FILTER _IO(0xF6, 0x90) +#define LTTNG_KERNEL_EXCLUSION _IO(0xF6, 0x91) /* LTTng-specific ioctls for the lib ringbuffer */ /* returns the timestamp begin of the current sub-buffer */ diff --git a/lttng-events.c b/lttng-events.c index 6aa994ca..78a0077b 100644 --- a/lttng-events.c +++ b/lttng-events.c @@ -1144,7 +1144,7 @@ int lttng_session_list_tracker_pids(struct lttng_session *session) * Enabler management. */ static -int lttng_match_enabler_star_glob(const char *desc_name, +int lttng_match_event_pattern_star_glob(const char *desc_name, const char *pattern) { if (!strutils_star_glob_match(pattern, LTTNG_SIZE_MAX, @@ -1162,6 +1162,48 @@ int lttng_match_enabler_name(const char *desc_name, return 1; } +static +int lttng_match_enabler_exclusion(const char *desc_name, + struct lttng_enabler *enabler) +{ + struct lttng_excluder_node *excluder; + /** + * Iterate over the list of excluders of this enabler. If the + * event matches with an excluder, return 'do not enable' + */ + list_for_each_entry(excluder, &enabler->excluder_head, node) { + int count; + + for (count = 0; count < excluder->exclusion.count; count++) { + int len; + char *excluder_name; + + excluder_name = (char *) (excluder->exclusion.names) + + (count * LTTNG_KERNEL_SYM_NAME_LEN); + + len = strnlen(excluder_name, LTTNG_KERNEL_SYM_NAME_LEN); + + if (len <= 0) { + continue; + } + + /** + * If the event name matches with the exclusion, + * return 0 to signify that this event should _not_ be + * enabled + */ + if (lttng_match_event_pattern_star_glob(desc_name, excluder_name)) { + return 0; + } + } + } + /** + * If none of the exclusions match with the event name, + * return 1 to signify that this event should be enabled + */ + return 1; +} + static int lttng_desc_match_enabler(const struct lttng_event_desc *desc, struct lttng_enabler *enabler) @@ -1194,7 +1236,17 @@ int lttng_desc_match_enabler(const struct lttng_event_desc *desc, } switch (enabler->type) { case LTTNG_ENABLER_STAR_GLOB: - return lttng_match_enabler_star_glob(desc_name, enabler_name); + { + /** + * Return 'does not match' if the event name does not match with + * the enabler. + */ + if (!lttng_match_event_pattern_star_glob(desc_name, enabler_name)) { + return 0; + } + + return lttng_match_enabler_exclusion(desc_name, enabler); + } case LTTNG_ENABLER_NAME: return lttng_match_enabler_name(desc_name, enabler_name); default: @@ -1386,6 +1438,7 @@ struct lttng_enabler *lttng_enabler_create(enum lttng_enabler_type type, return NULL; enabler->type = type; INIT_LIST_HEAD(&enabler->filter_bytecode_head); + INIT_LIST_HEAD(&enabler->excluder_head); memcpy(&enabler->event_param, event_param, sizeof(enabler->event_param)); enabler->chan = chan; @@ -1447,6 +1500,45 @@ int lttng_enabler_attach_bytecode(struct lttng_enabler *enabler, return ret; } +int lttng_enabler_attach_exclusion(struct lttng_enabler *enabler, + struct lttng_kernel_event_exclusion __user *exclusion) +{ + struct lttng_excluder_node *excluder_node; + uint32_t exclusion_count; + int ret; + + ret = get_user(exclusion_count, &exclusion->count); + if (ret) { + goto error; + } + + excluder_node = kzalloc(sizeof(*excluder_node) + + (exclusion_count * LTTNG_KERNEL_SYM_NAME_LEN), + GFP_KERNEL); + if (!excluder_node) { + ret = -ENOMEM; + goto error; + } + + ret = copy_from_user(&excluder_node->exclusion, exclusion, + sizeof(*exclusion) + (exclusion_count * LTTNG_KERNEL_SYM_NAME_LEN)); + if (ret) { + goto error_free; + } + + excluder_node->enabler = enabler; + excluder_node->exclusion.count = exclusion_count; + + list_add_tail(&excluder_node->node, &enabler->excluder_head); + lttng_session_lazy_sync_enablers(enabler->chan->session); + return 0; + +error_free: + kfree(excluder_node); +error: + return ret; +} + int lttng_enabler_attach_context(struct lttng_enabler *enabler, struct lttng_kernel_context *context_param) { @@ -1457,6 +1549,7 @@ static void lttng_enabler_destroy(struct lttng_enabler *enabler) { struct lttng_filter_bytecode_node *filter_node, *tmp_filter_node; + struct lttng_excluder_node *excluder_node, *tmp_excluder_node; /* Destroy filter bytecode */ list_for_each_entry_safe(filter_node, tmp_filter_node, @@ -1464,6 +1557,11 @@ void lttng_enabler_destroy(struct lttng_enabler *enabler) kfree(filter_node); } + list_for_each_entry_safe(excluder_node, tmp_excluder_node, + &enabler->excluder_head, node) { + kfree(excluder_node); + } + /* Destroy contexts */ lttng_destroy_context(enabler->ctx); diff --git a/lttng-events.h b/lttng-events.h index f55bf663..b71f11b1 100644 --- a/lttng-events.h +++ b/lttng-events.h @@ -258,6 +258,15 @@ struct lttng_filter_bytecode_node { */ struct lttng_kernel_filter_bytecode bc; }; +struct lttng_excluder_node { + struct list_head node; + struct lttng_enabler *enabler; + /* + * struct lttng_kernel_event_exclusion has var. sized array, must be + * last field. + */ + struct lttng_kernel_event_exclusion exclusion; +}; /* * Filter return value masks. @@ -340,6 +349,7 @@ struct lttng_enabler { struct list_head node; /* per-session list of enablers */ /* head list of struct lttng_ust_filter_bytecode_node */ struct list_head filter_bytecode_head; + struct list_head excluder_head; struct lttng_kernel_event event_param; struct lttng_channel *chan; @@ -655,6 +665,8 @@ static inline long lttng_channel_syscall_mask(struct lttng_channel *channel, void lttng_filter_sync_state(struct lttng_bytecode_runtime *runtime); int lttng_enabler_attach_bytecode(struct lttng_enabler *enabler, struct lttng_kernel_filter_bytecode __user *bytecode); +int lttng_enabler_attach_exclusion(struct lttng_enabler *enabler, + struct lttng_kernel_event_exclusion __user *exclusion); void lttng_enabler_event_link_bytecode(struct lttng_event *event, struct lttng_enabler *enabler);