Skip to content

Commit

Permalink
ftrace: Add recording of functions that caused recursion
Browse files Browse the repository at this point in the history
This adds CONFIG_FTRACE_RECORD_RECURSION that will record to a file
"recursed_functions" all the functions that caused recursion while a
callback to the function tracer was running.

Link: https://lkml.kernel.org/r/[email protected]

Cc: Masami Hiramatsu <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Peter Zijlstra <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Jonathan Corbet <[email protected]>
Cc: Guo Ren <[email protected]>
Cc: "James E.J. Bottomley" <[email protected]>
Cc: Helge Deller <[email protected]>
Cc: Michael Ellerman <[email protected]>
Cc: Benjamin Herrenschmidt <[email protected]>
Cc: Paul Mackerras <[email protected]>
Cc: Heiko Carstens <[email protected]>
Cc: Vasily Gorbik <[email protected]>
Cc: Christian Borntraeger <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Borislav Petkov <[email protected]>
Cc: [email protected]
Cc: "H. Peter Anvin" <[email protected]>
Cc: Kees Cook <[email protected]>
Cc: Anton Vorontsov <[email protected]>
Cc: Colin Cross <[email protected]>
Cc: Tony Luck <[email protected]>
Cc: Josh Poimboeuf <[email protected]>
Cc: Jiri Kosina <[email protected]>
Cc: Miroslav Benes <[email protected]>
Cc: Petr Mladek <[email protected]>
Cc: Joe Lawrence <[email protected]>
Cc: Kamalesh Babulal <[email protected]>
Cc: Mauro Carvalho Chehab <[email protected]>
Cc: Sebastian Andrzej Siewior <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Signed-off-by: Steven Rostedt (VMware) <[email protected]>
  • Loading branch information
rostedt committed Nov 6, 2020
1 parent a25d036 commit 773c167
Show file tree
Hide file tree
Showing 17 changed files with 306 additions and 20 deletions.
6 changes: 4 additions & 2 deletions Documentation/trace/ftrace-uses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ can help in this regard. If you start your code with:

int bit;

bit = ftrace_test_recursion_trylock();
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;

Expand All @@ -130,7 +130,9 @@ The code in between will be safe to use, even if it ends up calling a
function that the callback is tracing. Note, on success,
ftrace_test_recursion_trylock() will disable preemption, and the
ftrace_test_recursion_unlock() will enable it again (if it was previously
enabled).
enabled). The instruction pointer (ip) and its parent (parent_ip) is passed to
ftrace_test_recursion_trylock() to record where the recursion happened
(if CONFIG_FTRACE_RECORD_RECURSION is set).

Alternatively, if the FTRACE_OPS_FL_RECURSION flag is set on the ftrace_ops
(as explained below), then a helper trampoline will be used to test
Expand Down
2 changes: 1 addition & 1 deletion arch/csky/kernel/probes/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct kprobe *p;
struct kprobe_ctlblk *kcb;

bit = ftrace_test_recursion_trylock();
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;

Expand Down
2 changes: 1 addition & 1 deletion arch/parisc/kernel/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct kprobe *p;
int bit;

bit = ftrace_test_recursion_trylock();
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;

