[RFC PATCH v3 1/2] tracing: add TRACE_EVENT_MAP_COND

From: Julien Desfossez
Date: Fri Jan 13 2017 - 12:28:54 EST


This new macro allows to hook conditional tracepoint probes to
pre-existing trace events. This allows to create specialized versions of
the same tracepoint without having to explicitly call every possible
tracepoints in the instrumented code.

In order to use it, a TRACE_EVENT must already exist, after that, we can
connect as many TRACE_EVENT_MAP_COND to this TRACE_EVENT as needed.

Example usage:

TRACE_EVENT(tp_test,
TP_PROTO(proto),
TP_ARGS(args),
TP_STRUCT__entry(), /* can be empty */
TP_fast_assign(), /* can be empty */
TP_printk() /* can be empty */
);

TRACE_EVENT_MAP_COND(tp_test, cond_test,
TP_PROTO(proto),
TP_ARGS(args),
TP_CONDITION(cond),
TP_STRUCT__entry(entry),
TP_fast_assign(assign),
TP_printk(print)
);

Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: Steven Rostedt (Red Hat) <rostedt@xxxxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
Cc: Daniel Bristot de Oliveira <bristot@xxxxxxxxxx>
Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@xxxxxxxxxxxx>
Signed-off-by: Julien Desfossez <jdesfossez@xxxxxxxxxxxx>
---
include/linux/trace_events.h | 14 ++++-
include/linux/tracepoint.h | 6 ++
include/trace/define_trace.h | 6 ++
include/trace/perf.h | 24 ++++++--
include/trace/trace_events.h | 130 +++++++++++++++++++++++++++++++++++++------
kernel/trace/trace_events.c | 15 +++--
6 files changed, 168 insertions(+), 27 deletions(-)

diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index be00761..1f7e0ec 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -217,6 +217,7 @@ enum {
TRACE_EVENT_FL_TRACEPOINT_BIT,
TRACE_EVENT_FL_KPROBE_BIT,
TRACE_EVENT_FL_UPROBE_BIT,
+ TRACE_EVENT_FL_MAP_BIT,
};

