X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=lttng-syscalls.c;h=5023c2f9dcc2ff8c588ad159303255dabab92ac6;hb=f1e7d826123df8fe2164aa22fa0f418debc0df1b;hp=1872f161faa22955587c609bbc8eb7013edebcde;hpb=63728b0280b098167c6fc9f8e423b134fdd83a88;p=lttng-modules.git diff --git a/lttng-syscalls.c b/lttng-syscalls.c index 1872f161..5023c2f9 100644 --- a/lttng-syscalls.c +++ b/lttng-syscalls.c @@ -1,46 +1,254 @@ /* * lttng-syscalls.c * - * Copyright 2010 (c) - Mathieu Desnoyers + * LTTng syscall probes. * - * LTTng sched probes. + * Copyright (C) 2010-2012 Mathieu Desnoyers * - * Dual LGPL v2.1/GPL v2 license. + * 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 -#include "ltt-events.h" +#include "wrapper/tracepoint.h" +#include "lttng-events.h" + +#ifndef CONFIG_COMPAT +# ifndef is_compat_task +# define is_compat_task() (0) +# endif +#endif + +enum sc_type { + SC_TYPE_ENTRY, + SC_TYPE_EXIT, + SC_TYPE_COMPAT_ENTRY, + SC_TYPE_COMPAT_EXIT, +}; + +#define SYSCALL_ENTRY_STR "syscall_entry_" +#define COMPAT_SYSCALL_ENTRY_STR "compat_syscall_entry_" +#define SYSCALL_EXIT_STR "syscall_exit_" +#define COMPAT_SYSCALL_EXIT_STR "compat_syscall_exit_" -static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id); +static +void syscall_entry_probe(void *__data, struct pt_regs *regs, long id); +static +void syscall_exit_probe(void *__data, struct pt_regs *regs, long ret); /* - * Take care of NOARGS not supported by mainline. + * Forward declarations for old kernels. */ -#define DECLARE_EVENT_CLASS_NOARGS(name, tstruct, assign, print) -#define DEFINE_EVENT_NOARGS(template, name) -#define TRACE_EVENT_NOARGS(name, struct, assign, print) +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 /* * Create LTTng tracepoint probes. */ #define LTTNG_PACKAGE_BUILD #define CREATE_TRACE_POINTS +#define TP_MODULE_NOINIT +#define TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers -/* Hijack probe callback for system calls */ +#define PARAMS(args...) args + +/* Handle unknown syscalls */ +#define TRACE_SYSTEM syscalls_unknown +#include "instrumentation/syscalls/headers/syscalls_unknown.h" +#undef TRACE_SYSTEM + +#define SC_ENTER + +#undef sc_exit +#define sc_exit(...) +#undef sc_in +#define sc_in(...) __VA_ARGS__ +#undef sc_out +#define sc_out(...) +#undef sc_inout +#define sc_inout(...) __VA_ARGS__ + +/* Hijack probe callback for system call enter */ +#undef TP_PROBE_CB #define TP_PROBE_CB(_template) &syscall_entry_probe -#define TP_MODULE_OVERRIDE +#define SC_LTTNG_TRACEPOINT_EVENT(_name, _proto, _args, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT(syscall_enter_##_name, PARAMS(_proto), PARAMS(_args), \ + PARAMS(_struct), PARAMS(_assign), PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_CODE(_name, _proto, _args, _locvar, _code, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT_CODE(syscall_enter_##_name, PARAMS(_proto), PARAMS(_args), \ + PARAMS(_locvar), PARAMS(_code), \ + PARAMS(_struct), PARAMS(_assign), PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(_name, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(syscall_enter_##_name, PARAMS(_struct), PARAMS(_assign), \ + PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(_template, _name) \ + LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(syscall_enter_##_template, syscall_enter_##_name) +#undef TRACE_SYSTEM +#define TRACE_SYSTEM syscall_enter_integers +#define TRACE_INCLUDE_FILE syscalls_integers +#include "instrumentation/syscalls/headers/syscalls_integers.h" +#undef TRACE_INCLUDE_FILE +#undef TRACE_SYSTEM +#define TRACE_SYSTEM syscall_enter_pointers +#define TRACE_INCLUDE_FILE syscalls_pointers +#include "instrumentation/syscalls/headers/syscalls_pointers.h" +#undef TRACE_INCLUDE_FILE +#undef TRACE_SYSTEM +#undef SC_LTTNG_TRACEPOINT_EVENT_CODE +#undef SC_LTTNG_TRACEPOINT_EVENT +#undef SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS +#undef SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS +#undef TP_PROBE_CB +#undef _TRACE_SYSCALLS_INTEGERS_H +#undef _TRACE_SYSCALLS_POINTERS_H -#define TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers +/* Hijack probe callback for compat system call enter */ +#define TP_PROBE_CB(_template) &syscall_entry_probe +#define SC_LTTNG_TRACEPOINT_EVENT(_name, _proto, _args, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT(compat_syscall_enter_##_name, PARAMS(_proto), PARAMS(_args), \ + PARAMS(_struct), PARAMS(_assign), \ + PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_CODE(_name, _proto, _args, _locvar, _code, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT_CODE(compat_syscall_enter_##_name, PARAMS(_proto), PARAMS(_args), \ + PARAMS(_locvar), PARAMS(_code), \ + PARAMS(_struct), PARAMS(_assign), PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(_name, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(compat_syscall_enter_##_name, PARAMS(_struct), \ + PARAMS(_assign), PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(_template, _name) \ + LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(compat_syscall_enter_##_template, \ + compat_syscall_enter_##_name) +#define TRACE_SYSTEM compat_syscall_enter_integers +#define TRACE_INCLUDE_FILE compat_syscalls_integers +#include "instrumentation/syscalls/headers/compat_syscalls_integers.h" +#undef TRACE_INCLUDE_FILE +#undef TRACE_SYSTEM +#define TRACE_SYSTEM compat_syscall_enter_pointers +#define TRACE_INCLUDE_FILE compat_syscalls_pointers +#include "instrumentation/syscalls/headers/compat_syscalls_pointers.h" +#undef TRACE_INCLUDE_FILE +#undef TRACE_SYSTEM +#undef SC_LTTNG_TRACEPOINT_EVENT_CODE +#undef SC_LTTNG_TRACEPOINT_EVENT +#undef SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS +#undef SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS +#undef TP_PROBE_CB +#undef _TRACE_SYSCALLS_INTEGERS_H +#undef _TRACE_SYSCALLS_POINTERS_H + +#undef SC_ENTER + +#define SC_EXIT + +#undef sc_exit +#define sc_exit(...) __VA_ARGS__ +#undef sc_in +#define sc_in(...) +#undef sc_out +#define sc_out(...) __VA_ARGS__ +#undef sc_inout +#define sc_inout(...) __VA_ARGS__ + +/* Hijack probe callback for system call exit */ +#define TP_PROBE_CB(_template) &syscall_exit_probe +#define SC_LTTNG_TRACEPOINT_EVENT(_name, _proto, _args, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT(syscall_exit_##_name, PARAMS(_proto), PARAMS(_args), \ + PARAMS(_struct), PARAMS(_assign), PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_CODE(_name, _proto, _args, _locvar, _code, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT_CODE(syscall_exit_##_name, PARAMS(_proto), PARAMS(_args), \ + PARAMS(_locvar), PARAMS(_code), \ + PARAMS(_struct), PARAMS(_assign), PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(_name, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(syscall_exit_##_name, PARAMS(_struct), \ + PARAMS(_assign), PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(_template, _name) \ + LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(syscall_exit_##_template, \ + syscall_exit_##_name) +#define TRACE_SYSTEM syscall_exit_integers +#define TRACE_INCLUDE_FILE syscalls_integers #include "instrumentation/syscalls/headers/syscalls_integers.h" +#undef TRACE_INCLUDE_FILE +#undef TRACE_SYSTEM +#define TRACE_SYSTEM syscall_exit_pointers +#define TRACE_INCLUDE_FILE syscalls_pointers #include "instrumentation/syscalls/headers/syscalls_pointers.h" -#include "instrumentation/syscalls/headers/syscalls_unknown.h" +#undef TRACE_INCLUDE_FILE +#undef TRACE_SYSTEM +#undef SC_LTTNG_TRACEPOINT_EVENT_CODE +#undef SC_LTTNG_TRACEPOINT_EVENT +#undef SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS +#undef SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS +#undef TP_PROBE_CB +#undef _TRACE_SYSCALLS_INTEGERS_H +#undef _TRACE_SYSCALLS_POINTERS_H + -#undef TP_MODULE_OVERRIDE +/* Hijack probe callback for compat system call exit */ +#define TP_PROBE_CB(_template) &syscall_exit_probe +#define SC_LTTNG_TRACEPOINT_EVENT(_name, _proto, _args, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT(compat_syscall_exit_##_name, PARAMS(_proto), PARAMS(_args), \ + PARAMS(_struct), PARAMS(_assign), PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_CODE(_name, _proto, _args, _locvar, _code, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT_CODE(compat_syscall_exit_##_name, PARAMS(_proto), PARAMS(_args), \ + PARAMS(_locvar), PARAMS(_code), \ + PARAMS(_struct), PARAMS(_assign), PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(_name, _struct, _assign, _printk) \ + LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS(compat_syscall_exit_##_name, PARAMS(_struct), \ + PARAMS(_assign), PARAMS(_printk)) +#define SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(_template, _name) \ + LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS(compat_syscall_exit_##_template, \ + compat_syscall_exit_##_name) +#define TRACE_SYSTEM compat_syscall_exit_integers +#define TRACE_INCLUDE_FILE compat_syscalls_integers +#include "instrumentation/syscalls/headers/compat_syscalls_integers.h" +#undef TRACE_INCLUDE_FILE +#undef TRACE_SYSTEM +#define TRACE_SYSTEM compat_syscall_exit_pointers +#define TRACE_INCLUDE_FILE compat_syscalls_pointers +#include "instrumentation/syscalls/headers/compat_syscalls_pointers.h" +#undef TRACE_INCLUDE_FILE +#undef TRACE_SYSTEM +#undef SC_LTTNG_TRACEPOINT_EVENT_CODE +#undef SC_LTTNG_TRACEPOINT_EVENT +#undef SC_LTTNG_TRACEPOINT_EVENT_CLASS_NOARGS +#undef SC_LTTNG_TRACEPOINT_EVENT_INSTANCE_NOARGS #undef TP_PROBE_CB +#undef _TRACE_SYSCALLS_INTEGERS_H +#undef _TRACE_SYSCALLS_POINTERS_H + +#undef SC_EXIT + +#undef TP_MODULE_NOINIT #undef LTTNG_PACKAGE_BUILD #undef CREATE_TRACE_POINTS @@ -53,37 +261,149 @@ struct trace_syscall_entry { #define CREATE_SYSCALL_TABLE +#define SC_ENTER + +#undef sc_exit +#define sc_exit(...) + #undef TRACE_SYSCALL_TABLE #define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \ [ _nr ] = { \ - .func = __event_probe__##_template, \ + .func = __event_probe__syscall_enter_##_template, \ .nrargs = (_nrargs), \ - .fields = __event_fields___##_template, \ - .desc = &__event_desc___##_name, \ + .fields = __event_fields___syscall_enter_##_template, \ + .desc = &__event_desc___syscall_enter_##_name, \ }, -static struct trace_syscall_entry sc_table[] = { +/* Syscall enter tracing table */ +static const struct trace_syscall_entry sc_table[] = { #include "instrumentation/syscalls/headers/syscalls_integers.h" #include "instrumentation/syscalls/headers/syscalls_pointers.h" }; -static int sc_table_filled; +#undef TRACE_SYSCALL_TABLE +#define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \ + [ _nr ] = { \ + .func = __event_probe__compat_syscall_enter_##_template, \ + .nrargs = (_nrargs), \ + .fields = __event_fields___compat_syscall_enter_##_template, \ + .desc = &__event_desc___compat_syscall_enter_##_name, \ + }, + +/* Compat syscall enter table */ +const struct trace_syscall_entry compat_sc_table[] = { +#include "instrumentation/syscalls/headers/compat_syscalls_integers.h" +#include "instrumentation/syscalls/headers/compat_syscalls_pointers.h" +}; + +#undef SC_ENTER + +#define SC_EXIT + +#undef sc_exit +#define sc_exit(...) __VA_ARGS__ + +#undef TRACE_SYSCALL_TABLE +#define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \ + [ _nr ] = { \ + .func = __event_probe__syscall_exit_##_template, \ + .nrargs = (_nrargs), \ + .fields = __event_fields___syscall_exit_##_template, \ + .desc = &__event_desc___syscall_exit_##_name, \ + }, + +/* Syscall exit table */ +static const struct trace_syscall_entry sc_exit_table[] = { +#include "instrumentation/syscalls/headers/syscalls_integers.h" +#include "instrumentation/syscalls/headers/syscalls_pointers.h" +}; + +#undef TRACE_SYSCALL_TABLE +#define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \ + [ _nr ] = { \ + .func = __event_probe__compat_syscall_exit_##_template, \ + .nrargs = (_nrargs), \ + .fields = __event_fields___compat_syscall_exit_##_template, \ + .desc = &__event_desc___compat_syscall_exit_##_name, \ + }, + +/* Compat syscall exit table */ +const struct trace_syscall_entry compat_sc_exit_table[] = { +#include "instrumentation/syscalls/headers/compat_syscalls_integers.h" +#include "instrumentation/syscalls/headers/compat_syscalls_pointers.h" +}; + +#undef SC_EXIT #undef CREATE_SYSCALL_TABLE -static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id) +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) +{ + unsigned long args[UNKNOWN_SYSCALL_NRARGS]; + + syscall_get_arguments(current, regs, 0, UNKNOWN_SYSCALL_NRARGS, args); + if (unlikely(is_compat_task())) + __event_probe__compat_syscall_enter_unknown(event, id, args); + else + __event_probe__syscall_enter_unknown(event, id, args); +} + +void syscall_entry_probe(void *__data, struct pt_regs *regs, long id) { - struct trace_syscall_entry *entry; - struct ltt_channel *chan = __data; - struct ltt_event *event; + struct lttng_channel *chan = __data; + struct lttng_event *event, *unknown_event; + const struct trace_syscall_entry *table, *entry; + size_t table_len; - if (unlikely(id >= ARRAY_SIZE(sc_table))) + 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; + } + if (unlikely(id >= table_len)) { + syscall_entry_unknown(unknown_event, regs, id); return; - entry = &sc_table[id]; - if (unlikely(!entry->func)) + } + if (unlikely(is_compat_task())) + event = chan->compat_sc_table[id]; + else + event = chan->sc_table[id]; + if (unlikely(!event)) { + syscall_entry_unknown(unknown_event, regs, id); return; - event = chan->sc_table[id]; - WARN_ON_ONCE(!event); + } + entry = &table[id]; + WARN_ON_ONCE(!entry); switch (entry->nrargs) { case 0: @@ -173,85 +493,378 @@ static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id) } } -static void fill_sc_table(void) +static void syscall_exit_unknown(struct lttng_event *event, + struct pt_regs *regs, unsigned int id, long ret) { - int i; + unsigned long args[UNKNOWN_SYSCALL_NRARGS]; - if (sc_table_filled) { - smp_rmb(); /* read flag before table */ + syscall_get_arguments(current, regs, 0, UNKNOWN_SYSCALL_NRARGS, args); + if (unlikely(is_compat_task())) + __event_probe__compat_syscall_exit_unknown(event, id, ret, + args); + else + __event_probe__syscall_exit_unknown(event, id, ret, args); +} + +void syscall_exit_probe(void *__data, struct pt_regs *regs, long ret) +{ + struct lttng_channel *chan = __data; + struct lttng_event *event, *unknown_event; + const struct trace_syscall_entry *table, *entry; + size_t table_len; + long id; + + id = syscall_get_nr(current, regs); + 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_exit_table; + table_len = ARRAY_SIZE(compat_sc_exit_table); + unknown_event = chan->compat_sc_exit_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_exit_table; + table_len = ARRAY_SIZE(sc_exit_table); + unknown_event = chan->sc_exit_unknown; + } + if (unlikely(id >= table_len)) { + syscall_exit_unknown(unknown_event, regs, id, ret); + return; + } + if (unlikely(is_compat_task())) + event = chan->compat_sc_exit_table[id]; + else + event = chan->sc_exit_table[id]; + if (unlikely(!event)) { + syscall_exit_unknown(unknown_event, regs, id, ret); return; } + entry = &table[id]; + WARN_ON_ONCE(!entry); - for (i = 0; i < ARRAY_SIZE(sc_table); i++) { - if (sc_table[i].func) - continue; - sc_table[i].func = __event_probe__sys_unknown; - sc_table[i].nrargs = UNKNOWN_SYSCALL_NRARGS; - sc_table[i].fields = __event_fields___sys_unknown; - sc_table[i].desc = &__event_desc___sys_unknown; + switch (entry->nrargs) { + case 0: + { + void (*fptr)(void *__data, long ret) = entry->func; + + fptr(event, ret); + break; } - smp_wmb(); /* Fill sc table before set flag to 1 */ - sc_table_filled = 1; -} + case 1: + { + void (*fptr)(void *__data, + long ret, + unsigned long arg0) = entry->func; + unsigned long args[1]; -int lttng_syscalls_register(struct ltt_channel *chan, void *filter) -{ - unsigned int i; - int ret; + syscall_get_arguments(current, regs, 0, entry->nrargs, args); + fptr(event, ret, args[0]); + break; + } + case 2: + { + void (*fptr)(void *__data, + long ret, + unsigned long arg0, + unsigned long arg1) = entry->func; + unsigned long args[2]; - wrapper_vmalloc_sync_all(); + syscall_get_arguments(current, regs, 0, entry->nrargs, args); + fptr(event, ret, args[0], args[1]); + break; + } + case 3: + { + void (*fptr)(void *__data, + long ret, + unsigned long arg0, + unsigned long arg1, + unsigned long arg2) = entry->func; + unsigned long args[3]; - fill_sc_table(); + syscall_get_arguments(current, regs, 0, entry->nrargs, args); + fptr(event, ret, args[0], args[1], args[2]); + break; + } + case 4: + { + void (*fptr)(void *__data, + long ret, + unsigned long arg0, + unsigned long arg1, + unsigned long arg2, + unsigned long arg3) = entry->func; + unsigned long args[4]; - if (!chan->sc_table) { - /* create syscall table mapping syscall to events */ - chan->sc_table = kzalloc(sizeof(struct ltt_event *) - * ARRAY_SIZE(sc_table), GFP_KERNEL); - if (!chan->sc_table) - return -ENOMEM; + syscall_get_arguments(current, regs, 0, entry->nrargs, args); + fptr(event, ret, args[0], args[1], args[2], args[3]); + break; + } + case 5: + { + void (*fptr)(void *__data, + long ret, + unsigned long arg0, + unsigned long arg1, + unsigned long arg2, + unsigned long arg3, + unsigned long arg4) = entry->func; + unsigned long args[5]; + + syscall_get_arguments(current, regs, 0, entry->nrargs, args); + fptr(event, ret, args[0], args[1], args[2], args[3], args[4]); + break; + } + case 6: + { + void (*fptr)(void *__data, + long ret, + unsigned long arg0, + unsigned long arg1, + unsigned long arg2, + unsigned long arg3, + unsigned long arg4, + unsigned long arg5) = entry->func; + unsigned long args[6]; + + syscall_get_arguments(current, regs, 0, entry->nrargs, args); + fptr(event, ret, args[0], args[1], args[2], + args[3], args[4], args[5]); + break; + } + default: + break; } +} + +/* noinline to diminish caller stack size */ +static +int fill_table(const struct trace_syscall_entry *table, size_t table_len, + struct lttng_event **chan_table, struct lttng_channel *chan, + void *filter, enum sc_type type) +{ + const struct lttng_event_desc *desc; + unsigned int i; /* Allocate events for each syscall, insert into table */ - for (i = 0; i < ARRAY_SIZE(sc_table); i++) { + for (i = 0; i < table_len; i++) { struct lttng_kernel_event ev; - const struct lttng_event_desc *desc = sc_table[i].desc; + desc = table[i].desc; - WARN_ON_ONCE(!desc); + if (!desc) { + /* Unknown syscall */ + continue; + } /* * Skip those already populated by previous failed * register for this channel. */ - if (chan->sc_table[i]) + if (chan_table[i]) continue; memset(&ev, 0, sizeof(ev)); - strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN); - ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0'; + switch (type) { + case SC_TYPE_ENTRY: + strncpy(ev.name, SYSCALL_ENTRY_STR, + LTTNG_KERNEL_SYM_NAME_LEN); + break; + case SC_TYPE_EXIT: + strncpy(ev.name, SYSCALL_EXIT_STR, + LTTNG_KERNEL_SYM_NAME_LEN); + break; + case SC_TYPE_COMPAT_ENTRY: + strncpy(ev.name, COMPAT_SYSCALL_ENTRY_STR, + LTTNG_KERNEL_SYM_NAME_LEN); + break; + case SC_TYPE_COMPAT_EXIT: + strncpy(ev.name, COMPAT_SYSCALL_EXIT_STR, + LTTNG_KERNEL_SYM_NAME_LEN); + break; + default: + BUG_ON(1); + break; + } + strncat(ev.name, desc->name, + LTTNG_KERNEL_SYM_NAME_LEN - strlen(ev.name) - 1); + ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; ev.instrumentation = LTTNG_KERNEL_NOOP; - chan->sc_table[i] = ltt_event_create(chan, &ev, filter, - desc); - if (!chan->sc_table[i]) { + chan_table[i] = lttng_event_create(chan, &ev, filter, + desc); + 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]); } } - ret = tracepoint_probe_register("sys_enter", - (void *) syscall_entry_probe, chan); + return 0; +} + +int lttng_syscalls_register(struct lttng_channel *chan, void *filter) +{ + struct lttng_kernel_event ev; + int ret; + + wrapper_vmalloc_sync_all(); + + if (!chan->sc_table) { + /* create syscall table mapping syscall to events */ + chan->sc_table = kzalloc(sizeof(struct lttng_event *) + * ARRAY_SIZE(sc_table), GFP_KERNEL); + if (!chan->sc_table) + return -ENOMEM; + } + if (!chan->sc_exit_table) { + /* create syscall table mapping syscall to events */ + chan->sc_exit_table = kzalloc(sizeof(struct lttng_event *) + * ARRAY_SIZE(sc_exit_table), GFP_KERNEL); + if (!chan->sc_exit_table) + return -ENOMEM; + } + + +#ifdef CONFIG_COMPAT + if (!chan->compat_sc_table) { + /* create syscall table mapping compat syscall to events */ + chan->compat_sc_table = kzalloc(sizeof(struct lttng_event *) + * ARRAY_SIZE(compat_sc_table), GFP_KERNEL); + if (!chan->compat_sc_table) + return -ENOMEM; + } + + if (!chan->compat_sc_exit_table) { + /* create syscall table mapping compat syscall to events */ + chan->compat_sc_exit_table = kzalloc(sizeof(struct lttng_event *) + * ARRAY_SIZE(compat_sc_exit_table), GFP_KERNEL); + if (!chan->compat_sc_exit_table) + return -ENOMEM; + } +#endif + if (!chan->sc_unknown) { + const struct lttng_event_desc *desc = + &__event_desc___syscall_enter_unknown; + + memset(&ev, 0, sizeof(ev)); + strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN); + ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + ev.instrumentation = LTTNG_KERNEL_NOOP; + chan->sc_unknown = lttng_event_create(chan, &ev, filter, + desc); + WARN_ON_ONCE(!chan->sc_unknown); + if (IS_ERR(chan->sc_unknown)) { + return PTR_ERR(chan->sc_unknown); + } + } + + if (!chan->sc_compat_unknown) { + const struct lttng_event_desc *desc = + &__event_desc___compat_syscall_enter_unknown; + + memset(&ev, 0, sizeof(ev)); + strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN); + ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + ev.instrumentation = LTTNG_KERNEL_NOOP; + chan->sc_compat_unknown = lttng_event_create(chan, &ev, filter, + desc); + WARN_ON_ONCE(!chan->sc_unknown); + if (IS_ERR(chan->sc_compat_unknown)) { + return PTR_ERR(chan->sc_compat_unknown); + } + } + + if (!chan->compat_sc_exit_unknown) { + const struct lttng_event_desc *desc = + &__event_desc___compat_syscall_exit_unknown; + + memset(&ev, 0, sizeof(ev)); + strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN); + ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + ev.instrumentation = LTTNG_KERNEL_NOOP; + chan->compat_sc_exit_unknown = lttng_event_create(chan, &ev, + filter, desc); + WARN_ON_ONCE(!chan->compat_sc_exit_unknown); + if (IS_ERR(chan->compat_sc_exit_unknown)) { + return PTR_ERR(chan->compat_sc_exit_unknown); + } + } + + if (!chan->sc_exit_unknown) { + const struct lttng_event_desc *desc = + &__event_desc___syscall_exit_unknown; + + memset(&ev, 0, sizeof(ev)); + strncpy(ev.name, desc->name, LTTNG_KERNEL_SYM_NAME_LEN); + ev.name[LTTNG_KERNEL_SYM_NAME_LEN - 1] = '\0'; + ev.instrumentation = LTTNG_KERNEL_NOOP; + chan->sc_exit_unknown = lttng_event_create(chan, &ev, filter, + desc); + WARN_ON_ONCE(!chan->sc_exit_unknown); + if (IS_ERR(chan->sc_exit_unknown)) { + return PTR_ERR(chan->sc_exit_unknown); + } + } + + ret = fill_table(sc_table, ARRAY_SIZE(sc_table), + chan->sc_table, chan, filter, SC_TYPE_ENTRY); + if (ret) + return ret; + ret = fill_table(sc_exit_table, ARRAY_SIZE(sc_exit_table), + chan->sc_exit_table, chan, filter, SC_TYPE_EXIT); + if (ret) + return ret; + +#ifdef CONFIG_COMPAT + ret = fill_table(compat_sc_table, ARRAY_SIZE(compat_sc_table), + chan->compat_sc_table, chan, filter, + SC_TYPE_COMPAT_ENTRY); if (ret) return ret; + ret = fill_table(compat_sc_exit_table, ARRAY_SIZE(compat_sc_exit_table), + chan->compat_sc_exit_table, chan, filter, + SC_TYPE_COMPAT_EXIT); + if (ret) + return ret; +#endif + 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); - 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 *) syscall_exit_probe, chan); + 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; } @@ -259,21 +872,194 @@ int lttng_syscalls_register(struct ltt_channel *chan, void *filter) /* * Only called at session destruction. */ -int lttng_syscalls_unregister(struct ltt_channel *chan) +int lttng_syscalls_unregister(struct lttng_channel *chan) { int ret; if (!chan->sc_table) return 0; - ret = tracepoint_probe_unregister("sys_exit", - (void *) __event_probe__exit_syscall, chan); - if (ret) - return ret; - ret = tracepoint_probe_unregister("sys_enter", - (void *) syscall_entry_probe, chan); - if (ret) - return ret; - /* ltt_event destroy will be performed by ltt_session_destroy() */ + if (chan->sys_enter_registered) { + ret = lttng_wrapper_tracepoint_probe_unregister("sys_exit", + (void *) syscall_exit_probe, chan); + 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); + kfree(chan->sc_exit_table); +#ifdef CONFIG_COMPAT + kfree(chan->compat_sc_table); + kfree(chan->compat_sc_exit_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; + const char *it_name; + + entry = &sc_table[i]; + if (!entry->desc) + continue; + it_name = entry->desc->name; + it_name += strlen(SYSCALL_ENTRY_STR); + if (!strcmp(syscall_name, it_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; + const char *it_name; + + entry = &compat_sc_table[i]; + if (!entry->desc) + continue; + it_name = entry->desc->name; + it_name += strlen(COMPAT_SYSCALL_ENTRY_STR); + if (!strcmp(syscall_name, it_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; }