Expand Down
2 changes: 1 addition & 1 deletion arch/powerpc/kernel/kprobes-ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void kprobe_ftrace_handler(unsigned long nip, unsigned long parent_nip,
struct kprobe_ctlblk *kcb;
int bit;

bit = ftrace_test_recursion_trylock();
bit = ftrace_test_recursion_trylock(nip, parent_nip);
if (bit < 0)
return;

Expand Down
2 changes: 1 addition & 1 deletion arch/s390/kernel/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct kprobe *p;
int bit;

bit = ftrace_test_recursion_trylock();
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;

Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/kprobes/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
struct kprobe_ctlblk *kcb;
int bit;

bit = ftrace_test_recursion_trylock();
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;

Expand Down
2 changes: 1 addition & 1 deletion fs/pstore/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ static void notrace pstore_ftrace_call(unsigned long ip,
if (unlikely(oops_in_progress))
return;

bit = ftrace_test_recursion_trylock();
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;

Expand Down
29 changes: 25 additions & 4 deletions include/linux/trace_recursion.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ enum {
* not be correct. Allow for a single recursion to cover this case.
*/
TRACE_TRANSITION_BIT,

/* Used to prevent recursion recording from recursing. */
TRACE_RECORD_RECURSION_BIT,
};

#define trace_recursion_set(bit) do { (current)->trace_recursion |= (1<<(bit)); } while (0)
Expand Down Expand Up @@ -142,7 +145,22 @@ static __always_inline int trace_get_context_bit(void)
pc & HARDIRQ_MASK ? TRACE_CTX_IRQ : TRACE_CTX_SOFTIRQ;
}

static __always_inline int trace_test_and_set_recursion(int start, int max)
#ifdef CONFIG_FTRACE_RECORD_RECURSION
extern void ftrace_record_recursion(unsigned long ip, unsigned long parent_ip);
# define do_ftrace_record_recursion(ip, pip) \
do { \
if (!trace_recursion_test(TRACE_RECORD_RECURSION_BIT)) { \
trace_recursion_set(TRACE_RECORD_RECURSION_BIT); \
ftrace_record_recursion(ip, pip); \
trace_recursion_clear(TRACE_RECORD_RECURSION_BIT); \
} \
} while (0)
#else
# define do_ftrace_record_recursion(ip, pip) do { } while (0)
#endif

static __always_inline int trace_test_and_set_recursion(unsigned long ip, unsigned long pip,
int start, int max)
{
unsigned int val = current->trace_recursion;
int bit;
Expand All @@ -158,8 +176,10 @@ static __always_inline int trace_test_and_set_recursion(int start, int max)
* a switch between contexts. Allow for a single recursion.
*/
bit = TRACE_TRANSITION_BIT;
if (trace_recursion_test(bit))
if (trace_recursion_test(bit)) {
do_ftrace_record_recursion(ip, pip);
return -1;
}
trace_recursion_set(bit);
barrier();
return bit + 1;
Expand Down Expand Up @@ -199,9 +219,10 @@ static __always_inline void trace_clear_recursion(int bit)
* Returns: -1 if a recursion happened.
* >= 0 if no recursion
*/
static __always_inline int ftrace_test_recursion_trylock(void)
static __always_inline int ftrace_test_recursion_trylock(unsigned long ip,
unsigned long parent_ip)
{
return trace_test_and_set_recursion(TRACE_FTRACE_START, TRACE_FTRACE_MAX);
return trace_test_and_set_recursion(ip, parent_ip, TRACE_FTRACE_START, TRACE_FTRACE_MAX);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion kernel/livepatch/patch.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ static void notrace klp_ftrace_handler(unsigned long ip,

ops = container_of(fops, struct klp_ops, fops);

bit = ftrace_test_recursion_trylock();
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (WARN_ON_ONCE(bit < 0))
return;
/*
Expand Down
25 changes: 25 additions & 0 deletions kernel/trace/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,31 @@ config TRACE_EVAL_MAP_FILE

If unsure, say N.

config FTRACE_RECORD_RECURSION
bool "Record functions that recurse in function tracing"
depends on FUNCTION_TRACER
help
All callbacks that attach to the function tracing have some sort
of protection against recursion. Even though the protection exists,
it adds overhead. This option will create a file in the tracefs
file system called "recursed_functions" that will list the functions
that triggered a recursion.

This will add more overhead to cases that have recursion.

If unsure, say N

config FTRACE_RECORD_RECURSION_SIZE
int "Max number of recursed functions to record"
default 128
depends on FTRACE_RECORD_RECURSION
help
This defines the limit of number of functions that can be
listed in the "recursed_functions" file, that lists all
the functions that caused a recursion to happen.
This file can be reset, but the limit can not change in
size at runtime.

config GCOV_PROFILE_FTRACE
bool "Enable GCOV profiling on ftrace subsystem"
depends on GCOV_KERNEL
Expand Down
1 change: 1 addition & 0 deletions kernel/trace/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ obj-$(CONFIG_DYNAMIC_EVENTS) += trace_dynevent.o
obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o
obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o
obj-$(CONFIG_BOOTTIME_TRACING) += trace_boot.o
obj-$(CONFIG_FTRACE_RECORD_RECURSION) += trace_recursion_record.o

obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o

Expand Down
4 changes: 2 additions & 2 deletions kernel/trace/ftrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -6918,7 +6918,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op;
int bit;

bit = trace_test_and_set_recursion(TRACE_LIST_START, TRACE_LIST_MAX);
bit = trace_test_and_set_recursion(ip, parent_ip, TRACE_LIST_START, TRACE_LIST_MAX);
if (bit < 0)
return;

Expand Down Expand Up @@ -6993,7 +6993,7 @@ static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip,
{
int bit;

bit = trace_test_and_set_recursion(TRACE_LIST_START, TRACE_LIST_MAX);
bit = trace_test_and_set_recursion(ip, parent_ip, TRACE_LIST_START, TRACE_LIST_MAX);
if (bit < 0)
return;

Expand Down
2 changes: 1 addition & 1 deletion kernel/trace/trace_event_perf.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip,
if ((unsigned long)ops->private != smp_processor_id())
return;

bit = ftrace_test_recursion_trylock();
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;

Expand Down
2 changes: 1 addition & 1 deletion kernel/trace/trace_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function_trace_call(unsigned long ip, unsigned long parent_ip,
if (unlikely(!tr->function_enabled))
return;

bit = ftrace_test_recursion_trylock();
bit = ftrace_test_recursion_trylock(ip, parent_ip);
if (bit < 0)
return;

Expand Down
6 changes: 3 additions & 3 deletions kernel/trace/trace_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,8 @@ static inline const char *kretprobed(const char *name)
}
#endif /* CONFIG_KRETPROBES */

static void
seq_print_sym(struct trace_seq *s, unsigned long address, bool offset)
void
trace_seq_print_sym(struct trace_seq *s, unsigned long address, bool offset)
{
#ifdef CONFIG_KALLSYMS
char str[KSYM_SYMBOL_LEN];
Expand Down Expand Up @@ -420,7 +420,7 @@ seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags)
goto out;
}

seq_print_sym(s, ip, sym_flags & TRACE_ITER_SYM_OFFSET);
trace_seq_print_sym(s, ip, sym_flags & TRACE_ITER_SYM_OFFSET);

if (sym_flags & TRACE_ITER_SYM_ADDR)
trace_seq_printf(s, " <" IP_FMT ">", ip);
Expand Down
1 change: 1 addition & 0 deletions kernel/trace/trace_output.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extern int
seq_print_ip_sym(struct trace_seq *s, unsigned long ip,
unsigned long sym_flags);

extern void trace_seq_print_sym(struct trace_seq *s, unsigned long address, bool offset);
extern int trace_print_context(struct trace_iterator *iter);
extern int trace_print_lat_context(struct trace_iterator *iter);

Expand Down
Loading

0 comments on commit 773c167

Please sign in to comment.