/*
@@ -231,6 +232,7 @@ enum {
* TRACEPOINT - Event is a tracepoint
* KPROBE - Event is a kprobe
* UPROBE - Event is a uprobe
+ * MAP - Event maps to a tracepoint as an alias
*/
enum {
TRACE_EVENT_FL_FILTERED = (1 << TRACE_EVENT_FL_FILTERED_BIT),
@@ -241,10 +243,16 @@ enum {
TRACE_EVENT_FL_TRACEPOINT = (1 << TRACE_EVENT_FL_TRACEPOINT_BIT),
TRACE_EVENT_FL_KPROBE = (1 << TRACE_EVENT_FL_KPROBE_BIT),
TRACE_EVENT_FL_UPROBE = (1 << TRACE_EVENT_FL_UPROBE_BIT),
+ TRACE_EVENT_FL_MAP = (1 << TRACE_EVENT_FL_MAP_BIT),
};

#define TRACE_EVENT_FL_UKPROBE (TRACE_EVENT_FL_KPROBE | TRACE_EVENT_FL_UPROBE)

+struct trace_event_map {
+ struct tracepoint *tp;
+ char *name;
+};
+
struct trace_event_call {
struct list_head list;
struct trace_event_class *class;
@@ -252,6 +260,8 @@ struct trace_event_call {
char *name;
/* Set TRACE_EVENT_FL_TRACEPOINT flag when using "tp" */
struct tracepoint *tp;
+ /* Set TRACE_EVENT_FL_MAP flag when using "map" instead */
+ struct trace_event_map *map;
};
struct trace_event event;
char *print_fmt;
@@ -282,7 +292,9 @@ struct trace_event_call {
static inline const char *
trace_event_name(struct trace_event_call *call)
{
- if (call->flags & TRACE_EVENT_FL_TRACEPOINT)
+ if (call->flags & TRACE_EVENT_FL_MAP)
+ return call->map->name;
+ else if (call->flags & TRACE_EVENT_FL_TRACEPOINT)
return call->tp ? call->tp->name : NULL;
else
return call->name;
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index f72fcfe..3e5b5a4 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -276,6 +276,7 @@ static inline void tracepoint_synchronize_unregister(void)

#define DEFINE_TRACE_FN(name, reg, unreg)
#define DEFINE_TRACE(name)
+#define DEFINE_TRACE_MAP_COND(name, map, cond)
#define EXPORT_TRACEPOINT_SYMBOL_GPL(name)
#define EXPORT_TRACEPOINT_SYMBOL(name)

@@ -469,6 +470,8 @@ static inline void tracepoint_synchronize_unregister(void)
*/

#define DECLARE_EVENT_CLASS(name, proto, args, tstruct, assign, print)
+#define DECLARE_EVENT_COND_CLASS(name, proto, args, cond, \
+ tstruct, assign, print)
#define DEFINE_EVENT(template, name, proto, args) \
DECLARE_TRACE(name, PARAMS(proto), PARAMS(args))
#define DEFINE_EVENT_FN(template, name, proto, args, reg, unreg)\
@@ -498,4 +501,7 @@ static inline void tracepoint_synchronize_unregister(void)

#define TRACE_EVENT_PERF_PERM(event, expr...)

+#define TRACE_EVENT_MAP_COND(name, map, proto, args, cond, \
+ struct, assign, print)
+
#endif /* ifdef TRACE_EVENT (see note above) */
diff --git a/include/trace/define_trace.h b/include/trace/define_trace.h
index 6e3945f..4e112f2 100644
--- a/include/trace/define_trace.h
+++ b/include/trace/define_trace.h
@@ -45,6 +45,10 @@
assign, print, reg, unreg) \
DEFINE_TRACE_FN(name, reg, unreg)

+#undef TRACE_EVENT_MAP_COND
+#define TRACE_EVENT_MAP_COND(name, map, proto, args, cond, tstruct, \
+ assign, print)
+
#undef DEFINE_EVENT
#define DEFINE_EVENT(template, name, proto, args) \
DEFINE_TRACE(name)
@@ -100,7 +104,9 @@
#undef TRACE_EVENT_FN
#undef TRACE_EVENT_FN_COND
#undef TRACE_EVENT_CONDITION
+#undef TRACE_EVENT_MAP_COND
#undef DECLARE_EVENT_CLASS
+#undef DECLARE_EVENT_COND_CLASS
#undef DEFINE_EVENT
#undef DEFINE_EVENT_FN
#undef DEFINE_EVENT_PRINT
diff --git a/include/trace/perf.h b/include/trace/perf.h
index 04fe68bb..dbd3d27 100644
--- a/include/trace/perf.h
+++ b/include/trace/perf.h
@@ -26,8 +26,9 @@
#undef __perf_task
#define __perf_task(t) (__task = (t))

-#undef DECLARE_EVENT_CLASS
-#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+#undef DECLARE_EVENT_COND_CLASS
+#define DECLARE_EVENT_COND_CLASS(call, proto, args, cond, tstruct, \
+ assign, print) \
static notrace void \
perf_trace_##call(void *__data, proto) \
{ \
@@ -43,6 +44,9 @@
int __data_size; \
int rctx; \
\
+ if (!(cond)) \
+ return; \
+ \
__data_size = trace_event_get_offsets_##call(&__data_offsets, args); \
\
head = this_cpu_ptr(event_call->perf_events); \
@@ -69,18 +73,28 @@
head, __task); \
}

+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+ DECLARE_EVENT_COND_CLASS(call, PARAMS(proto), PARAMS(args), \
+ 1, PARAMS(tstruct), PARAMS(assign), \
+ PARAMS(print))
+
/*
* This part is compiled out, it is only here as a build time check
* to make sure that if the tracepoint handling changes, the
* perf probe will fail to compile unless it too is updated.
*/
-#undef DEFINE_EVENT
-#define DEFINE_EVENT(template, call, proto, args) \
-static inline void perf_test_probe_##call(void) \
+#undef DEFINE_EVENT_MAP_COND
+#define DEFINE_EVENT_MAP_COND(template, call, map, proto, args, cond) \
+static inline void perf_test_probe_##map(void) \
{ \
check_trace_callback_type_##call(perf_trace_##template); \
}

