X-Git-Url: http://git.lttng.org./?a=blobdiff_plain;f=lttng-syscalls.c;h=5135dfe2f88501f47c4436df3b42f878cbb6b800;hb=49c50022873702bca7c7589e82c1addee410690c;hp=1872f161faa22955587c609bbc8eb7013edebcde;hpb=63728b0280b098167c6fc9f8e423b134fdd83a88;p=lttng-modules.git diff --git a/lttng-syscalls.c b/lttng-syscalls.c index 1872f161..5135dfe2 100644 --- a/lttng-syscalls.c +++ b/lttng-syscalls.c @@ -10,11 +10,19 @@ #include #include +#include #include #include #include "ltt-events.h" +#ifndef CONFIG_COMPAT +static inline int is_compat_task(void) +{ + return 0; +} +#endif + static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id); /* @@ -29,18 +37,18 @@ static 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 TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers /* Hijack probe callback for system calls */ #define TP_PROBE_CB(_template) &syscall_entry_probe -#define TP_MODULE_OVERRIDE - -#define TRACE_INCLUDE_PATH ../instrumentation/syscalls/headers #include "instrumentation/syscalls/headers/syscalls_integers.h" #include "instrumentation/syscalls/headers/syscalls_pointers.h" +#undef TP_PROBE_CB + #include "instrumentation/syscalls/headers/syscalls_unknown.h" #undef TP_MODULE_OVERRIDE -#undef TP_PROBE_CB #undef LTTNG_PACKAGE_BUILD #undef CREATE_TRACE_POINTS @@ -51,8 +59,6 @@ struct trace_syscall_entry { unsigned int nrargs; }; -#define CREATE_SYSCALL_TABLE - #undef TRACE_SYSCALL_TABLE #define TRACE_SYSCALL_TABLE(_template, _name, _nr, _nrargs) \ [ _nr ] = { \ @@ -62,28 +68,65 @@ struct trace_syscall_entry { .desc = &__event_desc___##_name, \ }, -static struct trace_syscall_entry sc_table[] = { +#define CREATE_SYSCALL_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; +/* Create compatibility syscall table */ +static 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 CREATE_SYSCALL_TABLE +static void syscall_entry_unknown(struct ltt_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); + __event_probe__sys_unknown(event, id, args); +} + +/* + * Currently, given that the kernel syscall metadata extraction only + * considers native system calls (not 32-bit compability ones), we + * fall-back on the "unknown" system call tracing for 32-bit compat. + */ static 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; - - if (unlikely(id >= ARRAY_SIZE(sc_table))) + struct ltt_event *event, *unknown_event; + const struct trace_syscall_entry *table, *entry; + size_t table_len; + + if (unlikely(is_compat_task())) { + table = compat_sc_table; + table_len = ARRAY_SIZE(compat_sc_table); + unknown_event = chan->sc_compat_unknown; + } else { + 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,36 +216,52 @@ static void syscall_entry_probe(void *__data, struct pt_regs *regs, long id) } } -static void fill_sc_table(void) +static +int fill_table(const struct trace_syscall_entry *table, size_t table_len, + struct ltt_event **chan_table, struct ltt_channel *chan, void *filter) { - int i; + unsigned int i; - if (sc_table_filled) { - smp_rmb(); /* read flag before table */ - return; - } + /* Allocate events for each syscall, insert into table */ + for (i = 0; i < table_len; i++) { + struct lttng_kernel_event ev; + const struct lttng_event_desc *desc = table[i].desc; - for (i = 0; i < ARRAY_SIZE(sc_table); i++) { - if (sc_table[i].func) + if (!desc) { + /* Unknown syscall */ 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; + } + /* + * Skip those already populated by previous failed + * register for this channel. + */ + 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'; + ev.instrumentation = LTTNG_KERNEL_NOOP; + chan_table[i] = ltt_event_create(chan, &ev, filter, + desc); + if (!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; + } } - smp_wmb(); /* Fill sc table before set flag to 1 */ - sc_table_filled = 1; + return 0; } int lttng_syscalls_register(struct ltt_channel *chan, void *filter) { - unsigned int i; int ret; wrapper_vmalloc_sync_all(); - fill_sc_table(); - if (!chan->sc_table) { /* create syscall table mapping syscall to events */ chan->sc_table = kzalloc(sizeof(struct ltt_event *) @@ -211,34 +270,73 @@ int lttng_syscalls_register(struct ltt_channel *chan, void *filter) return -ENOMEM; } - /* Allocate events for each syscall, insert into table */ - for (i = 0; i < ARRAY_SIZE(sc_table); i++) { +#ifdef CONFIG_COMPAT + if (!chan->compat_sc_table) { + /* create syscall table mapping compat syscall to events */ + chan->compat_sc_table = kzalloc(sizeof(struct ltt_event *) + * ARRAY_SIZE(compat_sc_table), GFP_KERNEL); + if (!chan->compat_sc_table) + return -ENOMEM; + } +#endif + if (!chan->sc_unknown) { struct lttng_kernel_event ev; - const struct lttng_event_desc *desc = sc_table[i].desc; + const struct lttng_event_desc *desc = + &__event_desc___sys_unknown; - WARN_ON_ONCE(!desc); - /* - * Skip those already populated by previous failed - * register for this channel. - */ - if (chan->sc_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'; ev.instrumentation = LTTNG_KERNEL_NOOP; - chan->sc_table[i] = ltt_event_create(chan, &ev, filter, - desc); - if (!chan->sc_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. - */ + chan->sc_unknown = ltt_event_create(chan, &ev, filter, + desc); + if (!chan->sc_unknown) { + return -EINVAL; + } + } + + if (!chan->sc_compat_unknown) { + struct lttng_kernel_event ev; + const struct lttng_event_desc *desc = + &__event_desc___compat_sys_unknown; + + memset(&ev, 0, sizeof(ev)); + strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN); + ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0'; + ev.instrumentation = LTTNG_KERNEL_NOOP; + chan->sc_compat_unknown = ltt_event_create(chan, &ev, filter, + desc); + if (!chan->sc_compat_unknown) { return -EINVAL; } } + + if (!chan->sc_exit) { + struct lttng_kernel_event ev; + const struct lttng_event_desc *desc = + &__event_desc___exit_syscall; + + memset(&ev, 0, sizeof(ev)); + strncpy(ev.name, desc->name, LTTNG_SYM_NAME_LEN); + ev.name[LTTNG_SYM_NAME_LEN - 1] = '\0'; + ev.instrumentation = LTTNG_KERNEL_NOOP; + chan->sc_exit = ltt_event_create(chan, &ev, filter, + desc); + if (!chan->sc_exit) { + return -EINVAL; + } + } + + ret = fill_table(sc_table, ARRAY_SIZE(sc_table), + chan->sc_table, chan, filter); + if (ret) + return ret; +#ifdef CONFIG_COMPAT + ret = fill_table(compat_sc_table, ARRAY_SIZE(compat_sc_table), + chan->compat_sc_table, chan, filter); + if (ret) + return ret; +#endif ret = tracepoint_probe_register("sys_enter", (void *) syscall_entry_probe, chan); if (ret) @@ -248,7 +346,8 @@ int lttng_syscalls_register(struct ltt_channel *chan, void *filter) * conflict with sys_exit syscall entry. */ ret = tracepoint_probe_register("sys_exit", - (void *) __event_probe__exit_syscall, chan); + (void *) __event_probe__exit_syscall, + chan->sc_exit); if (ret) { WARN_ON_ONCE(tracepoint_probe_unregister("sys_enter", (void *) syscall_entry_probe, chan)); @@ -266,7 +365,8 @@ int lttng_syscalls_unregister(struct ltt_channel *chan) if (!chan->sc_table) return 0; ret = tracepoint_probe_unregister("sys_exit", - (void *) __event_probe__exit_syscall, chan); + (void *) __event_probe__exit_syscall, + chan->sc_exit); if (ret) return ret; ret = tracepoint_probe_unregister("sys_enter", @@ -275,5 +375,8 @@ int lttng_syscalls_unregister(struct ltt_channel *chan) return ret; /* ltt_event destroy will be performed by ltt_session_destroy() */ kfree(chan->sc_table); +#ifdef CONFIG_COMPAT + kfree(chan->compat_sc_table); +#endif return 0; }