X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=lttng-syscalls.c;h=f12c81c0215872f7edaa7c35b1a0db4644c5146e;hb=80f87dd2321bb99f5b8cdab7ea6ff33f6ba99b9e;hp=ebf32f8ed3274ad7ce90ec14e59a64df23ca390d;hpb=886d51a3d7ed5fa6b41d7f19b3e14ae6c535a44c;p=deliverable%2Flttng-modules.git diff --git a/lttng-syscalls.c b/lttng-syscalls.c index ebf32f8e..f12c81c0 100644 --- a/lttng-syscalls.c +++ b/lttng-syscalls.c @@ -23,21 +23,39 @@ #include #include #include +#include +#include #include #include +#include "wrapper/tracepoint.h" #include "lttng-events.h" #ifndef CONFIG_COMPAT -static inline int is_compat_task(void) -{ - return 0; -} +# ifndef is_compat_task +# define is_compat_task() (0) +# endif #endif static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id); +/* + * Forward declarations for old kernels. + */ +struct mmsghdr; +struct rlimit64; +struct oldold_utsname; +struct old_utsname; +struct sel_arg_struct; +struct mmap_arg_struct; + +#ifdef IA32_NR_syscalls +#define NR_compat_syscalls IA32_NR_syscalls +#else +#define NR_compat_syscalls NR_syscalls +#endif + /* * Take care of NOARGS not supported by mainline. */ @@ -50,7 +68,7 @@ void syscall_entry_probe(void *__data, struct pt_regs *regs, long id); */ #define LTTNG_PACKAGE_BUILD #define CREATE_TRACE_POINTS -#define TP_MODULE_OVERRIDE +#define TP_MODULE_NOINIT #define TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers #define PARAMS(args...) args @@ -108,7 +126,7 @@ void syscall_entry_probe(void *__data, struct pt_regs *regs, long id); #undef SC_DEFINE_EVENT_NOARGS #undef TP_PROBE_CB -#undef TP_MODULE_OVERRIDE +#undef TP_MODULE_NOINIT #undef LTTNG_PACKAGE_BUILD #undef CREATE_TRACE_POINTS @@ -152,6 +170,11 @@ const struct trace_syscall_entry compat_sc_table[] = { #undef CREATE_SYSCALL_TABLE +struct lttng_syscall_filter { + DECLARE_BITMAP(sc, NR_syscalls); + DECLARE_BITMAP(sc_compat, NR_compat_syscalls); +}; + static void syscall_entry_unknown(struct lttng_event *event, struct pt_regs *regs, unsigned int id) { @@ -172,10 +195,30 @@ void syscall_entry_probe(void *__data, struct pt_regs *regs, long id) size_t table_len; if (unlikely(is_compat_task())) { + struct lttng_syscall_filter *filter; + + filter = rcu_dereference(chan->sc_filter); + if (filter) { + if (id >= NR_compat_syscalls + || !test_bit(id, filter->sc_compat)) { + /* System call filtered out. */ + return; + } + } table = compat_sc_table; table_len = ARRAY_SIZE(compat_sc_table); unknown_event = chan->sc_compat_unknown; } else { + struct lttng_syscall_filter *filter; + + filter = rcu_dereference(chan->sc_filter); + if (filter) { + if (id >= NR_syscalls + || !test_bit(id, filter->sc)) { + /* System call filtered out. */ + return; + } + } table = sc_table; table_len = ARRAY_SIZE(sc_table); unknown_event = chan->sc_unknown; @@ -312,14 +355,15 @@ int fill_table(const struct trace_syscall_entry *table, size_t table_len, ev.instrumentation = LTTNG_KERNEL_NOOP; chan_table[i] = lttng_event_create(chan, &ev, filter, desc); - if (!chan_table[i]) { + WARN_ON_ONCE(!chan_table[i]); + if (IS_ERR(chan_table[i])) { /* * If something goes wrong in event registration * after the first one, we have no choice but to * leave the previous events in there, until * deleted by session teardown. */ - return -EINVAL; + return PTR_ERR(chan_table[i]); } } return 0; @@ -359,8 +403,9 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter) ev.instrumentation = LTTNG_KERNEL_NOOP; chan->sc_unknown = lttng_event_create(chan, &ev, filter, desc); - if (!chan->sc_unknown) { - return -EINVAL; + WARN_ON_ONCE(!chan->sc_unknown); + if (IS_ERR(chan->sc_unknown)) { + return PTR_ERR(chan->sc_unknown); } } @@ -374,8 +419,9 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter) ev.instrumentation = LTTNG_KERNEL_NOOP; chan->sc_compat_unknown = lttng_event_create(chan, &ev, filter, desc); - if (!chan->sc_compat_unknown) { - return -EINVAL; + WARN_ON_ONCE(!chan->sc_unknown); + if (IS_ERR(chan->sc_compat_unknown)) { + return PTR_ERR(chan->sc_compat_unknown); } } @@ -389,8 +435,9 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter) ev.instrumentation = LTTNG_KERNEL_NOOP; chan->sc_exit = lttng_event_create(chan, &ev, filter, desc); - if (!chan->sc_exit) { - return -EINVAL; + WARN_ON_ONCE(!chan->sc_exit); + if (IS_ERR(chan->sc_exit)) { + return PTR_ERR(chan->sc_exit); } } @@ -404,20 +451,27 @@ int lttng_syscalls_register(struct lttng_channel *chan, void *filter) if (ret) return ret; #endif - ret = tracepoint_probe_register("sys_enter", - (void *) syscall_entry_probe, chan); - if (ret) - return ret; + if (!chan->sys_enter_registered) { + ret = lttng_wrapper_tracepoint_probe_register("sys_enter", + (void *) syscall_entry_probe, chan); + if (ret) + return ret; + chan->sys_enter_registered = 1; + } /* * We change the name of sys_exit tracepoint due to namespace * conflict with sys_exit syscall entry. */ - ret = tracepoint_probe_register("sys_exit", - (void *) __event_probe__exit_syscall, - chan->sc_exit); - if (ret) { - WARN_ON_ONCE(tracepoint_probe_unregister("sys_enter", - (void *) syscall_entry_probe, chan)); + if (!chan->sys_exit_registered) { + ret = lttng_wrapper_tracepoint_probe_register("sys_exit", + (void *) __event_probe__exit_syscall, + chan->sc_exit); + if (ret) { + WARN_ON_ONCE(lttng_wrapper_tracepoint_probe_unregister("sys_enter", + (void *) syscall_entry_probe, chan)); + return ret; + } + chan->sys_exit_registered = 1; } return ret; } @@ -431,19 +485,181 @@ int lttng_syscalls_unregister(struct lttng_channel *chan) if (!chan->sc_table) return 0; - ret = tracepoint_probe_unregister("sys_exit", - (void *) __event_probe__exit_syscall, - chan->sc_exit); - if (ret) - return ret; - ret = tracepoint_probe_unregister("sys_enter", - (void *) syscall_entry_probe, chan); - if (ret) - return ret; + if (chan->sys_enter_registered) { + ret = lttng_wrapper_tracepoint_probe_unregister("sys_exit", + (void *) __event_probe__exit_syscall, + chan->sc_exit); + if (ret) + return ret; + chan->sys_enter_registered = 0; + } + if (chan->sys_exit_registered) { + ret = lttng_wrapper_tracepoint_probe_unregister("sys_enter", + (void *) syscall_entry_probe, chan); + if (ret) + return ret; + chan->sys_exit_registered = 0; + } /* lttng_event destroy will be performed by lttng_session_destroy() */ kfree(chan->sc_table); #ifdef CONFIG_COMPAT kfree(chan->compat_sc_table); #endif + kfree(chan->sc_filter); return 0; } + +static +int get_syscall_nr(const char *syscall_name) +{ + int syscall_nr = -1; + int i; + + for (i = 0; i < ARRAY_SIZE(sc_table); i++) { + const struct trace_syscall_entry *entry; + + entry = &sc_table[i]; + if (!entry->desc) + continue; + if (!strcmp(syscall_name, entry->desc->name)) { + syscall_nr = i; + break; + } + } + return syscall_nr; +} + +static +int get_compat_syscall_nr(const char *syscall_name) +{ + int syscall_nr = -1; + int i; + + for (i = 0; i < ARRAY_SIZE(compat_sc_table); i++) { + const struct trace_syscall_entry *entry; + + entry = &compat_sc_table[i]; + if (!entry->desc) + continue; + if (!strcmp(syscall_name, entry->desc->name)) { + syscall_nr = i; + break; + } + } + return syscall_nr; +} + +int lttng_syscall_filter_enable(struct lttng_channel *chan, + const char *name) +{ + int syscall_nr, compat_syscall_nr, ret; + struct lttng_syscall_filter *filter; + + WARN_ON_ONCE(!chan->sc_table); + + if (!name) { + /* Enable all system calls by removing filter */ + if (chan->sc_filter) { + filter = chan->sc_filter; + rcu_assign_pointer(chan->sc_filter, NULL); + synchronize_trace(); + kfree(filter); + } + chan->syscall_all = 1; + return 0; + } + + if (!chan->sc_filter) { + if (chan->syscall_all) { + /* + * All syscalls are already enabled. + */ + return -EEXIST; + } + filter = kzalloc(sizeof(struct lttng_syscall_filter), + GFP_KERNEL); + if (!filter) + return -ENOMEM; + } else { + filter = chan->sc_filter; + } + syscall_nr = get_syscall_nr(name); + compat_syscall_nr = get_compat_syscall_nr(name); + if (syscall_nr < 0 && compat_syscall_nr < 0) { + ret = -ENOENT; + goto error; + } + if (syscall_nr >= 0) { + if (test_bit(syscall_nr, filter->sc)) { + ret = -EEXIST; + goto error; + } + bitmap_set(filter->sc, syscall_nr, 1); + } + if (compat_syscall_nr >= 0) { + if (test_bit(compat_syscall_nr, filter->sc_compat)) { + ret = -EEXIST; + goto error; + } + bitmap_set(filter->sc_compat, compat_syscall_nr, 1); + } + if (!chan->sc_filter) + rcu_assign_pointer(chan->sc_filter, filter); + return 0; + +error: + if (!chan->sc_filter) + kfree(filter); + return ret; +} + +int lttng_syscall_filter_disable(struct lttng_channel *chan, + const char *name) +{ + int syscall_nr, compat_syscall_nr, ret; + struct lttng_syscall_filter *filter; + + WARN_ON_ONCE(!chan->sc_table); + + if (!chan->sc_filter) { + filter = kzalloc(sizeof(struct lttng_syscall_filter), + GFP_KERNEL); + if (!filter) + return -ENOMEM; + /* Trace all system calls, then apply disable. */ + bitmap_set(filter->sc, 0, NR_syscalls); + bitmap_set(filter->sc_compat, 0, NR_compat_syscalls); + } else { + filter = chan->sc_filter; + } + + syscall_nr = get_syscall_nr(name); + compat_syscall_nr = get_compat_syscall_nr(name); + if (syscall_nr < 0 && compat_syscall_nr < 0) { + ret = -ENOENT; + goto error; + } + if (syscall_nr >= 0) { + if (!test_bit(syscall_nr, chan->sc_filter->sc)) { + ret = -EEXIST; + goto error; + } + bitmap_clear(chan->sc_filter->sc, syscall_nr, 1); + } + if (compat_syscall_nr >= 0) { + if (!test_bit(compat_syscall_nr, chan->sc_filter->sc_compat)) { + ret = -EEXIST; + goto error; + } + bitmap_clear(chan->sc_filter->sc_compat, compat_syscall_nr, 1); + } + if (!chan->sc_filter) + rcu_assign_pointer(chan->sc_filter, filter); + chan->syscall_all = 0; + return 0; + +error: + if (!chan->sc_filter) + kfree(filter); + return ret; +}