+#undef DEFINE_EVENT
+#define DEFINE_EVENT(template, call, proto, args) \
+ DEFINE_EVENT_MAP_COND(template, call, call, PARAMS(proto), \
+ PARAMS(args), 1)

#undef DEFINE_EVENT_PRINT
#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \
diff --git a/include/trace/trace_events.h b/include/trace/trace_events.h
index 467e12f..953cea3 100644
--- a/include/trace/trace_events.h
+++ b/include/trace/trace_events.h
@@ -65,6 +65,18 @@
PARAMS(print)); \
DEFINE_EVENT(name, name, PARAMS(proto), PARAMS(args));

+#undef TRACE_EVENT_MAP_COND
+#define TRACE_EVENT_MAP_COND(name, map, proto, args, cond, \
+ tstruct, assign, print) \
+ DECLARE_EVENT_COND_CLASS(map, \
+ PARAMS(proto), \
+ PARAMS(args), \
+ PARAMS(cond), \
+ PARAMS(tstruct), \
+ PARAMS(assign), \
+ PARAMS(print)); \
+ DEFINE_EVENT_MAP_COND(map, name, map, PARAMS(proto), \
+ PARAMS(args), cond);

#undef __field
#define __field(type, item) type item;
@@ -93,8 +105,9 @@
#undef TP_STRUCT__entry
#define TP_STRUCT__entry(args...) args

-#undef DECLARE_EVENT_CLASS
-#define DECLARE_EVENT_CLASS(name, proto, args, tstruct, assign, print) \
+#undef DECLARE_EVENT_COND_CLASS
+#define DECLARE_EVENT_COND_CLASS(name, proto, args, cond, tstruct, \
+ assign, print) \
struct trace_event_raw_##name { \
struct trace_entry ent; \
tstruct \
@@ -103,6 +116,11 @@
\
static struct trace_event_class event_class_##name;

+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(name, proto, args, tstruct, assign, print) \
+ DECLARE_EVENT_COND_CLASS(name, PARAMS(proto), PARAMS(args), 1, \
+ PARAMS(tstruct), PARAMS(assign), PARAMS(print))
+
#undef DEFINE_EVENT
#define DEFINE_EVENT(template, name, proto, args) \
static struct trace_event_call __used \
@@ -116,6 +134,9 @@
#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \
DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))

+#undef DEFINE_EVENT_MAP_COND
+#define DEFINE_EVENT_MAP_COND(template, name, map, proto, args, cond)
+
/* Callbacks are meaningless to ftrace. */
#undef TRACE_EVENT_FN
#define TRACE_EVENT_FN(name, proto, args, tstruct, \
@@ -182,12 +203,18 @@
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1)

-#undef DECLARE_EVENT_CLASS
-#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+#undef DECLARE_EVENT_COND_CLASS
+#define DECLARE_EVENT_COND_CLASS(call, proto, args, cond, tstruct, \
+ assign, print) \
struct trace_event_data_offsets_##call { \
tstruct; \
};

+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+ DECLARE_EVENT_COND_CLASS(call, PARAMS(proto), PARAMS(args), 1, \
+ PARAMS(tstruct), PARAMS(assign), PARAMS(print))
+
#undef DEFINE_EVENT
#define DEFINE_EVENT(template, name, proto, args)

@@ -195,6 +222,9 @@
#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \
DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))

+#undef DEFINE_EVENT_MAP_COND
+#define DEFINE_EVENT_MAP_COND(template, name, map, proto, args, cond)
+
#undef TRACE_EVENT_FLAGS
#define TRACE_EVENT_FLAGS(event, flag)

@@ -307,8 +337,9 @@
trace_print_array_seq(p, array, count, el_size); \
})

