From 20591cf76d5dfd42d61a18bd494b40a70a04dd59 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Wed, 19 Mar 2014 23:12:32 -0400 Subject: [PATCH] Adapt to 3.15 tracepoint API Signed-off-by: Mathieu Desnoyers --- Makefile | 4 + lttng-events.c | 16 +- lttng-statedump-impl.c | 7 + lttng-syscalls.c | 10 +- lttng-tracepoint.c | 451 +++++++++++++++++++++++++++++++++++++++++ lttng-tracepoint.h | 31 +++ probes/define_trace.h | 27 +-- probes/lttng.c | 2 + wrapper/tracepoint.h | 27 ++- 9 files changed, 546 insertions(+), 29 deletions(-) create mode 100644 lttng-tracepoint.c create mode 100644 lttng-tracepoint.h diff --git a/Makefile b/Makefile index a4c602fd..606b5ec8 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,10 @@ lttng-tracer-objs += $(shell \ echo "lttng-context-perf-counters.o" ; fi;) endif # CONFIG_PERF_EVENTS +lttng-tracer-objs += $(shell \ + if [ $(VERSION) -eq 3 -a $(PATCHLEVEL) -ge 15 -a $(SUBLEVEL) -ge 0 ] ; then \ + echo "lttng-tracepoint.o" ; fi;) + obj-m += probes/ obj-m += lib/ diff --git a/lttng-events.c b/lttng-events.c index ff28c9af..eefee69b 100644 --- a/lttng-events.c +++ b/lttng-events.c @@ -400,7 +400,7 @@ struct lttng_event *lttng_event_create(struct lttng_channel *chan, ret = -ENOENT; goto register_error; } - ret = kabi_2635_tracepoint_probe_register(event->desc->kname, + ret = lttng_wrapper_tracepoint_probe_register(event->desc->kname, event->desc->probe_callback, event); if (ret) { @@ -519,7 +519,7 @@ int _lttng_event_unregister(struct lttng_event *event) switch (event->instrumentation) { case LTTNG_KERNEL_TRACEPOINT: - ret = kabi_2635_tracepoint_probe_unregister(event->desc->kname, + ret = lttng_wrapper_tracepoint_probe_unregister(event->desc->kname, event->desc->probe_callback, event); if (ret) @@ -1255,9 +1255,14 @@ static int __init lttng_events_init(void) { int ret; + ret = lttng_tracepoint_init(); + if (ret) + return ret; event_cache = KMEM_CACHE(lttng_event, 0); - if (!event_cache) - return -ENOMEM; + if (!event_cache) { + ret = -ENOMEM; + goto error_kmem; + } ret = lttng_abi_init(); if (ret) goto error_abi; @@ -1270,6 +1275,8 @@ error_logger: lttng_abi_exit(); error_abi: kmem_cache_destroy(event_cache); +error_kmem: + lttng_tracepoint_exit(); return ret; } @@ -1284,6 +1291,7 @@ static void __exit lttng_events_exit(void) list_for_each_entry_safe(session, tmpsession, &sessions, list) lttng_session_destroy(session); kmem_cache_destroy(event_cache); + lttng_tracepoint_exit(); } module_exit(lttng_events_exit); diff --git a/lttng-statedump-impl.c b/lttng-statedump-impl.c index 40eab8e5..3e46ca16 100644 --- a/lttng-statedump-impl.c +++ b/lttng-statedump-impl.c @@ -65,6 +65,13 @@ #define TRACE_INCLUDE_FILE lttng-statedump #include "instrumentation/events/lttng-module/lttng-statedump.h" +DEFINE_TRACE(lttng_statedump_end); +DEFINE_TRACE(lttng_statedump_interrupt); +DEFINE_TRACE(lttng_statedump_file_descriptor); +DEFINE_TRACE(lttng_statedump_start); +DEFINE_TRACE(lttng_statedump_process_state); +DEFINE_TRACE(lttng_statedump_network_interface); + struct lttng_fd_ctx { char *page; struct lttng_session *session; diff --git a/lttng-syscalls.c b/lttng-syscalls.c index 32155641..f452b487 100644 --- a/lttng-syscalls.c +++ b/lttng-syscalls.c @@ -419,7 +419,7 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter) if (ret) return ret; #endif - ret = kabi_2635_tracepoint_probe_register("sys_enter", + ret = lttng_wrapper_tracepoint_probe_register("sys_enter", (void *) syscall_entry_probe, chan); if (ret) return ret; @@ -427,11 +427,11 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter) * We change the name of sys_exit tracepoint due to namespace * conflict with sys_exit syscall entry. */ - ret = kabi_2635_tracepoint_probe_register("sys_exit", + ret = lttng_wrapper_tracepoint_probe_register("sys_exit", (void *) __event_probe__exit_syscall, chan->sc_exit); if (ret) { - WARN_ON_ONCE(kabi_2635_tracepoint_probe_unregister("sys_enter", + WARN_ON_ONCE(lttng_wrapper_tracepoint_probe_unregister("sys_enter", (void *) syscall_entry_probe, chan)); } return ret; @@ -446,12 +446,12 @@ int lttng_syscalls_unregister(struct lttng_channel *chan) if (!chan->sc_table) return 0; - ret = kabi_2635_tracepoint_probe_unregister("sys_exit", + ret = lttng_wrapper_tracepoint_probe_unregister("sys_exit", (void *) __event_probe__exit_syscall, chan->sc_exit); if (ret) return ret; - ret = kabi_2635_tracepoint_probe_unregister("sys_enter", + ret = lttng_wrapper_tracepoint_probe_unregister("sys_enter", (void *) syscall_entry_probe, chan); if (ret) return ret; diff --git a/lttng-tracepoint.c b/lttng-tracepoint.c new file mode 100644 index 00000000..13f9c247 --- /dev/null +++ b/lttng-tracepoint.c @@ -0,0 +1,451 @@ +/* + * lttng-tracepoint.c + * + * LTTng adaptation layer for Linux kernel 3.15+ tracepoints. + * + * Copyright (C) 2014 Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; only + * version 2.1 of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lttng-tracepoint.h" + +/* + * Protect the tracepoint table. lttng_tracepoint_mutex nests within + * kernel/tracepoint.c tp_modlist_mutex. kernel/tracepoint.c + * tracepoint_mutex nests within lttng_tracepoint_mutex. + */ +static +DEFINE_MUTEX(lttng_tracepoint_mutex); + +#define TRACEPOINT_HASH_BITS 6 +#define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS) +static +struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE]; + +/* + * The tracepoint entry is the node contained within the hash table. It + * is a mapping from the "string" key to the struct tracepoint pointer. + */ +struct tracepoint_entry { + struct hlist_node hlist; + struct tracepoint *tp; + int refcount; + struct list_head probes; + char name[0]; +}; + +struct lttng_tp_probe { + struct tracepoint_func tp_func; + struct list_head list; +}; + +static +int add_probe(struct tracepoint_entry *e, void *probe, void *data) +{ + struct lttng_tp_probe *p; + int found = 0; + + list_for_each_entry(p, &e->probes, list) { + if (p->tp_func.func == probe && p->tp_func.data == data) { + found = 1; + break; + } + } + if (found) + return -EEXIST; + p = kmalloc(sizeof(struct lttng_tp_probe), GFP_KERNEL); + if (!p) + return -ENOMEM; + p->tp_func.func = probe; + p->tp_func.data = data; + list_add(&p->list, &e->probes); + return 0; +} + +static +int remove_probe(struct tracepoint_entry *e, void *probe, void *data) +{ + struct lttng_tp_probe *p; + int found = 0; + + list_for_each_entry(p, &e->probes, list) { + if (p->tp_func.func == probe && p->tp_func.data == data) { + found = 1; + break; + } + } + if (found) { + list_del(&p->list); + kfree(p); + return 0; + } else { + WARN_ON(1); + return -ENOENT; + } +} + +/* + * Get tracepoint if the tracepoint is present in the tracepoint hash table. + * Must be called with lttng_tracepoint_mutex held. + * Returns NULL if not present. + */ +static +struct tracepoint_entry *get_tracepoint(const char *name) +{ + struct hlist_head *head; + struct tracepoint_entry *e; + u32 hash = jhash(name, strlen(name), 0); + + head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)]; + hlist_for_each_entry(e, head, hlist) { + if (!strcmp(name, e->name)) + return e; + } + return NULL; +} + +/* + * Add the tracepoint to the tracepoint hash table. Must be called with + * lttng_tracepoint_mutex held. + */ +static +struct tracepoint_entry *add_tracepoint(const char *name) +{ + struct hlist_head *head; + struct tracepoint_entry *e; + size_t name_len = strlen(name) + 1; + u32 hash = jhash(name, name_len - 1, 0); + + head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)]; + hlist_for_each_entry(e, head, hlist) { + if (!strcmp(name, e->name)) { + printk(KERN_NOTICE + "tracepoint %s busy\n", name); + return ERR_PTR(-EEXIST); /* Already there */ + } + } + /* + * Using kmalloc here to allocate a variable length element. Could + * cause some memory fragmentation if overused. + */ + e = kmalloc(sizeof(struct tracepoint_entry) + name_len, GFP_KERNEL); + if (!e) + return ERR_PTR(-ENOMEM); + memcpy(&e->name[0], name, name_len); + e->tp = NULL; + e->refcount = 0; + INIT_LIST_HEAD(&e->probes); + hlist_add_head(&e->hlist, head); + return e; +} + +/* + * Remove the tracepoint from the tracepoint hash table. Must be called + * with lttng_tracepoint_mutex held. + */ +static +void remove_tracepoint(struct tracepoint_entry *e) +{ + hlist_del(&e->hlist); + kfree(e); +} + +int lttng_tracepoint_probe_register(const char *name, void *probe, void *data) +{ + struct tracepoint_entry *e; + int ret = 0; + + mutex_lock(<tng_tracepoint_mutex); + e = get_tracepoint(name); + if (!e) { + e = add_tracepoint(name); + if (IS_ERR(e)) { + ret = PTR_ERR(e); + goto end; + } + } + /* add (probe, data) to entry */ + ret = add_probe(e, probe, data); + if (ret) + goto end; + e->refcount++; + if (e->tp) { + ret = tracepoint_probe_register(e->tp, probe, data); + WARN_ON_ONCE(ret); + ret = 0; + } +end: + mutex_unlock(<tng_tracepoint_mutex); + return ret; +} + +int lttng_tracepoint_probe_unregister(const char *name, void *probe, void *data) +{ + struct tracepoint_entry *e; + int ret = 0; + + mutex_lock(<tng_tracepoint_mutex); + e = get_tracepoint(name); + if (!e) { + ret = -ENOENT; + goto end; + } + /* remove (probe, data) from entry */ + ret = remove_probe(e, probe, data); + if (ret) + goto end; + if (e->tp) { + ret = tracepoint_probe_unregister(e->tp, probe, data); + WARN_ON_ONCE(ret); + ret = 0; + } + if (!--e->refcount) + remove_tracepoint(e); +end: + mutex_unlock(<tng_tracepoint_mutex); + return ret; +} + +#ifdef CONFIG_MODULES + +static +int lttng_tracepoint_coming(struct tp_module *tp_mod) +{ + int i; + + mutex_lock(<tng_tracepoint_mutex); + for (i = 0; i < tp_mod->mod->num_tracepoints; i++) { + struct tracepoint *tp; + struct tracepoint_entry *e; + struct lttng_tp_probe *p; + + tp = tp_mod->mod->tracepoints_ptrs[i]; + e = get_tracepoint(tp->name); + if (!e) { + e = add_tracepoint(tp->name); + if (IS_ERR(e)) { + pr_warn("LTTng: error (%ld) adding tracepoint\n", + PTR_ERR(e)); + continue; + } + } + /* If already enabled, just check consistency */ + if (e->tp) { + WARN_ON(e->tp != tp); + continue; + } + e->tp = tp; + e->refcount++; + /* register each (probe, data) */ + list_for_each_entry(p, &e->probes, list) { + int ret; + + ret = tracepoint_probe_register(e->tp, + p->tp_func.func, p->tp_func.data); + WARN_ON_ONCE(ret); + } + } + mutex_unlock(<tng_tracepoint_mutex); + return 0; +} + +static +int lttng_tracepoint_going(struct tp_module *tp_mod) +{ + int i; + + mutex_lock(<tng_tracepoint_mutex); + for (i = 0; i < tp_mod->mod->num_tracepoints; i++) { + struct tracepoint *tp; + struct tracepoint_entry *e; + struct lttng_tp_probe *p; + + tp = tp_mod->mod->tracepoints_ptrs[i]; + e = get_tracepoint(tp->name); + if (!e || !e->tp) + continue; + /* unregister each (probe, data) */ + list_for_each_entry(p, &e->probes, list) { + int ret; + + ret = tracepoint_probe_unregister(e->tp, + p->tp_func.func, p->tp_func.data); + WARN_ON_ONCE(ret); + } + e->tp = NULL; + if (!--e->refcount) + remove_tracepoint(e); + } + mutex_unlock(<tng_tracepoint_mutex); + return 0; +} + +static +int lttng_tracepoint_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + struct tp_module *tp_mod = data; + int ret = 0; + + switch (val) { + case MODULE_STATE_COMING: + ret = lttng_tracepoint_coming(tp_mod); + break; + case MODULE_STATE_GOING: + ret = lttng_tracepoint_going(tp_mod); + break; + default: + break; + } + return ret; +} + +static +struct notifier_block lttng_tracepoint_notifier = { + .notifier_call = lttng_tracepoint_notify, + .priority = 0, +}; + +static +int lttng_tracepoint_module_init(void) +{ + return register_tracepoint_module_notifier(<tng_tracepoint_notifier); +} + +static +void lttng_tracepoint_module_exit(void) +{ + WARN_ON(unregister_tracepoint_module_notifier(<tng_tracepoint_notifier)); +} + +#else /* #ifdef CONFIG_MODULES */ + +static +int lttng_tracepoint_module_init(void) +{ + return 0; +} + +static +void lttng_tracepoint_module_exit(void) +{ +} + +#endif /* #else #ifdef CONFIG_MODULES */ + +static +void lttng_kernel_tracepoint_add(struct tracepoint *tp, void *priv) +{ + struct tracepoint_entry *e; + struct lttng_tp_probe *p; + int *ret = priv; + + mutex_lock(<tng_tracepoint_mutex); + e = get_tracepoint(tp->name); + if (!e) { + e = add_tracepoint(tp->name); + if (IS_ERR(e)) { + pr_warn("LTTng: error (%ld) adding tracepoint\n", + PTR_ERR(e)); + *ret = (int) PTR_ERR(e); + goto end; + } + } + /* If already enabled, just check consistency */ + if (e->tp) { + WARN_ON(e->tp != tp); + goto end; + } + e->tp = tp; + e->refcount++; + /* register each (probe, data) */ + list_for_each_entry(p, &e->probes, list) { + int ret; + + ret = tracepoint_probe_register(e->tp, + p->tp_func.func, p->tp_func.data); + WARN_ON_ONCE(ret); + } +end: + mutex_unlock(<tng_tracepoint_mutex); +} + +static +void lttng_kernel_tracepoint_remove(struct tracepoint *tp, void *priv) +{ + struct tracepoint_entry *e; + int *ret = priv; + + mutex_lock(<tng_tracepoint_mutex); + e = get_tracepoint(tp->name); + if (!e || e->refcount != 1 || !list_empty(&e->probes)) { + *ret = -EINVAL; + goto end; + } + remove_tracepoint(e); +end: + mutex_unlock(<tng_tracepoint_mutex); +} + +int __init lttng_tracepoint_init(void) +{ + int ret = 0; + + for_each_kernel_tracepoint(lttng_kernel_tracepoint_add, &ret); + if (ret) + goto error; + ret = lttng_tracepoint_module_init(); + if (ret) + goto error_module; + return 0; + +error_module: + { + int error_ret = 0; + + for_each_kernel_tracepoint(lttng_kernel_tracepoint_remove, + &error_ret); + WARN_ON(error_ret); + } +error: + return ret; +} + +void lttng_tracepoint_exit(void) +{ + int i, ret = 0; + + lttng_tracepoint_module_exit(); + for_each_kernel_tracepoint(lttng_kernel_tracepoint_remove, &ret); + WARN_ON(ret); + mutex_lock(<tng_tracepoint_mutex); + for (i = 0; i < TRACEPOINT_TABLE_SIZE; i++) { + struct hlist_head *head = &tracepoint_table[i]; + + /* All tracepoints should be removed */ + WARN_ON(!hlist_empty(head)); + } + mutex_unlock(<tng_tracepoint_mutex); +} diff --git a/lttng-tracepoint.h b/lttng-tracepoint.h new file mode 100644 index 00000000..e4f815d6 --- /dev/null +++ b/lttng-tracepoint.h @@ -0,0 +1,31 @@ +#ifndef _LTTNG_TRACEPOINT_H +#define _LTTNG_TRACEPOINT_H + +/* + * lttng-tracepoint.h + * + * LTTng adaptation layer for Linux kernel 3.15+ tracepoints. + * + * Copyright (C) 2014 Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; only + * version 2.1 of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +int lttng_tracepoint_probe_register(const char *name, void *probe, void *data); +int lttng_tracepoint_probe_unregister(const char *name, void *probe, void *data); +int lttng_tracepoint_init(void); +void lttng_tracepoint_exit(void); + +#endif /* _LTTNG_TRACEPOINT_H */ diff --git a/probes/define_trace.h b/probes/define_trace.h index 96d80a58..ed5233af 100644 --- a/probes/define_trace.h +++ b/probes/define_trace.h @@ -54,8 +54,7 @@ #include #undef TRACE_EVENT_MAP -#define TRACE_EVENT_MAP(name, map, proto, args, tstruct, assign, print) \ - DEFINE_TRACE(name) +#define TRACE_EVENT_MAP(name, map, proto, args, tstruct, assign, print) #undef TRACE_EVENT_CONDITION_MAP #define TRACE_EVENT_CONDITION_MAP(name, map, proto, args, cond, tstruct, assign, print) \ @@ -68,16 +67,13 @@ #undef TRACE_EVENT_FN_MAP #define TRACE_EVENT_FN_MAP(name, map, proto, args, tstruct, \ - assign, print, reg, unreg) \ - DEFINE_TRACE_FN(name, reg, unreg) + assign, print, reg, unreg) #undef DEFINE_EVENT_MAP -#define DEFINE_EVENT_MAP(template, name, map, proto, args) \ - DEFINE_TRACE(name) +#define DEFINE_EVENT_MAP(template, name, map, proto, args) #undef DEFINE_EVENT_PRINT_MAP -#define DEFINE_EVENT_PRINT_MAP(template, name, map, proto, args, print) \ - DEFINE_TRACE(name) +#define DEFINE_EVENT_PRINT_MAP(template, name, map, proto, args, print) #undef DEFINE_EVENT_CONDITION_MAP #define DEFINE_EVENT_CONDITION_MAP(template, name, map, proto, args, cond) \ @@ -85,8 +81,7 @@ #undef TRACE_EVENT -#define TRACE_EVENT(name, proto, args, tstruct, assign, print) \ - DEFINE_TRACE(name) +#define TRACE_EVENT(name, proto, args, tstruct, assign, print) #undef TRACE_EVENT_CONDITION #define TRACE_EVENT_CONDITION(name, proto, args, cond, tstruct, assign, print) \ @@ -99,24 +94,20 @@ #undef TRACE_EVENT_FN #define TRACE_EVENT_FN(name, proto, args, tstruct, \ - assign, print, reg, unreg) \ - DEFINE_TRACE_FN(name, reg, unreg) + assign, print, reg, unreg) #undef DEFINE_EVENT -#define DEFINE_EVENT(template, name, proto, args) \ - DEFINE_TRACE(name) +#define DEFINE_EVENT(template, name, proto, args) #undef DEFINE_EVENT_PRINT -#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \ - DEFINE_TRACE(name) +#define DEFINE_EVENT_PRINT(template, name, proto, args, print) #undef DEFINE_EVENT_CONDITION #define DEFINE_EVENT_CONDITION(template, name, proto, args, cond) \ DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args)) #undef DECLARE_TRACE -#define DECLARE_TRACE(name, proto, args) \ - DEFINE_TRACE(name) +#define DECLARE_TRACE(name, proto, args) #undef TRACE_INCLUDE #undef __TRACE_INCLUDE diff --git a/probes/lttng.c b/probes/lttng.c index 80256876..65f9061a 100644 --- a/probes/lttng.c +++ b/probes/lttng.c @@ -43,6 +43,8 @@ #define LTTNG_LOGGER_COUNT_MAX 1024 #define LTTNG_LOGGER_FILE "lttng-logger" +DEFINE_TRACE(lttng_logger); + static struct proc_dir_entry *lttng_logger_dentry; /** diff --git a/wrapper/tracepoint.h b/wrapper/tracepoint.h index 5f429db7..d094f42a 100644 --- a/wrapper/tracepoint.h +++ b/wrapper/tracepoint.h @@ -37,11 +37,34 @@ #define kabi_2635_tracepoint_probe_register tracepoint_probe_register #define kabi_2635_tracepoint_probe_unregister tracepoint_probe_unregister -#define kabi_2635_tracepoint_probe_register_noupdate tracepoint_probe_register_noupdate -#define kabi_2635_tracepoint_probe_unregister_noupdate tracepoint_probe_unregister_noupdate #endif /* HAVE_KABI_2635_TRACEPOINT */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)) + +#include "../lttng-tracepoint.h" + +#define lttng_wrapper_tracepoint_probe_register lttng_tracepoint_probe_register +#define lttng_wrapper_tracepoint_probe_unregister lttng_tracepoint_probe_unregister + +#else /* #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)) */ + +#define lttng_wrapper_tracepoint_probe_register kabi_2635_tracepoint_probe_register +#define lttng_wrapper_tracepoint_probe_unregister kabi_2635_tracepoint_probe_unregister + +static inline +int lttng_tracepoint_init(void) +{ + return 0; +} + +static inline +void lttng_tracepoint_exit(void) +{ +} + +#endif /* #else #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,15,0)) */ + #ifdef CONFIG_MODULE_SIG #include -- 2.34.1