Re: [PATCH 1/2] tracepoints: Add helper to test if tracepoint is enabled in a header

From: Mathieu Desnoyers
Date: Thu Sep 24 2020 - 14:27:48 EST


----- On Sep 24, 2020, at 2:19 PM, Axel Rasmussen axelrasmussen@xxxxxxxxxx wrote:

> On Thu, Sep 24, 2020 at 10:42 AM Mathieu Desnoyers
> <mathieu.desnoyers@xxxxxxxxxxxx> wrote:
>>
>> ----- On Sep 24, 2020, at 1:09 PM, rostedt rostedt@xxxxxxxxxxx wrote:
>>
>> > From: "Steven Rostedt (VMware)" <rostedt@xxxxxxxxxxx>
>> >
>> > As tracepoints are discouraged from being added in a header because it can
>> > cause side effects if other tracepoints are in headers, the common
>> > workaround is to add a function call that calls a wrapper function in a
>> > C file that then calls the tracepoint. But as function calls add overhead,
>> > this function should only be called when the tracepoint in question is
>> > enabled. To get around the overhead, a static_branch can be used that only
>> > gets set when the tracepoint is enabled, and then inside the block of the
>> > static branch can contain the call to the tracepoint wrapper.
>> >
>> > Add a tracepoint_enabled(tp) macro that gets passed the name of the
>> > tracepoint, and this becomes a static_branch that is enabled when the
>> > tracepoint is enabled and is a nop when the tracepoint is disabled.
>> >
>> > Signed-off-by: Steven Rostedt (VMware) <rostedt@xxxxxxxxxxx>
>> > ---
>> > Documentation/trace/tracepoints.rst | 25 ++++++++++++++++++++++
>> > include/linux/tracepoint-defs.h | 33 +++++++++++++++++++++++++++++
>> > 2 files changed, 58 insertions(+)
>> >
>> > diff --git a/Documentation/trace/tracepoints.rst
>> > b/Documentation/trace/tracepoints.rst
>> > index 6e3ce3bf3593..833d39ee1c44 100644
>> > --- a/Documentation/trace/tracepoints.rst
>> > +++ b/Documentation/trace/tracepoints.rst
>> > @@ -146,3 +146,28 @@ with jump labels and avoid conditional branches.
>> > define tracepoints. Check http://lwn.net/Articles/379903,
>> > http://lwn.net/Articles/381064 and http://lwn.net/Articles/383362
>> > for a series of articles with more details.
>> > +
>> > +If you require calling a tracepoint from a header file, it is not
>> > +recommended to call one directly or to use the trace_<tracepoint>_enabled()
>> > +function call, as tracepoints in header files can have side effects if a
>> > +header is included from a file that has CREATE_TRACE_POINTS set. Instead,
>> > +include tracepoint-defs.h and use trace_enabled().
>>
>> Tracepoints per-se have no issues being used from header files. The TRACE_EVENT
>> infrastructure seems to be the cause of this problem. We should fix trace events
>> rather than require all users to use weird work-arounds thorough the kernel code
>> base.
>>
>> I am not against the idea of a tracepoint_enabled(tp), but I am against the
>> motivation behind this patch and the new tracepoint user requirements it
>> documents.
>
> Perhaps anecdotally, I've found that the situation Steven described
> occurs not just because of the TRACE_EVENT infrastructure. We also run
> into this problem when adding tracepoints under any "very core" APIs,
> i.e. anything that is transiently included from linux/tracepoint.h.
> For example, I ran into this issue while adding tracepoints under the
> linux/mmap_lock.h API, because that header is somehow transiently
> included by linux/tracepoint.h (sorry, I don't have the exact
> transient include path on hand; I can dig it up if it would be
> useful).

If it's not too much trouble, it would be useful to know what headers
included from tracepoint.h are problematic. When I originally wrote
tracepoint.h, I made sure to include the minimum set needed, but I
suspect some feature-creep may have ended up including additional
headers which are not strictly needed for the core instrumentation.

Once we identify those, we can then make sure we split tracepoint.h
into a "core instrumentation" header (tracepoint.h) and non-core
stuff (e.g. tracepoint-utils.h or better name).

Another possible approach is to make sure that whatever is included
by the tracepoint.h "core" is split into *.h and *-{defs,types}.h, for
cases where all we need from tracepoint.h is type declarations.

Thanks,

Mathieu

>
>
>
>>
>> > +
>> > +In a C file::
>> > +
>> > + void do_trace_foo_bar_wrapper(args)
>> > + {
>> > + trace_foo_bar(args);
>> > + }
>> > +
>> > +In the header file::
>> > +
>> > + DECLEARE_TRACEPOINT(foo_bar);
>> > +
>> > + static inline void some_inline_function()
>> > + {
>> > + [..]
>> > + if (trace_enabled(foo_bar))
>>
>> Is it trace_enabled() or tracepoint_enabled() ? There is a mismatch
>> between the commit message/code and the documentation.
>>
>> Thanks,
>>
>> Mathieu
>>
>> > + do_trace_foo_bar_wrapper(args);
>> > + [..]
>> > + }
>> > diff --git a/include/linux/tracepoint-defs.h b/include/linux/tracepoint-defs.h
>> > index b29950a19205..ca2f1f77f6f8 100644
>> > --- a/include/linux/tracepoint-defs.h
>> > +++ b/include/linux/tracepoint-defs.h
>> > @@ -48,4 +48,37 @@ struct bpf_raw_event_map {
>> > u32 writable_size;
>> > } __aligned(32);
>> >
>> > +/*
>> > + * If a tracepoint needs to be called from a header file, it is not
>> > + * recommended to call it directly, as tracepoints in header files
>> > + * may cause side-effects. Instead, use trace_enabled() to test
>> > + * if the tracepoint is enabled, then if it is, call a wrapper
>> > + * function defined in a C file that will then call the tracepoint.
>> > + *
>> > + * For "trace_foo()", you would need to create a wrapper function
>> > + * in a C file to call trace_foo():
>> > + * void trace_bar(args) { trace_foo(args); }
>> > + * Then in the header file, declare the tracepoint:
>> > + * DECLARE_TRACEPOINT(foo);
>> > + * And call your wrapper:
>> > + * static inline void some_inlined_function() {
>> > + * [..]
>> > + * if (tracepoint_enabled(foo))
>> > + * trace_bar(args);
>> > + * [..]
>> > + * }
>> > + *
>> > + * Note: tracepoint_enabled(foo) is equivalent to trace_foo_enabled()
>> > + * but is safe to have in headers, where trace_foo_enabled() is not.
>> > + */
>> > +#define DECLARE_TRACEPOINT(tp) \
>> > + extern struct tracepoint __tracepoint_##tp
>> > +
>> > +#ifdef CONFIG_TRACEPOINTS
>> > +# define tracepoint_enabled(tp) \
>> > + static_key_false(&(__tracepoint_##tp).key)
>> > +#else
>> > +# define tracepoint_enabled(tracepoint) false
>> > +#endif
>> > +
>> > #endif
>> > --
>> > 2.28.0
>>
>> --
>> Mathieu Desnoyers
>> EfficiOS Inc.
> > http://www.efficios.com

--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com