diff options
author | Mathieu Desnoyers <mathieu.desnoyers@efficios.com> | 2011-11-28 07:42:24 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-11-29 10:05:07 +0900 |
commit | 1e8ab70d74ed14bc287e2cb98145e860e2d95f6e (patch) | |
tree | 1af152009868379b490ff1f7b4eef17f5d2011eb /drivers | |
parent | 97104e24fbefa7081e4c9aa9bff3c4fa1a0212cf (diff) |
lttng: probe callbacks
Implement the LTTng probe callbacks. One notable file here is
lttng-events.h, which is the core implementation of the LTTng
TRACE_EVENT macros for generation of probes and tracepoint decription
from the TRACE_EVENT declarations.
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/staging/lttng/probes/Makefile | 37 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/define_trace.h | 132 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-events-reset.h | 84 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-events.h | 703 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-ftrace.c | 188 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-kprobes.c | 164 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-kretprobes.c | 277 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-probe-block.c | 31 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-probe-irq.c | 31 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-probe-kvm.c | 31 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-probe-lttng.c | 24 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-probe-sched.c | 30 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-type-list.h | 21 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-types.c | 49 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng-types.h | 72 | ||||
-rw-r--r-- | drivers/staging/lttng/probes/lttng.h | 15 |
16 files changed, 1889 insertions, 0 deletions
diff --git a/drivers/staging/lttng/probes/Makefile b/drivers/staging/lttng/probes/Makefile new file mode 100644 index 000000000000..bdc1179ec8a8 --- /dev/null +++ b/drivers/staging/lttng/probes/Makefile @@ -0,0 +1,37 @@ +# +# Makefile for the LTT probes. +# + +ccflags-y += -I$(PWD)/probes +obj-m += lttng-types.o + +obj-m += lttng-probe-lttng.o + +obj-m += lttng-probe-sched.o +obj-m += lttng-probe-irq.o + +ifneq ($(CONFIG_KVM),) +obj-m += lttng-probe-kvm.o +endif + +ifneq ($(CONFIG_BLOCK),) +ifneq ($(CONFIG_EVENT_TRACING),) # need blk_cmd_buf_len +obj-m += $(shell \ + if [ $(VERSION) -ge 3 \ + -o \( $(VERSION) -eq 2 -a $(PATCHLEVEL) -ge 6 -a $(SUBLEVEL) -ge 38 \) ] ; then \ + echo "lttng-probe-block.o" ; fi;) +endif +endif + +ifneq ($(CONFIG_KPROBES),) +obj-m += lttng-kprobes.o +endif + + +ifneq ($(CONFIG_KRETPROBES),) +obj-m += lttng-kretprobes.o +endif + +ifneq ($(CONFIG_DYNAMIC_FTRACE),) +obj-m += lttng-ftrace.o +endif diff --git a/drivers/staging/lttng/probes/define_trace.h b/drivers/staging/lttng/probes/define_trace.h new file mode 100644 index 000000000000..3c9a46784abc --- /dev/null +++ b/drivers/staging/lttng/probes/define_trace.h @@ -0,0 +1,132 @@ +/* + * define_trace.h + * + * Copyright (C) 2009 Steven Rostedt <rostedt@goodmis.org> + * Copyright (C) 2010-2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * Dual LGPL v2.1/GPL v2 license. + */ + +/* + * Trace files that want to automate creationg of all tracepoints defined + * in their file should include this file. The following are macros that the + * trace file may define: + * + * TRACE_SYSTEM defines the system the tracepoint is for + * + * TRACE_INCLUDE_FILE if the file name is something other than TRACE_SYSTEM.h + * This macro may be defined to tell define_trace.h what file to include. + * Note, leave off the ".h". + * + * TRACE_INCLUDE_PATH if the path is something other than core kernel include/trace + * then this macro can define the path to use. Note, the path is relative to + * define_trace.h, not the file including it. Full path names for out of tree + * modules must be used. + */ + +#ifdef CREATE_TRACE_POINTS + +/* Prevent recursion */ +#undef CREATE_TRACE_POINTS + +#include <linux/stringify.h> +/* + * module.h includes tracepoints, and because ftrace.h + * pulls in module.h: + * trace/ftrace.h -> linux/ftrace_event.h -> linux/perf_event.h -> + * linux/ftrace.h -> linux/module.h + * we must include module.h here before we play with any of + * the TRACE_EVENT() macros, otherwise the tracepoints included + * by module.h may break the build. + */ +#include <linux/module.h> + +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, args, tstruct, assign, print) \ + DEFINE_TRACE(name) + +#undef TRACE_EVENT_CONDITION +#define TRACE_EVENT_CONDITION(name, proto, args, cond, tstruct, assign, print) \ + TRACE_EVENT(name, \ + PARAMS(proto), \ + PARAMS(args), \ + PARAMS(tstruct), \ + PARAMS(assign), \ + PARAMS(print)) + +#undef TRACE_EVENT_FN +#define TRACE_EVENT_FN(name, proto, args, tstruct, \ + assign, print, reg, unreg) \ + DEFINE_TRACE_FN(name, reg, unreg) + +#undef DEFINE_EVENT +#define DEFINE_EVENT(template, name, proto, args) \ + DEFINE_TRACE(name) + +#undef DEFINE_EVENT_PRINT +#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \ + DEFINE_TRACE(name) + +#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) + +#undef TRACE_INCLUDE +#undef __TRACE_INCLUDE + +#ifndef TRACE_INCLUDE_FILE +# define TRACE_INCLUDE_FILE TRACE_SYSTEM +# define UNDEF_TRACE_INCLUDE_FILE +#endif + +#ifndef TRACE_INCLUDE_PATH +# define __TRACE_INCLUDE(system) <trace/events/system.h> +# define UNDEF_TRACE_INCLUDE_PATH +#else +# define __TRACE_INCLUDE(system) __stringify(TRACE_INCLUDE_PATH/system.h) +#endif + +# define TRACE_INCLUDE(system) __TRACE_INCLUDE(system) + +/* Let the trace headers be reread */ +#define TRACE_HEADER_MULTI_READ + +#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) + +/* Make all open coded DECLARE_TRACE nops */ +#undef DECLARE_TRACE +#define DECLARE_TRACE(name, proto, args) + +#ifdef LTTNG_PACKAGE_BUILD +#include "lttng-events.h" +#endif + +#undef TRACE_EVENT +#undef TRACE_EVENT_FN +#undef TRACE_EVENT_CONDITION +#undef DECLARE_EVENT_CLASS +#undef DEFINE_EVENT +#undef DEFINE_EVENT_PRINT +#undef DEFINE_EVENT_CONDITION +#undef TRACE_HEADER_MULTI_READ +#undef DECLARE_TRACE + +/* Only undef what we defined in this file */ +#ifdef UNDEF_TRACE_INCLUDE_FILE +# undef TRACE_INCLUDE_FILE +# undef UNDEF_TRACE_INCLUDE_FILE +#endif + +#ifdef UNDEF_TRACE_INCLUDE_PATH +# undef TRACE_INCLUDE_PATH +# undef UNDEF_TRACE_INCLUDE_PATH +#endif + +/* We may be processing more files */ +#define CREATE_TRACE_POINTS + +#endif /* CREATE_TRACE_POINTS */ diff --git a/drivers/staging/lttng/probes/lttng-events-reset.h b/drivers/staging/lttng/probes/lttng-events-reset.h new file mode 100644 index 000000000000..c8a1046d90b9 --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-events-reset.h @@ -0,0 +1,84 @@ +/* + * lttng-events-reset.h + * + * Copyright (C) 2010-2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * Dual LGPL v2.1/GPL v2 license. + */ + +/* Reset macros used within TRACE_EVENT to "nothing" */ + +#undef __field_full +#define __field_full(_type, _item, _order, _base) + +#undef __array_enc_ext +#define __array_enc_ext(_type, _item, _length, _order, _base, _encoding) + +#undef __dynamic_array_enc_ext +#define __dynamic_array_enc_ext(_type, _item, _length, _order, _base, _encoding) + +#undef __dynamic_array_len +#define __dynamic_array_len(_type, _item, _length) + +#undef __string +#define __string(_item, _src) + +#undef tp_assign +#define tp_assign(dest, src) + +#undef tp_memcpy +#define tp_memcpy(dest, src, len) + +#undef tp_memcpy_dyn +#define tp_memcpy_dyn(dest, src, len) + +#undef tp_strcpy +#define tp_strcpy(dest, src) + +#undef __get_str +#define __get_str(field) + +#undef __get_dynamic_array +#define __get_dynamic_array(field) + +#undef __get_dynamic_array_len +#define __get_dynamic_array_len(field) + +#undef TP_PROTO +#define TP_PROTO(args...) + +#undef TP_ARGS +#define TP_ARGS(args...) + +#undef TP_STRUCT__entry +#define TP_STRUCT__entry(args...) + +#undef TP_fast_assign +#define TP_fast_assign(args...) + +#undef __perf_count +#define __perf_count(args...) + +#undef __perf_addr +#define __perf_addr(args...) + +#undef TP_perf_assign +#define TP_perf_assign(args...) + +#undef TP_printk +#define TP_printk(args...) + +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(_name, _proto, _args, _tstruct, _assign, _print) + +#undef DECLARE_EVENT_CLASS_NOARGS +#define DECLARE_EVENT_CLASS_NOARGS(_name, _tstruct, _assign, _print) + +#undef DEFINE_EVENT +#define DEFINE_EVENT(_template, _name, _proto, _args) + +#undef DEFINE_EVENT_NOARGS +#define DEFINE_EVENT_NOARGS(_template, _name) + +#undef TRACE_EVENT_FLAGS +#define TRACE_EVENT_FLAGS(name, value) diff --git a/drivers/staging/lttng/probes/lttng-events.h b/drivers/staging/lttng/probes/lttng-events.h new file mode 100644 index 000000000000..ff6273fb3541 --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-events.h @@ -0,0 +1,703 @@ +/* + * lttng-events.h + * + * Copyright (C) 2009 Steven Rostedt <rostedt@goodmis.org> + * Copyright (C) 2010-2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include <linux/debugfs.h> +#include "lttng.h" +#include "lttng-types.h" +#include "../wrapper/vmalloc.h" /* for wrapper_vmalloc_sync_all() */ +#include "../wrapper/ringbuffer/frontend_types.h" +#include "../ltt-events.h" +#include "../ltt-tracer-core.h" + +/* + * Macro declarations used for all stages. + */ + +/* + * DECLARE_EVENT_CLASS can be used to add a generic function + * handlers for events. That is, if all events have the same + * parameters and just have distinct trace points. + * Each tracepoint can be defined with DEFINE_EVENT and that + * will map the DECLARE_EVENT_CLASS to the tracepoint. + * + * TRACE_EVENT is a one to one mapping between tracepoint and template. + */ + +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, args, tstruct, assign, print) \ + DECLARE_EVENT_CLASS(name, \ + PARAMS(proto), \ + PARAMS(args), \ + PARAMS(tstruct), \ + PARAMS(assign), \ + PARAMS(print)) \ + DEFINE_EVENT(name, name, PARAMS(proto), PARAMS(args)) + +#undef TRACE_EVENT_NOARGS +#define TRACE_EVENT_NOARGS(name, tstruct, assign, print) \ + DECLARE_EVENT_CLASS_NOARGS(name, \ + PARAMS(tstruct), \ + PARAMS(assign), \ + PARAMS(print)) \ + DEFINE_EVENT_NOARGS(name, name) + + +#undef DEFINE_EVENT_PRINT +#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \ + DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args)) + +/* Callbacks are meaningless to LTTng. */ +#undef TRACE_EVENT_FN +#define TRACE_EVENT_FN(name, proto, args, tstruct, \ + assign, print, reg, unreg) \ + TRACE_EVENT(name, PARAMS(proto), PARAMS(args), \ + PARAMS(tstruct), PARAMS(assign), PARAMS(print)) \ + +/* + * Stage 1 of the trace events. + * + * Create dummy trace calls for each events, verifying that the LTTng module + * TRACE_EVENT headers match the kernel arguments. Will be optimized out by the + * compiler. + */ + +#include "lttng-events-reset.h" /* Reset all macros within TRACE_EVENT */ + +#undef TP_PROTO +#define TP_PROTO(args...) args + +#undef TP_ARGS +#define TP_ARGS(args...) args + +#undef DEFINE_EVENT +#define DEFINE_EVENT(_template, _name, _proto, _args) \ +void trace_##_name(_proto); + +#undef DEFINE_EVENT_NOARGS +#define DEFINE_EVENT_NOARGS(_template, _name) \ +void trace_##_name(void *__data); + +#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) + +/* + * Stage 2 of the trace events. + * + * Create event field type metadata section. + * Each event produce an array of fields. + */ + +#include "lttng-events-reset.h" /* Reset all macros within TRACE_EVENT */ + +/* Named field types must be defined in lttng-types.h */ + +#undef __field_full +#define __field_full(_type, _item, _order, _base) \ + { \ + .name = #_item, \ + .type = __type_integer(_type, _order, _base, none), \ + }, + +#undef __field +#define __field(_type, _item) \ + __field_full(_type, _item, __BYTE_ORDER, 10) + +#undef __field_ext +#define __field_ext(_type, _item, _filter_type) \ + __field(_type, _item) + +#undef __field_hex +#define __field_hex(_type, _item) \ + __field_full(_type, _item, __BYTE_ORDER, 16) + +#undef __field_network +#define __field_network(_type, _item) \ + __field_full(_type, _item, __BIG_ENDIAN, 10) + +#undef __field_network_hex +#define __field_network_hex(_type, _item) \ + __field_full(_type, _item, __BIG_ENDIAN, 16) + +#undef __array_enc_ext +#define __array_enc_ext(_type, _item, _length, _order, _base, _encoding)\ + { \ + .name = #_item, \ + .type = \ + { \ + .atype = atype_array, \ + .u.array = \ + { \ + .length = _length, \ + .elem_type = __type_integer(_type, _order, _base, _encoding), \ + }, \ + }, \ + }, + +#undef __array +#define __array(_type, _item, _length) \ + __array_enc_ext(_type, _item, _length, __BYTE_ORDER, 10, none) + +#undef __array_text +#define __array_text(_type, _item, _length) \ + __array_enc_ext(_type, _item, _length, __BYTE_ORDER, 10, UTF8) + +#undef __array_hex +#define __array_hex(_type, _item, _length) \ + __array_enc_ext(_type, _item, _length, __BYTE_ORDER, 16, none) + +#undef __dynamic_array_enc_ext +#define __dynamic_array_enc_ext(_type, _item, _length, _order, _base, _encoding) \ + { \ + .name = #_item, \ + .type = \ + { \ + .atype = atype_sequence, \ + .u.sequence = \ + { \ + .length_type = __type_integer(u32, __BYTE_ORDER, 10, none), \ + .elem_type = __type_integer(_type, _order, _base, _encoding), \ + }, \ + }, \ + }, + +#undef __dynamic_array +#define __dynamic_array(_type, _item, _length) \ + __dynamic_array_enc_ext(_type, _item, _length, __BYTE_ORDER, 10, none) + +#undef __dynamic_array_text +#define __dynamic_array_text(_type, _item, _length) \ + __dynamic_array_enc_ext(_type, _item, _length, __BYTE_ORDER, 10, UTF8) + +#undef __dynamic_array_hex +#define __dynamic_array_hex(_type, _item, _length) \ + __dynamic_array_enc_ext(_type, _item, _length, __BYTE_ORDER, 16, none) + +#undef __string +#define __string(_item, _src) \ + { \ + .name = #_item, \ + .type = \ + { \ + .atype = atype_string, \ + .u.basic.string.encoding = lttng_encode_UTF8, \ + }, \ + }, + +#undef __string_from_user +#define __string_from_user(_item, _src) \ + __string(_item, _src) + +#undef TP_STRUCT__entry +#define TP_STRUCT__entry(args...) args /* Only one used in this phase */ + +#undef DECLARE_EVENT_CLASS_NOARGS +#define DECLARE_EVENT_CLASS_NOARGS(_name, _tstruct, _assign, _print) \ + static const struct lttng_event_field __event_fields___##_name[] = { \ + _tstruct \ + }; + +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(_name, _proto, _args, _tstruct, _assign, _print) \ + DECLARE_EVENT_CLASS_NOARGS(_name, PARAMS(_tstruct), PARAMS(_assign), \ + PARAMS(_print)) + +#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) + +/* + * Stage 3 of the trace events. + * + * Create probe callback prototypes. + */ + +#include "lttng-events-reset.h" /* Reset all macros within TRACE_EVENT */ + +#undef TP_PROTO +#define TP_PROTO(args...) args + +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(_name, _proto, _args, _tstruct, _assign, _print) \ +static void __event_probe__##_name(void *__data, _proto); + +#undef DECLARE_EVENT_CLASS_NOARGS +#define DECLARE_EVENT_CLASS_NOARGS(_name, _tstruct, _assign, _print) \ +static void __event_probe__##_name(void *__data); + +#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) + +/* + * Stage 3.9 of the trace events. + * + * Create event descriptions. + */ + +/* Named field types must be defined in lttng-types.h */ + +#include "lttng-events-reset.h" /* Reset all macros within TRACE_EVENT */ + +#ifndef TP_PROBE_CB +#define TP_PROBE_CB(_template) &__event_probe__##_template +#endif + +#undef DEFINE_EVENT_NOARGS +#define DEFINE_EVENT_NOARGS(_template, _name) \ +static const struct lttng_event_desc __event_desc___##_name = { \ + .fields = __event_fields___##_template, \ + .name = #_name, \ + .probe_callback = (void *) TP_PROBE_CB(_template), \ + .nr_fields = ARRAY_SIZE(__event_fields___##_template), \ + .owner = THIS_MODULE, \ +}; + +#undef DEFINE_EVENT +#define DEFINE_EVENT(_template, _name, _proto, _args) \ + DEFINE_EVENT_NOARGS(_template, _name) + +#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) + + +/* + * Stage 4 of the trace events. + * + * Create an array of event description pointers. + */ + +/* Named field types must be defined in lttng-types.h */ + +#include "lttng-events-reset.h" /* Reset all macros within TRACE_EVENT */ + +#undef DEFINE_EVENT_NOARGS +#define DEFINE_EVENT_NOARGS(_template, _name) \ + &__event_desc___##_name, + +#undef DEFINE_EVENT +#define DEFINE_EVENT(_template, _name, _proto, _args) \ + DEFINE_EVENT_NOARGS(_template, _name) + +#define TP_ID1(_token, _system) _token##_system +#define TP_ID(_token, _system) TP_ID1(_token, _system) + +static const struct lttng_event_desc *TP_ID(__event_desc___, TRACE_SYSTEM)[] = { +#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) +}; + +#undef TP_ID1 +#undef TP_ID + + +/* + * Stage 5 of the trace events. + * + * Create a toplevel descriptor for the whole probe. + */ + +#define TP_ID1(_token, _system) _token##_system +#define TP_ID(_token, _system) TP_ID1(_token, _system) + +/* non-const because list head will be modified when registered. */ +static __used struct lttng_probe_desc TP_ID(__probe_desc___, TRACE_SYSTEM) = { + .event_desc = TP_ID(__event_desc___, TRACE_SYSTEM), + .nr_events = ARRAY_SIZE(TP_ID(__event_desc___, TRACE_SYSTEM)), +}; + +#undef TP_ID1 +#undef TP_ID + +/* + * Stage 6 of the trace events. + * + * Create static inline function that calculates event size. + */ + +#include "lttng-events-reset.h" /* Reset all macros within TRACE_EVENT */ + +/* Named field types must be defined in lttng-types.h */ + +#undef __field_full +#define __field_full(_type, _item, _order, _base) \ + __event_len += lib_ring_buffer_align(__event_len, ltt_alignof(_type)); \ + __event_len += sizeof(_type); + +#undef __array_enc_ext +#define __array_enc_ext(_type, _item, _length, _order, _base, _encoding) \ + __event_len += lib_ring_buffer_align(__event_len, ltt_alignof(_type)); \ + __event_len += sizeof(_type) * (_length); + +#undef __dynamic_array_enc_ext +#define __dynamic_array_enc_ext(_type, _item, _length, _order, _base, _encoding)\ + __event_len += lib_ring_buffer_align(__event_len, ltt_alignof(u32)); \ + __event_len += sizeof(u32); \ + __event_len += lib_ring_buffer_align(__event_len, ltt_alignof(_type)); \ + __dynamic_len[__dynamic_len_idx] = (_length); \ + __event_len += sizeof(_type) * __dynamic_len[__dynamic_len_idx]; \ + __dynamic_len_idx++; + +#undef __string +#define __string(_item, _src) \ + __event_len += __dynamic_len[__dynamic_len_idx++] = strlen(_src) + 1; + +/* + * strlen_user includes \0. If returns 0, it faulted, so we set size to + * 1 (\0 only). + */ +#undef __string_from_user +#define __string_from_user(_item, _src) \ + __event_len += __dynamic_len[__dynamic_len_idx++] = \ + min_t(size_t, strlen_user(_src), 1); + +#undef TP_PROTO +#define TP_PROTO(args...) args + +#undef TP_STRUCT__entry +#define TP_STRUCT__entry(args...) args + +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(_name, _proto, _args, _tstruct, _assign, _print) \ +static inline size_t __event_get_size__##_name(size_t *__dynamic_len, _proto) \ +{ \ + size_t __event_len = 0; \ + unsigned int __dynamic_len_idx = 0; \ + \ + if (0) \ + (void) __dynamic_len_idx; /* don't warn if unused */ \ + _tstruct \ + return __event_len; \ +} + +#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) + +/* + * Stage 7 of the trace events. + * + * Create static inline function that calculates event payload alignment. + */ + +#include "lttng-events-reset.h" /* Reset all macros within TRACE_EVENT */ + +/* Named field types must be defined in lttng-types.h */ + +#undef __field_full +#define __field_full(_type, _item, _order, _base) \ + __event_align = max_t(size_t, __event_align, ltt_alignof(_type)); + +#undef __array_enc_ext +#define __array_enc_ext(_type, _item, _length, _order, _base, _encoding) \ + __event_align = max_t(size_t, __event_align, ltt_alignof(_type)); + +#undef __dynamic_array_enc_ext +#define __dynamic_array_enc_ext(_type, _item, _length, _order, _base, _encoding)\ + __event_align = max_t(size_t, __event_align, ltt_alignof(u32)); \ + __event_align = max_t(size_t, __event_align, ltt_alignof(_type)); + +#undef __string +#define __string(_item, _src) + +#undef __string_from_user +#define __string_from_user(_item, _src) + +#undef TP_PROTO +#define TP_PROTO(args...) args + +#undef TP_STRUCT__entry +#define TP_STRUCT__entry(args...) args + +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(_name, _proto, _args, _tstruct, _assign, _print) \ +static inline size_t __event_get_align__##_name(_proto) \ +{ \ + size_t __event_align = 1; \ + _tstruct \ + return __event_align; \ +} + +#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) + + +/* + * Stage 8 of the trace events. + * + * Create structure declaration that allows the "assign" macros to access the + * field types. + */ + +#include "lttng-events-reset.h" /* Reset all macros within TRACE_EVENT */ + +/* Named field types must be defined in lttng-types.h */ + +#undef __field_full +#define __field_full(_type, _item, _order, _base) _type _item; + +#undef __array_enc_ext +#define __array_enc_ext(_type, _item, _length, _order, _base, _encoding) \ + _type _item; + +#undef __dynamic_array_enc_ext +#define __dynamic_array_enc_ext(_type, _item, _length, _order, _base, _encoding)\ + _type _item; + +#undef __string +#define __string(_item, _src) char _item; + +#undef __string_from_user +#define __string_from_user(_item, _src) \ + __string(_item, _src) + +#undef TP_STRUCT__entry +#define TP_STRUCT__entry(args...) args + +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(_name, _proto, _args, _tstruct, _assign, _print) \ +struct __event_typemap__##_name { \ + _tstruct \ +}; + +#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) + + +/* + * Stage 9 of the trace events. + * + * Create the probe function : call even size calculation and write event data + * into the buffer. + * + * We use both the field and assignment macros to write the fields in the order + * defined in the field declaration. The field declarations control the + * execution order, jumping to the appropriate assignment block. + */ + +#include "lttng-events-reset.h" /* Reset all macros within TRACE_EVENT */ + +#undef __field_full +#define __field_full(_type, _item, _order, _base) \ + goto __assign_##_item; \ +__end_field_##_item: + +#undef __array_enc_ext +#define __array_enc_ext(_type, _item, _length, _order, _base, _encoding)\ + goto __assign_##_item; \ +__end_field_##_item: + +#undef __dynamic_array_enc_ext +#define __dynamic_array_enc_ext(_type, _item, _length, _order, _base, _encoding)\ + goto __assign_##_item##_1; \ +__end_field_##_item##_1: \ + goto __assign_##_item##_2; \ +__end_field_##_item##_2: + +#undef __string +#define __string(_item, _src) \ + goto __assign_##_item; \ +__end_field_##_item: + +#undef __string_from_user +#define __string_from_user(_item, _src) \ + __string(_item, _src) + +/* + * Macros mapping tp_assign() to "=", tp_memcpy() to memcpy() and tp_strcpy() to + * strcpy(). + */ +#undef tp_assign +#define tp_assign(dest, src) \ +__assign_##dest: \ + { \ + __typeof__(__typemap.dest) __tmp = (src); \ + lib_ring_buffer_align_ctx(&__ctx, ltt_alignof(__tmp)); \ + __chan->ops->event_write(&__ctx, &__tmp, sizeof(__tmp));\ + } \ + goto __end_field_##dest; + +#undef tp_memcpy +#define tp_memcpy(dest, src, len) \ +__assign_##dest: \ + if (0) \ + (void) __typemap.dest; \ + lib_ring_buffer_align_ctx(&__ctx, ltt_alignof(__typemap.dest)); \ + __chan->ops->event_write(&__ctx, src, len); \ + goto __end_field_##dest; + +#undef tp_memcpy_dyn +#define tp_memcpy_dyn(dest, src) \ +__assign_##dest##_1: \ + { \ + u32 __tmpl = __dynamic_len[__dynamic_len_idx]; \ + lib_ring_buffer_align_ctx(&__ctx, ltt_alignof(u32)); \ + __chan->ops->event_write(&__ctx, &__tmpl, sizeof(u32)); \ + } \ + goto __end_field_##dest##_1; \ +__assign_##dest##_2: \ + lib_ring_buffer_align_ctx(&__ctx, ltt_alignof(__typemap.dest)); \ + __chan->ops->event_write(&__ctx, src, \ + sizeof(__typemap.dest) * __get_dynamic_array_len(dest));\ + goto __end_field_##dest##_2; + +#undef tp_memcpy_from_user +#define tp_memcpy_from_user(dest, src, len) \ + __assign_##dest: \ + if (0) \ + (void) __typemap.dest; \ + lib_ring_buffer_align_ctx(&__ctx, ltt_alignof(__typemap.dest)); \ + __chan->ops->event_write_from_user(&__ctx, src, len); \ + goto __end_field_##dest; + +/* + * The string length including the final \0. + */ +#undef tp_copy_string_from_user +#define tp_copy_string_from_user(dest, src) \ + __assign_##dest: \ + { \ + size_t __ustrlen; \ + \ + if (0) \ + (void) __typemap.dest; \ + lib_ring_buffer_align_ctx(&__ctx, ltt_alignof(__typemap.dest));\ + __ustrlen = __get_dynamic_array_len(dest); \ + if (likely(__ustrlen) > 1) { \ + __chan->ops->event_write_from_user(&__ctx, src, \ + __ustrlen - 1); \ + } \ + __chan->ops->event_memset(&__ctx, 0, 1); \ + } \ + goto __end_field_##dest; +#undef tp_strcpy +#define tp_strcpy(dest, src) \ + tp_memcpy(dest, src, __get_dynamic_array_len(dest)) + +/* Named field types must be defined in lttng-types.h */ + +#undef __get_str +#define __get_str(field) field + +#undef __get_dynamic_array +#define __get_dynamic_array(field) field + +/* Beware: this get len actually consumes the len value */ +#undef __get_dynamic_array_len +#define __get_dynamic_array_len(field) __dynamic_len[__dynamic_len_idx++] + +#undef TP_PROTO +#define TP_PROTO(args...) args + +#undef TP_ARGS +#define TP_ARGS(args...) args + +#undef TP_STRUCT__entry +#define TP_STRUCT__entry(args...) args + +#undef TP_fast_assign +#define TP_fast_assign(args...) args + +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(_name, _proto, _args, _tstruct, _assign, _print) \ +static void __event_probe__##_name(void *__data, _proto) \ +{ \ + struct ltt_event *__event = __data; \ + struct ltt_channel *__chan = __event->chan; \ + struct lib_ring_buffer_ctx __ctx; \ + size_t __event_len, __event_align; \ + size_t __dynamic_len_idx = 0; \ + size_t __dynamic_len[ARRAY_SIZE(__event_fields___##_name)]; \ + struct __event_typemap__##_name __typemap; \ + int __ret; \ + \ + if (0) \ + (void) __dynamic_len_idx; /* don't warn if unused */ \ + if (unlikely(!ACCESS_ONCE(__chan->session->active))) \ + return; \ + if (unlikely(!ACCESS_ONCE(__chan->enabled))) \ + return; \ + if (unlikely(!ACCESS_ONCE(__event->enabled))) \ + return; \ + __event_len = __event_get_size__##_name(__dynamic_len, _args); \ + __event_align = __event_get_align__##_name(_args); \ + lib_ring_buffer_ctx_init(&__ctx, __chan->chan, __event, __event_len, \ + __event_align, -1); \ + __ret = __chan->ops->event_reserve(&__ctx, __event->id); \ + if (__ret < 0) \ + return; \ + /* Control code (field ordering) */ \ + _tstruct \ + __chan->ops->event_commit(&__ctx); \ + return; \ + /* Copy code, steered by control code */ \ + _assign \ +} + +#undef DECLARE_EVENT_CLASS_NOARGS +#define DECLARE_EVENT_CLASS_NOARGS(_name, _tstruct, _assign, _print) \ +static void __event_probe__##_name(void *__data) \ +{ \ + struct ltt_event *__event = __data; \ + struct ltt_channel *__chan = __event->chan; \ + struct lib_ring_buffer_ctx __ctx; \ + size_t __event_len, __event_align; \ + int __ret; \ + \ + if (unlikely(!ACCESS_ONCE(__chan->session->active))) \ + return; \ + if (unlikely(!ACCESS_ONCE(__chan->enabled))) \ + return; \ + if (unlikely(!ACCESS_ONCE(__event->enabled))) \ + return; \ + __event_len = 0; \ + __event_align = 1; \ + lib_ring_buffer_ctx_init(&__ctx, __chan->chan, __event, __event_len, \ + __event_align, -1); \ + __ret = __chan->ops->event_reserve(&__ctx, __event->id); \ + if (__ret < 0) \ + return; \ + /* Control code (field ordering) */ \ + _tstruct \ + __chan->ops->event_commit(&__ctx); \ + return; \ + /* Copy code, steered by control code */ \ + _assign \ +} + +#include TRACE_INCLUDE(TRACE_INCLUDE_FILE) + +/* + * Stage 10 of the trace events. + * + * Register/unregister probes at module load/unload. + */ + +#include "lttng-events-reset.h" /* Reset all macros within TRACE_EVENT */ + +#define TP_ID1(_token, _system) _token##_system +#define TP_ID(_token, _system) TP_ID1(_token, _system) +#define module_init_eval1(_token, _system) module_init(_token##_system) +#define module_init_eval(_token, _system) module_init_eval1(_token, _system) +#define module_exit_eval1(_token, _system) module_exit(_token##_system) +#define module_exit_eval(_token, _system) module_exit_eval1(_token, _system) + +#ifndef TP_MODULE_OVERRIDE +static int TP_ID(__lttng_events_init__, TRACE_SYSTEM)(void) +{ + wrapper_vmalloc_sync_all(); + return ltt_probe_register(&TP_ID(__probe_desc___, TRACE_SYSTEM)); +} + +module_init_eval(__lttng_events_init__, TRACE_SYSTEM); + +static void TP_ID(__lttng_events_exit__, TRACE_SYSTEM)(void) +{ + ltt_probe_unregister(&TP_ID(__probe_desc___, TRACE_SYSTEM)); +} + +module_exit_eval(__lttng_events_exit__, TRACE_SYSTEM); +#endif + +#undef module_init_eval +#undef module_exit_eval +#undef TP_ID1 +#undef TP_ID + +#undef TP_PROTO +#undef TP_ARGS +#undef TRACE_EVENT_FLAGS diff --git a/drivers/staging/lttng/probes/lttng-ftrace.c b/drivers/staging/lttng/probes/lttng-ftrace.c new file mode 100644 index 000000000000..1aa71831e86f --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-ftrace.c @@ -0,0 +1,188 @@ +/* + * (C) Copyright 2009-2011 - + * Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * LTTng function tracer integration module. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +/* + * Ftrace function tracer does not seem to provide synchronization between probe + * teardown and callback execution. Therefore, we make this module permanently + * loaded (unloadable). + * + * TODO: Move to register_ftrace_function() (which is exported for + * modules) for Linux >= 3.0. It is faster (only enables the selected + * functions), and will stay there. + */ + +#include <linux/module.h> +#include <linux/ftrace.h> +#include <linux/slab.h> +#include "../ltt-events.h" +#include "../wrapper/ringbuffer/frontend_types.h" +#include "../wrapper/ftrace.h" +#include "../wrapper/vmalloc.h" +#include "../ltt-tracer.h" + +static +void lttng_ftrace_handler(unsigned long ip, unsigned long parent_ip, void **data) +{ + struct ltt_event *event = *data; + struct ltt_channel *chan = event->chan; + struct lib_ring_buffer_ctx ctx; + struct { + unsigned long ip; + unsigned long parent_ip; + } payload; + int ret; + + if (unlikely(!ACCESS_ONCE(chan->session->active))) + return; + if (unlikely(!ACCESS_ONCE(chan->enabled))) + return; + if (unlikely(!ACCESS_ONCE(event->enabled))) + return; + + lib_ring_buffer_ctx_init(&ctx, chan->chan, event, + sizeof(payload), ltt_alignof(payload), -1); + ret = chan->ops->event_reserve(&ctx, event->id); + if (ret < 0) + return; + payload.ip = ip; + payload.parent_ip = parent_ip; + lib_ring_buffer_align_ctx(&ctx, ltt_alignof(payload)); + chan->ops->event_write(&ctx, &payload, sizeof(payload)); + chan->ops->event_commit(&ctx); + return; +} + +/* + * Create event description + */ +static +int lttng_create_ftrace_event(const char *name, struct ltt_event *event) +{ + struct lttng_event_field *fields; + struct lttng_event_desc *desc; + int ret; + + desc = kzalloc(sizeof(*event->desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + desc->name = kstrdup(name, GFP_KERNEL); + if (!desc->name) { + ret = -ENOMEM; + goto error_str; + } + desc->nr_fields = 2; + desc->fields = fields = + kzalloc(2 * sizeof(struct lttng_event_field), GFP_KERNEL); + if (!desc->fields) { + ret = -ENOMEM; + goto error_fields; + } + fields[0].name = "ip"; + fields[0].type.atype = atype_integer; + fields[0].type.u.basic.integer.size = sizeof(unsigned long) * CHAR_BIT; + fields[0].type.u.basic.integer.alignment = ltt_alignof(unsigned long) * CHAR_BIT; + fields[0].type.u.basic.integer.signedness = is_signed_type(unsigned long); + fields[0].type.u.basic.integer.reverse_byte_order = 0; + fields[0].type.u.basic.integer.base = 16; + fields[0].type.u.basic.integer.encoding = lttng_encode_none; + + fields[1].name = "parent_ip"; + fields[1].type.atype = atype_integer; + fields[1].type.u.basic.integer.size = sizeof(unsigned long) * CHAR_BIT; + fields[1].type.u.basic.integer.alignment = ltt_alignof(unsigned long) * CHAR_BIT; + fields[1].type.u.basic.integer.signedness = is_signed_type(unsigned long); + fields[1].type.u.basic.integer.reverse_byte_order = 0; + fields[1].type.u.basic.integer.base = 16; + fields[1].type.u.basic.integer.encoding = lttng_encode_none; + + desc->owner = THIS_MODULE; + event->desc = desc; + + return 0; + +error_fields: + kfree(desc->name); +error_str: + kfree(desc); + return ret; +} + +static +struct ftrace_probe_ops lttng_ftrace_ops = { + .func = lttng_ftrace_handler, +}; + +int lttng_ftrace_register(const char *name, + const char *symbol_name, + struct ltt_event *event) +{ + int ret; + + ret = lttng_create_ftrace_event(name, event); + if (ret) + goto error; + + event->u.ftrace.symbol_name = kstrdup(symbol_name, GFP_KERNEL); + if (!event->u.ftrace.symbol_name) + goto name_error; + + /* Ensure the memory we just allocated don't trigger page faults */ + wrapper_vmalloc_sync_all(); + + ret = wrapper_register_ftrace_function_probe(event->u.ftrace.symbol_name, + <tng_ftrace_ops, event); + if (ret < 0) + goto register_error; + return 0; + +register_error: + kfree(event->u.ftrace.symbol_name); +name_error: + kfree(event->desc->name); + kfree(event->desc); +error: + return ret; +} +EXPORT_SYMBOL_GPL(lttng_ftrace_register); + +void lttng_ftrace_unregister(struct ltt_event *event) +{ + wrapper_unregister_ftrace_function_probe(event->u.ftrace.symbol_name, + <tng_ftrace_ops, event); +} +EXPORT_SYMBOL_GPL(lttng_ftrace_unregister); + +void lttng_ftrace_destroy_private(struct ltt_event *event) +{ + kfree(event->u.ftrace.symbol_name); + kfree(event->desc->fields); + kfree(event->desc->name); + kfree(event->desc); +} +EXPORT_SYMBOL_GPL(lttng_ftrace_destroy_private); + +int lttng_ftrace_init(void) +{ + wrapper_vmalloc_sync_all(); + return 0; +} +module_init(lttng_ftrace_init) + +/* + * Ftrace takes care of waiting for a grace period (RCU sched) at probe + * unregistration, and disables preemption around probe call. + */ +void lttng_ftrace_exit(void) +{ +} +module_exit(lttng_ftrace_exit) + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers"); +MODULE_DESCRIPTION("Linux Trace Toolkit Ftrace Support"); diff --git a/drivers/staging/lttng/probes/lttng-kprobes.c b/drivers/staging/lttng/probes/lttng-kprobes.c new file mode 100644 index 000000000000..784002aed096 --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-kprobes.c @@ -0,0 +1,164 @@ +/* + * (C) Copyright 2009-2011 - + * Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * LTTng kprobes integration module. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include <linux/module.h> +#include <linux/kprobes.h> +#include <linux/slab.h> +#include "../ltt-events.h" +#include "../wrapper/ringbuffer/frontend_types.h" +#include "../wrapper/vmalloc.h" +#include "../ltt-tracer.h" + +static +int lttng_kprobes_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct ltt_event *event = + container_of(p, struct ltt_event, u.kprobe.kp); + struct ltt_channel *chan = event->chan; + struct lib_ring_buffer_ctx ctx; + int ret; + unsigned long data = (unsigned long) p->addr; + + if (unlikely(!ACCESS_ONCE(chan->session->active))) + return 0; + if (unlikely(!ACCESS_ONCE(chan->enabled))) + return 0; + if (unlikely(!ACCESS_ONCE(event->enabled))) + return 0; + + lib_ring_buffer_ctx_init(&ctx, chan->chan, event, sizeof(data), + ltt_alignof(data), -1); + ret = chan->ops->event_reserve(&ctx, event->id); + if (ret < 0) + return 0; + lib_ring_buffer_align_ctx(&ctx, ltt_alignof(data)); + chan->ops->event_write(&ctx, &data, sizeof(data)); + chan->ops->event_commit(&ctx); + return 0; +} + +/* + * Create event description + */ +static +int lttng_create_kprobe_event(const char *name, struct ltt_event *event) +{ + struct lttng_event_field *field; + struct lttng_event_desc *desc; + int ret; + + desc = kzalloc(sizeof(*event->desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + desc->name = kstrdup(name, GFP_KERNEL); + if (!desc->name) { + ret = -ENOMEM; + goto error_str; + } + desc->nr_fields = 1; + desc->fields = field = + kzalloc(1 * sizeof(struct lttng_event_field), GFP_KERNEL); + if (!field) { + ret = -ENOMEM; + goto error_field; + } + field->name = "ip"; + field->type.atype = atype_integer; + field->type.u.basic.integer.size = sizeof(unsigned long) * CHAR_BIT; + field->type.u.basic.integer.alignment = ltt_alignof(unsigned long) * CHAR_BIT; + field->type.u.basic.integer.signedness = is_signed_type(unsigned long); + field->type.u.basic.integer.reverse_byte_order = 0; + field->type.u.basic.integer.base = 16; + field->type.u.basic.integer.encoding = lttng_encode_none; + desc->owner = THIS_MODULE; + event->desc = desc; + + return 0; + +error_field: + kfree(desc->name); +error_str: + kfree(desc); + return ret; +} + +int lttng_kprobes_register(const char *name, + const char *symbol_name, + uint64_t offset, + uint64_t addr, + struct ltt_event *event) +{ + int ret; + + /* Kprobes expects a NULL symbol name if unused */ + if (symbol_name[0] == '\0') + symbol_name = NULL; + + ret = lttng_create_kprobe_event(name, event); + if (ret) + goto error; + memset(&event->u.kprobe.kp, 0, sizeof(event->u.kprobe.kp)); + event->u.kprobe.kp.pre_handler = lttng_kprobes_handler_pre; + if (symbol_name) { + event->u.kprobe.symbol_name = + kzalloc(LTTNG_SYM_NAME_LEN * sizeof(char), + GFP_KERNEL); + if (!event->u.kprobe.symbol_name) { + ret = -ENOMEM; + goto name_error; + } + memcpy(event->u.kprobe.symbol_name, symbol_name, + LTTNG_SYM_NAME_LEN * sizeof(char)); + event->u.kprobe.kp.symbol_name = + event->u.kprobe.symbol_name; + } + event->u.kprobe.kp.offset = offset; + event->u.kprobe.kp.addr = (void *) (unsigned long) addr; + + /* + * Ensure the memory we just allocated don't trigger page faults. + * Well.. kprobes itself puts the page fault handler on the blacklist, + * but we can never be too careful. + */ + wrapper_vmalloc_sync_all(); + + ret = register_kprobe(&event->u.kprobe.kp); + if (ret) + goto register_error; + return 0; + +register_error: + kfree(event->u.kprobe.symbol_name); +name_error: + kfree(event->desc->fields); + kfree(event->desc->name); + kfree(event->desc); +error: + return ret; +} +EXPORT_SYMBOL_GPL(lttng_kprobes_register); + +void lttng_kprobes_unregister(struct ltt_event *event) +{ + unregister_kprobe(&event->u.kprobe.kp); +} +EXPORT_SYMBOL_GPL(lttng_kprobes_unregister); + +void lttng_kprobes_destroy_private(struct ltt_event *event) +{ + kfree(event->u.kprobe.symbol_name); + kfree(event->desc->fields); + kfree(event->desc->name); + kfree(event->desc); +} +EXPORT_SYMBOL_GPL(lttng_kprobes_destroy_private); + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers"); +MODULE_DESCRIPTION("Linux Trace Toolkit Kprobes Support"); diff --git a/drivers/staging/lttng/probes/lttng-kretprobes.c b/drivers/staging/lttng/probes/lttng-kretprobes.c new file mode 100644 index 000000000000..6b291018f715 --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-kretprobes.c @@ -0,0 +1,277 @@ +/* + * (C) Copyright 2009-2011 - + * Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * LTTng kretprobes integration module. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include <linux/module.h> +#include <linux/kprobes.h> +#include <linux/slab.h> +#include <linux/kref.h> +#include "../ltt-events.h" +#include "../wrapper/ringbuffer/frontend_types.h" +#include "../wrapper/vmalloc.h" +#include "../ltt-tracer.h" + +enum lttng_kretprobe_type { + EVENT_ENTRY = 0, + EVENT_RETURN = 1, +}; + +struct lttng_krp { + struct kretprobe krp; + struct ltt_event *event[2]; /* ENTRY and RETURN */ + struct kref kref_register; + struct kref kref_alloc; +}; + +static +int _lttng_kretprobes_handler(struct kretprobe_instance *krpi, + struct pt_regs *regs, + enum lttng_kretprobe_type type) +{ + struct lttng_krp *lttng_krp = + container_of(krpi->rp, struct lttng_krp, krp); + struct ltt_event *event = + lttng_krp->event[type]; + struct ltt_channel *chan = event->chan; + struct lib_ring_buffer_ctx ctx; + int ret; + struct { + unsigned long ip; + unsigned long parent_ip; + } payload; + + if (unlikely(!ACCESS_ONCE(chan->session->active))) + return 0; + if (unlikely(!ACCESS_ONCE(chan->enabled))) + return 0; + if (unlikely(!ACCESS_ONCE(event->enabled))) + return 0; + + payload.ip = (unsigned long) krpi->rp->kp.addr; + payload.parent_ip = (unsigned long) krpi->ret_addr; + + lib_ring_buffer_ctx_init(&ctx, chan->chan, event, sizeof(payload), + ltt_alignof(payload), -1); + ret = chan->ops->event_reserve(&ctx, event->id); + if (ret < 0) + return 0; + lib_ring_buffer_align_ctx(&ctx, ltt_alignof(payload)); + chan->ops->event_write(&ctx, &payload, sizeof(payload)); + chan->ops->event_commit(&ctx); + return 0; +} + +static +int lttng_kretprobes_handler_entry(struct kretprobe_instance *krpi, + struct pt_regs *regs) +{ + return _lttng_kretprobes_handler(krpi, regs, EVENT_ENTRY); +} + +static +int lttng_kretprobes_handler_return(struct kretprobe_instance *krpi, + struct pt_regs *regs) +{ + return _lttng_kretprobes_handler(krpi, regs, EVENT_RETURN); +} + +/* + * Create event description + */ +static +int lttng_create_kprobe_event(const char *name, struct ltt_event *event, + enum lttng_kretprobe_type type) +{ + struct lttng_event_field *fields; + struct lttng_event_desc *desc; + int ret; + char *alloc_name; + size_t name_len; + const char *suffix = NULL; + + desc = kzalloc(sizeof(*event->desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + name_len = strlen(name); + switch (type) { + case EVENT_ENTRY: + suffix = "_entry"; + break; + case EVENT_RETURN: + suffix = "_return"; + break; + } + name_len += strlen(suffix); + alloc_name = kmalloc(name_len + 1, GFP_KERNEL); + if (!alloc_name) { + ret = -ENOMEM; + goto error_str; + } + strcpy(alloc_name, name); + strcat(alloc_name, suffix); + desc->name = alloc_name; + desc->nr_fields = 2; + desc->fields = fields = + kzalloc(2 * sizeof(struct lttng_event_field), GFP_KERNEL); + if (!desc->fields) { + ret = -ENOMEM; + goto error_fields; + } + fields[0].name = "ip"; + fields[0].type.atype = atype_integer; + fields[0].type.u.basic.integer.size = sizeof(unsigned long) * CHAR_BIT; + fields[0].type.u.basic.integer.alignment = ltt_alignof(unsigned long) * CHAR_BIT; + fields[0].type.u.basic.integer.signedness = is_signed_type(unsigned long); + fields[0].type.u.basic.integer.reverse_byte_order = 0; + fields[0].type.u.basic.integer.base = 16; + fields[0].type.u.basic.integer.encoding = lttng_encode_none; + + fields[1].name = "parent_ip"; + fields[1].type.atype = atype_integer; + fields[1].type.u.basic.integer.size = sizeof(unsigned long) * CHAR_BIT; + fields[1].type.u.basic.integer.alignment = ltt_alignof(unsigned long) * CHAR_BIT; + fields[1].type.u.basic.integer.signedness = is_signed_type(unsigned long); + fields[1].type.u.basic.integer.reverse_byte_order = 0; + fields[1].type.u.basic.integer.base = 16; + fields[1].type.u.basic.integer.encoding = lttng_encode_none; + + desc->owner = THIS_MODULE; + event->desc = desc; + + return 0; + +error_fields: + kfree(desc->name); +error_str: + kfree(desc); + return ret; +} + +int lttng_kretprobes_register(const char *name, + const char *symbol_name, + uint64_t offset, + uint64_t addr, + struct ltt_event *event_entry, + struct ltt_event *event_return) +{ + int ret; + struct lttng_krp *lttng_krp; + + /* Kprobes expects a NULL symbol name if unused */ + if (symbol_name[0] == '\0') + symbol_name = NULL; + + ret = lttng_create_kprobe_event(name, event_entry, EVENT_ENTRY); + if (ret) + goto error; + ret = lttng_create_kprobe_event(name, event_return, EVENT_RETURN); + if (ret) + goto event_return_error; + lttng_krp = kzalloc(sizeof(*lttng_krp), GFP_KERNEL); + if (!lttng_krp) + goto krp_error; + lttng_krp->krp.entry_handler = lttng_kretprobes_handler_entry; + lttng_krp->krp.handler = lttng_kretprobes_handler_return; + if (symbol_name) { + char *alloc_symbol; + + alloc_symbol = kstrdup(symbol_name, GFP_KERNEL); + if (!alloc_symbol) { + ret = -ENOMEM; + goto name_error; + } + lttng_krp->krp.kp.symbol_name = + alloc_symbol; + event_entry->u.kretprobe.symbol_name = + alloc_symbol; + event_return->u.kretprobe.symbol_name = + alloc_symbol; + } + lttng_krp->krp.kp.offset = offset; + lttng_krp->krp.kp.addr = (void *) (unsigned long) addr; + + /* Allow probe handler to find event structures */ + lttng_krp->event[EVENT_ENTRY] = event_entry; + lttng_krp->event[EVENT_RETURN] = event_return; + event_entry->u.kretprobe.lttng_krp = lttng_krp; + event_return->u.kretprobe.lttng_krp = lttng_krp; + + /* + * Both events must be unregistered before the kretprobe is + * unregistered. Same for memory allocation. + */ + kref_init(<tng_krp->kref_alloc); + kref_get(<tng_krp->kref_alloc); /* inc refcount to 2 */ + kref_init(<tng_krp->kref_register); + kref_get(<tng_krp->kref_register); /* inc refcount to 2 */ + + /* + * Ensure the memory we just allocated don't trigger page faults. + * Well.. kprobes itself puts the page fault handler on the blacklist, + * but we can never be too careful. + */ + wrapper_vmalloc_sync_all(); + + ret = register_kretprobe(<tng_krp->krp); + if (ret) + goto register_error; + return 0; + +register_error: + kfree(lttng_krp->krp.kp.symbol_name); +name_error: + kfree(lttng_krp); +krp_error: + kfree(event_return->desc->fields); + kfree(event_return->desc->name); + kfree(event_return->desc); +event_return_error: + kfree(event_entry->desc->fields); + kfree(event_entry->desc->name); + kfree(event_entry->desc); +error: + return ret; +} +EXPORT_SYMBOL_GPL(lttng_kretprobes_register); + +static +void _lttng_kretprobes_unregister_release(struct kref *kref) +{ + struct lttng_krp *lttng_krp = + container_of(kref, struct lttng_krp, kref_register); + unregister_kretprobe(<tng_krp->krp); +} + +void lttng_kretprobes_unregister(struct ltt_event *event) +{ + kref_put(&event->u.kretprobe.lttng_krp->kref_register, + _lttng_kretprobes_unregister_release); +} +EXPORT_SYMBOL_GPL(lttng_kretprobes_unregister); + +static +void _lttng_kretprobes_release(struct kref *kref) +{ + struct lttng_krp *lttng_krp = + container_of(kref, struct lttng_krp, kref_alloc); + kfree(lttng_krp->krp.kp.symbol_name); +} + +void lttng_kretprobes_destroy_private(struct ltt_event *event) +{ + kfree(event->desc->fields); + kfree(event->desc->name); + kfree(event->desc); + kref_put(&event->u.kretprobe.lttng_krp->kref_alloc, + _lttng_kretprobes_release); +} +EXPORT_SYMBOL_GPL(lttng_kretprobes_destroy_private); + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers"); +MODULE_DESCRIPTION("Linux Trace Toolkit Kretprobes Support"); diff --git a/drivers/staging/lttng/probes/lttng-probe-block.c b/drivers/staging/lttng/probes/lttng-probe-block.c new file mode 100644 index 000000000000..9eeebfc3a600 --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-probe-block.c @@ -0,0 +1,31 @@ +/* + * probes/lttng-probe-block.c + * + * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * LTTng block probes. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include <linux/module.h> +#include <linux/blktrace_api.h> + +/* + * Create the tracepoint static inlines from the kernel to validate that our + * trace event macros match the kernel we run on. + */ +#include <trace/events/block.h> + +/* + * Create LTTng tracepoint probes. + */ +#define LTTNG_PACKAGE_BUILD +#define CREATE_TRACE_POINTS +#define TRACE_INCLUDE_PATH ../instrumentation/events/lttng-module + +#include "../instrumentation/events/lttng-module/block.h" + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@efficios.com>"); +MODULE_DESCRIPTION("LTTng block probes"); diff --git a/drivers/staging/lttng/probes/lttng-probe-irq.c b/drivers/staging/lttng/probes/lttng-probe-irq.c new file mode 100644 index 000000000000..4a6a322d1447 --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-probe-irq.c @@ -0,0 +1,31 @@ +/* + * probes/lttng-probe-irq.c + * + * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * LTTng irq probes. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include <linux/module.h> +#include <linux/interrupt.h> + +/* + * Create the tracepoint static inlines from the kernel to validate that our + * trace event macros match the kernel we run on. + */ +#include <trace/events/irq.h> + +/* + * Create LTTng tracepoint probes. + */ +#define LTTNG_PACKAGE_BUILD +#define CREATE_TRACE_POINTS +#define TRACE_INCLUDE_PATH ../instrumentation/events/lttng-module + +#include "../instrumentation/events/lttng-module/irq.h" + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@efficios.com>"); +MODULE_DESCRIPTION("LTTng irq probes"); diff --git a/drivers/staging/lttng/probes/lttng-probe-kvm.c b/drivers/staging/lttng/probes/lttng-probe-kvm.c new file mode 100644 index 000000000000..9efc6dd16880 --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-probe-kvm.c @@ -0,0 +1,31 @@ +/* + * probes/lttng-probe-kvm.c + * + * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * LTTng kvm probes. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include <linux/module.h> +#include <linux/kvm_host.h> + +/* + * Create the tracepoint static inlines from the kernel to validate that our + * trace event macros match the kernel we run on. + */ +#include <trace/events/kvm.h> + +/* + * Create LTTng tracepoint probes. + */ +#define LTTNG_PACKAGE_BUILD +#define CREATE_TRACE_POINTS +#define TRACE_INCLUDE_PATH ../instrumentation/events/lttng-module + +#include "../instrumentation/events/lttng-module/kvm.h" + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@efficios.com>"); +MODULE_DESCRIPTION("LTTng kvm probes"); diff --git a/drivers/staging/lttng/probes/lttng-probe-lttng.c b/drivers/staging/lttng/probes/lttng-probe-lttng.c new file mode 100644 index 000000000000..62aab6c75a76 --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-probe-lttng.c @@ -0,0 +1,24 @@ +/* + * probes/lttng-probe-core.c + * + * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * LTTng core probes. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include <linux/module.h> + +/* + * Create LTTng tracepoint probes. + */ +#define LTTNG_PACKAGE_BUILD +#define CREATE_TRACE_POINTS +#define TRACE_INCLUDE_PATH ../instrumentation/events/lttng-module + +#include "../instrumentation/events/lttng-module/lttng.h" + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@efficios.com>"); +MODULE_DESCRIPTION("LTTng core probes"); diff --git a/drivers/staging/lttng/probes/lttng-probe-sched.c b/drivers/staging/lttng/probes/lttng-probe-sched.c new file mode 100644 index 000000000000..18c1521de1db --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-probe-sched.c @@ -0,0 +1,30 @@ +/* + * probes/lttng-probe-sched.c + * + * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * LTTng sched probes. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include <linux/module.h> + +/* + * Create the tracepoint static inlines from the kernel to validate that our + * trace event macros match the kernel we run on. + */ +#include <trace/events/sched.h> + +/* + * Create LTTng tracepoint probes. + */ +#define LTTNG_PACKAGE_BUILD +#define CREATE_TRACE_POINTS +#define TRACE_INCLUDE_PATH ../instrumentation/events/lttng-module + +#include "../instrumentation/events/lttng-module/sched.h" + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@efficios.com>"); +MODULE_DESCRIPTION("LTTng sched probes"); diff --git a/drivers/staging/lttng/probes/lttng-type-list.h b/drivers/staging/lttng/probes/lttng-type-list.h new file mode 100644 index 000000000000..7b953dbc5efe --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-type-list.h @@ -0,0 +1,21 @@ +/* + * lttng-type-list.h + * + * Copyright (C) 2010-2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * Dual LGPL v2.1/GPL v2 license. + */ + +/* Type list, used to create metadata */ + +/* Enumerations */ +TRACE_EVENT_ENUM(hrtimer_mode, + V(HRTIMER_MODE_ABS), + V(HRTIMER_MODE_REL), + V(HRTIMER_MODE_PINNED), + V(HRTIMER_MODE_ABS_PINNED), + V(HRTIMER_MODE_REL_PINNED), + R(HRTIMER_MODE_UNDEFINED, 0x04, 0x20), /* Example (to remove) */ +) + +TRACE_EVENT_TYPE(hrtimer_mode, enum, unsigned char) diff --git a/drivers/staging/lttng/probes/lttng-types.c b/drivers/staging/lttng/probes/lttng-types.c new file mode 100644 index 000000000000..93a9ae5436fb --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-types.c @@ -0,0 +1,49 @@ +/* + * probes/lttng-types.c + * + * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * LTTng types. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include "../wrapper/vmalloc.h" /* for wrapper_vmalloc_sync_all() */ +#include "../ltt-events.h" +#include "lttng-types.h" +#include <linux/hrtimer.h> + +#define STAGE_EXPORT_ENUMS +#include "lttng-types.h" +#include "lttng-type-list.h" +#undef STAGE_EXPORT_ENUMS + +struct lttng_enum lttng_enums[] = { +#define STAGE_EXPORT_TYPES +#include "lttng-types.h" +#include "lttng-type-list.h" +#undef STAGE_EXPORT_TYPES +}; + +static int lttng_types_init(void) +{ + int ret = 0; + + wrapper_vmalloc_sync_all(); + /* TODO */ + return ret; +} + +module_init(lttng_types_init); + +static void lttng_types_exit(void) +{ +} + +module_exit(lttng_types_exit); + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers <mathieu.desnoyers@efficios.com>"); +MODULE_DESCRIPTION("LTTng types"); diff --git a/drivers/staging/lttng/probes/lttng-types.h b/drivers/staging/lttng/probes/lttng-types.h new file mode 100644 index 000000000000..10620280baf6 --- /dev/null +++ b/drivers/staging/lttng/probes/lttng-types.h @@ -0,0 +1,72 @@ +/* + * Protect against multiple inclusion of structure declarations, but run the + * stages below each time. + */ +#ifndef _LTTNG_PROBES_LTTNG_TYPES_H +#define _LTTNG_PROBES_LTTNG_TYPES_H + +/* + * probes/lttng-types.h + * + * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * LTTng types. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include <linux/seq_file.h> +#include "lttng.h" +#include "../ltt-events.h" +#include "../ltt-tracer.h" +#include "../ltt-endian.h" + +#endif /* _LTTNG_PROBES_LTTNG_TYPES_H */ + +/* Export enumerations */ + +#ifdef STAGE_EXPORT_ENUMS + +#undef TRACE_EVENT_TYPE +#define TRACE_EVENT_TYPE(_name, _abstract_type, args...) + +#undef TRACE_EVENT_ENUM +#define TRACE_EVENT_ENUM(_name, _entries...) \ + const struct lttng_enum_entry __trace_event_enum_##_name[] = { \ + PARAMS(_entries) \ + }; + +/* Enumeration entry (single value) */ +#undef V +#define V(_string) { _string, _string, #_string} + +/* Enumeration entry (range) */ +#undef R +#define R(_string, _range_start, _range_end) \ + { _range_start, _range_end, #_string } + +#endif /* STAGE_EXPORT_ENUMS */ + + +/* Export named types */ + +#ifdef STAGE_EXPORT_TYPES + +#undef TRACE_EVENT_TYPE___enum +#define TRACE_EVENT_TYPE___enum(_name, _container_type) \ + { \ + .name = #_name, \ + .container_type = __type_integer(_container_type, __BYTE_ORDER, 10, none), \ + .entries = __trace_event_enum_##_name, \ + .len = ARRAY_SIZE(__trace_event_enum_##_name), \ + }, + +/* Local declaration */ +#undef TRACE_EVENT_TYPE +#define TRACE_EVENT_TYPE(_name, _abstract_type, args...) \ + TRACE_EVENT_TYPE___##_abstract_type(_name, args) + +#undef TRACE_EVENT_ENUM +#define TRACE_EVENT_ENUM(_name, _entries...) + +#endif /* STAGE_EXPORT_TYPES */ diff --git a/drivers/staging/lttng/probes/lttng.h b/drivers/staging/lttng/probes/lttng.h new file mode 100644 index 000000000000..e16fc2dd8ce5 --- /dev/null +++ b/drivers/staging/lttng/probes/lttng.h @@ -0,0 +1,15 @@ +#ifndef _LTTNG_PROBES_LTTNG_H +#define _LTTNG_PROBES_LTTNG_H + +/* + * lttng.h + * + * Copyright (C) 2010-2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#undef PARAMS +#define PARAMS(args...) args + +#endif /* _LTTNG_PROBES_LTTNG_H */ |