-#undef DECLARE_EVENT_CLASS
-#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+#undef DECLARE_EVENT_COND_CLASS
+#define DECLARE_EVENT_COND_CLASS(call, proto, args, cond, tstruct, \
+ assign, print) \
static notrace enum print_line_t \
trace_raw_output_##call(struct trace_iterator *iter, int flags, \
struct trace_event *trace_event) \
@@ -332,6 +363,11 @@
.trace = trace_raw_output_##call, \
};

+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+ DECLARE_EVENT_COND_CLASS(call, PARAMS(proto), PARAMS(args), 1, \
+ PARAMS(tstruct), PARAMS(assign), PARAMS(print))
+
#undef DEFINE_EVENT_PRINT
#define DEFINE_EVENT_PRINT(template, call, proto, args, print) \
static notrace enum print_line_t \
@@ -410,8 +446,9 @@
#undef __bitmask
#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, -1)

-#undef DECLARE_EVENT_CLASS
-#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, func, print) \
+#undef DECLARE_EVENT_COND_CLASS
+#define DECLARE_EVENT_COND_CLASS(call, proto, args, cond, tstruct, \
+ func, print) \
static int notrace __init \
trace_event_define_fields_##call(struct trace_event_call *event_call) \
{ \
@@ -423,6 +460,11 @@
return ret; \
}

+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, func, print) \
+ DECLARE_EVENT_COND_CLASS(call, PARAMS(proto), PARAMS(args), 1, \
+ PARAMS(tstruct), PARAMS(func), PARAMS(print))
+
#undef DEFINE_EVENT
#define DEFINE_EVENT(template, name, proto, args)

@@ -430,6 +472,9 @@
#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \
DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))

+#undef DEFINE_EVENT_MAP_COND
+#define DEFINE_EVENT_MAP_COND(template, name, map, proto, args, cond)
+
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)

/*
@@ -489,8 +534,9 @@
#define __bitmask(item, nr_bits) __dynamic_array(unsigned long, item, \
__bitmask_size_in_longs(nr_bits))

-#undef DECLARE_EVENT_CLASS
-#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+#undef DECLARE_EVENT_COND_CLASS
+#define DECLARE_EVENT_COND_CLASS(call, proto, args, cond, tstruct, \
+ assign, print) \
static inline notrace int trace_event_get_offsets_##call( \
struct trace_event_data_offsets_##call *__data_offsets, proto) \
{ \
@@ -503,6 +549,11 @@
return __data_size; \
}

+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+ DECLARE_EVENT_COND_CLASS(call, PARAMS(proto), PARAMS(args), 1, \
+ PARAMS(tstruct), PARAMS(assign), PARAMS(print))
+
#undef DEFINE_EVENT
#define DEFINE_EVENT(template, name, proto, args)

@@ -510,6 +561,9 @@
#define DEFINE_EVENT_PRINT(template, name, proto, args, print) \
DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))

+#undef DEFINE_EVENT_MAP_COND
+#define DEFINE_EVENT_MAP_COND(template, name, map, proto, args, cond)
+
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)

/*
@@ -658,8 +712,9 @@
#undef __perf_task
#define __perf_task(t) (t)

-#undef DECLARE_EVENT_CLASS
-#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+#undef DECLARE_EVENT_COND_CLASS
+#define DECLARE_EVENT_COND_CLASS(call, proto, args, cond, tstruct, \
+ assign, print) \
\
static notrace void \
trace_event_raw_event_##call(void *__data, proto) \
@@ -670,6 +725,9 @@
struct trace_event_raw_##call *entry; \
int __data_size; \
\
+ if (!(cond)) \
+ return; \
+ \
if (trace_trigger_soft_disabled(trace_file)) \
return; \
\
@@ -687,19 +745,30 @@
\
trace_event_buffer_commit(&fbuffer); \
}
+
+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+ DECLARE_EVENT_COND_CLASS(call, PARAMS(proto), PARAMS(args), 1, \
+ PARAMS(tstruct), PARAMS(assign), PARAMS(print))
+
/*
* The ftrace_test_probe is compiled out, it is only here as a build time check
* to make sure that if the tracepoint handling changes, the ftrace probe will
* fail to compile unless it too is updated.
*/

-#undef DEFINE_EVENT
-#define DEFINE_EVENT(template, call, proto, args) \
-static inline void ftrace_test_probe_##call(void) \
+#undef DEFINE_EVENT_MAP_COND
+#define DEFINE_EVENT_MAP_COND(template, call, map, proto, args, cond) \
+ static inline void ftrace_test_probe_##map(void) \
{ \
check_trace_callback_type_##call(trace_event_raw_event_##template); \
}

+#undef DEFINE_EVENT
+#define DEFINE_EVENT(template, call, proto, args) \
+ DEFINE_EVENT_MAP_COND(template, call, call, PARAMS(proto), \
+ PARAMS(args), 1)
+
#undef DEFINE_EVENT_PRINT
#define DEFINE_EVENT_PRINT(template, name, proto, args, print)

@@ -720,8 +789,9 @@
#undef TP_printk
#define TP_printk(fmt, args...) "\"" fmt "\", " __stringify(args)

-#undef DECLARE_EVENT_CLASS
-#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+#undef DECLARE_EVENT_COND_CLASS
+#define DECLARE_EVENT_COND_CLASS(call, proto, args, cond, tstruct, \
+ assign, print) \
_TRACE_PERF_PROTO(call, PARAMS(proto)); \
static char print_fmt_##call[] = print; \
static struct trace_event_class __used __refdata event_class_##call = { \
@@ -734,6 +804,11 @@
_TRACE_PERF_INIT(call) \
};

+#undef DECLARE_EVENT_CLASS
+#define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \
+ DECLARE_EVENT_COND_CLASS(call, PARAMS(proto), PARAMS(args), 1, \
+ PARAMS(tstruct), PARAMS(assign), PARAMS(print))
+
#undef DEFINE_EVENT
#define DEFINE_EVENT(template, call, proto, args) \
\
@@ -766,4 +841,25 @@
static struct trace_event_call __used \
__attribute__((section("_ftrace_events"))) *__event_##call = &event_##call

+#undef DEFINE_EVENT_MAP_COND
+#define DEFINE_EVENT_MAP_COND(_template, _call, _map, _proto, _args, cond) \
+ \
+static struct trace_event_map event_map_##_map = { \
+ .tp = &__tracepoint_##_call, \
+ .name = #_map, \
+}; \
+ \
+static struct trace_event_call __used event_##_map = { \
+ .class = &event_class_##_template, \
+ { \
+ .map = &event_map_##_map, \
+ }, \
+ .event.funcs = &trace_event_type_funcs_##_template, \
+ .print_fmt = print_fmt_##_template, \
+ .flags = TRACE_EVENT_FL_TRACEPOINT | TRACE_EVENT_FL_MAP, \
+}; \
+static struct trace_event_call __used \
+__attribute__((section("_ftrace_events"))) *__event_##_map = &event_##_map
+
+
#include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 9311654..b4ea2bf 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -287,26 +287,33 @@ int trace_event_reg(struct trace_event_call *call,
enum trace_reg type, void *data)
{
struct trace_event_file *file = data;
+ struct tracepoint *tp;

WARN_ON(!(call->flags & TRACE_EVENT_FL_TRACEPOINT));
+
+ if (call->flags & TRACE_EVENT_FL_MAP)
+ tp = call->map->tp;
+ else
+ tp = call->tp;
+
switch (type) {
case TRACE_REG_REGISTER:
- return tracepoint_probe_register(call->tp,
+ return tracepoint_probe_register(tp,
call->class->probe,
file);
case TRACE_REG_UNREGISTER:
- tracepoint_probe_unregister(call->tp,
+ tracepoint_probe_unregister(tp,
call->class->probe,
file);
return 0;

#ifdef CONFIG_PERF_EVENTS
case TRACE_REG_PERF_REGISTER:
- return tracepoint_probe_register(call->tp,
+ return tracepoint_probe_register(tp,
call->class->perf_probe,
call);
case TRACE_REG_PERF_UNREGISTER:
- tracepoint_probe_unregister(call->tp,
+ tracepoint_probe_unregister(tp,
call->class->perf_probe,
call);
return 0;
--
1.9.1