]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ftrace: Add arguments to function tracer
authorSven Schnelle <svens@linux.ibm.com>
Thu, 27 Feb 2025 18:58:08 +0000 (13:58 -0500)
committerSteven Rostedt (Google) <rostedt@goodmis.org>
Tue, 4 Mar 2025 16:27:24 +0000 (11:27 -0500)
Wire up the code to print function arguments in the function tracer.
This functionality can be enabled/disabled during runtime with
options/func-args.

        ping-689     [004] b....    77.170220: dummy_xmit(skb = 0x82904800, dev = 0x882d0000) <-dev_hard_start_xmit

Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Paul Walmsley <paul.walmsley@sifive.com>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Guo Ren <guoren@kernel.org>
Cc: Donglin Peng <dolinux.peng@gmail.com>
Cc: Zheng Yejian <zhengyejian@huaweicloud.com>
Link: https://lore.kernel.org/20250227185823.154996172@goodmis.org
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Co-developed-by: Steven Rostedt (Google) <rostedt@goodmis.org>
Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_entries.h
kernel/trace/trace_functions.c
kernel/trace/trace_irqsoff.c
kernel/trace/trace_output.c
kernel/trace/trace_sched_wakeup.c

index 0e6d517e74e0fd19bfb31fcbcb977d57f894c78b..cb13c88abfd629fb5f158579bcb0301d3326bb78 100644 (file)
@@ -2878,13 +2878,16 @@ trace_buffer_unlock_commit_nostack(struct trace_buffer *buffer,
 
 void
 trace_function(struct trace_array *tr, unsigned long ip, unsigned long
-              parent_ip, unsigned int trace_ctx)
+              parent_ip, unsigned int trace_ctx, struct ftrace_regs *fregs)
 {
        struct trace_buffer *buffer = tr->array_buffer.buffer;
        struct ring_buffer_event *event;
        struct ftrace_entry *entry;
+       int size = sizeof(*entry);
 
-       event = __trace_buffer_lock_reserve(buffer, TRACE_FN, sizeof(*entry),
+       size += FTRACE_REGS_MAX_ARGS * !!fregs * sizeof(long);
+
+       event = __trace_buffer_lock_reserve(buffer, TRACE_FN, size,
                                            trace_ctx);
        if (!event)
                return;
@@ -2892,6 +2895,13 @@ trace_function(struct trace_array *tr, unsigned long ip, unsigned long
        entry->ip                       = ip;
        entry->parent_ip                = parent_ip;
 
+#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
+       if (fregs) {
+               for (int i = 0; i < FTRACE_REGS_MAX_ARGS; i++)
+                       entry->args[i] = ftrace_regs_get_argument(fregs, i);
+       }
+#endif
+
        if (static_branch_unlikely(&trace_function_exports_enabled))
                ftrace_exports(event, TRACE_EXPORT_FUNCTION);
        __buffer_unlock_commit(buffer, event);
index 6963cd83b6daf23059aba4d778c43c8a176ee1c0..32da87f45010b60ff2b1399973418257979dbe67 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/workqueue.h>
 #include <linux/ctype.h>
 #include <linux/once_lite.h>
+#include <linux/ftrace_regs.h>
 
 #include "pid_list.h"
 
@@ -697,7 +698,8 @@ unsigned long trace_total_entries(struct trace_array *tr);
 void trace_function(struct trace_array *tr,
                    unsigned long ip,
                    unsigned long parent_ip,
-                   unsigned int trace_ctx);
+                   unsigned int trace_ctx,
+                   struct ftrace_regs *regs);
 void trace_graph_function(struct trace_array *tr,
                    unsigned long ip,
                    unsigned long parent_ip,
index 77a8ba3bc1e36f444fd4574c2ca382e92f5d31c8..ee40d4e6ad1cc8d1d9fb708490deedfa7ce5b61f 100644 (file)
@@ -61,8 +61,9 @@ FTRACE_ENTRY_REG(function, ftrace_entry,
        TRACE_FN,
 
        F_STRUCT(
-               __field_fn(     unsigned long,  ip              )
-               __field_fn(     unsigned long,  parent_ip       )
+               __field_fn(     unsigned long,          ip              )
+               __field_fn(     unsigned long,          parent_ip       )
+               __dynamic_array( unsigned long,         args            )
        ),
 
        F_printk(" %ps <-- %ps",
index df56f9b76010943a0673122fbc46d353a8018bb4..98ccf3f00c519deaa768a3341d1774a5ca8426a9 100644 (file)
@@ -25,6 +25,9 @@ static void
 function_trace_call(unsigned long ip, unsigned long parent_ip,
                    struct ftrace_ops *op, struct ftrace_regs *fregs);
 static void
+function_args_trace_call(unsigned long ip, unsigned long parent_ip,
+                        struct ftrace_ops *op, struct ftrace_regs *fregs);
+static void
 function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
                          struct ftrace_ops *op, struct ftrace_regs *fregs);
 static void
@@ -42,9 +45,10 @@ enum {
        TRACE_FUNC_NO_OPTS              = 0x0, /* No flags set. */
        TRACE_FUNC_OPT_STACK            = 0x1,
        TRACE_FUNC_OPT_NO_REPEATS       = 0x2,
+       TRACE_FUNC_OPT_ARGS             = 0x4,
 
        /* Update this to next highest bit. */
-       TRACE_FUNC_OPT_HIGHEST_BIT      = 0x4
+       TRACE_FUNC_OPT_HIGHEST_BIT      = 0x8
 };
 
 #define TRACE_FUNC_OPT_MASK    (TRACE_FUNC_OPT_HIGHEST_BIT - 1)
@@ -114,6 +118,8 @@ static ftrace_func_t select_trace_function(u32 flags_val)
        switch (flags_val & TRACE_FUNC_OPT_MASK) {
        case TRACE_FUNC_NO_OPTS:
                return function_trace_call;
+       case TRACE_FUNC_OPT_ARGS:
+               return function_args_trace_call;
        case TRACE_FUNC_OPT_STACK:
                return function_stack_trace_call;
        case TRACE_FUNC_OPT_NO_REPEATS:
@@ -220,7 +226,34 @@ function_trace_call(unsigned long ip, unsigned long parent_ip,
 
        data = this_cpu_ptr(tr->array_buffer.data);
        if (!atomic_read(&data->disabled))
-               trace_function(tr, ip, parent_ip, trace_ctx);
+               trace_function(tr, ip, parent_ip, trace_ctx, NULL);
+
+       ftrace_test_recursion_unlock(bit);
+}
+
+static void
+function_args_trace_call(unsigned long ip, unsigned long parent_ip,
+                        struct ftrace_ops *op, struct ftrace_regs *fregs)
+{
+       struct trace_array *tr = op->private;
+       struct trace_array_cpu *data;
+       unsigned int trace_ctx;
+       int bit;
+       int cpu;
+
+       if (unlikely(!tr->function_enabled))
+               return;
+
+       bit = ftrace_test_recursion_trylock(ip, parent_ip);
+       if (bit < 0)
+               return;
+
+       trace_ctx = tracing_gen_ctx();
+
+       cpu = smp_processor_id();
+       data = per_cpu_ptr(tr->array_buffer.data, cpu);
+       if (!atomic_read(&data->disabled))
+               trace_function(tr, ip, parent_ip, trace_ctx, fregs);
 
        ftrace_test_recursion_unlock(bit);
 }
@@ -270,7 +303,7 @@ function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
 
        if (likely(disabled == 1)) {
                trace_ctx = tracing_gen_ctx_flags(flags);
-               trace_function(tr, ip, parent_ip, trace_ctx);
+               trace_function(tr, ip, parent_ip, trace_ctx, NULL);
 #ifdef CONFIG_UNWINDER_FRAME_POINTER
                if (ftrace_pids_enabled(op))
                        skip++;
@@ -349,7 +382,7 @@ function_no_repeats_trace_call(unsigned long ip, unsigned long parent_ip,
        trace_ctx = tracing_gen_ctx_dec();
        process_repeats(tr, ip, parent_ip, last_info, trace_ctx);
 
-       trace_function(tr, ip, parent_ip, trace_ctx);
+       trace_function(tr, ip, parent_ip, trace_ctx, NULL);
 
 out:
        ftrace_test_recursion_unlock(bit);
@@ -389,7 +422,7 @@ function_stack_no_repeats_trace_call(unsigned long ip, unsigned long parent_ip,
                trace_ctx = tracing_gen_ctx_flags(flags);
                process_repeats(tr, ip, parent_ip, last_info, trace_ctx);
 
-               trace_function(tr, ip, parent_ip, trace_ctx);
+               trace_function(tr, ip, parent_ip, trace_ctx, NULL);
                __trace_stack(tr, trace_ctx, STACK_SKIP);
        }
 
@@ -403,6 +436,9 @@ static struct tracer_opt func_opts[] = {
        { TRACER_OPT(func_stack_trace, TRACE_FUNC_OPT_STACK) },
 #endif
        { TRACER_OPT(func-no-repeats, TRACE_FUNC_OPT_NO_REPEATS) },
+#ifdef CONFIG_FUNCTION_TRACE_ARGS
+       { TRACER_OPT(func-args, TRACE_FUNC_OPT_ARGS) },
+#endif
        { } /* Always set a last empty entry */
 };
 
index 7294ad676379a18e1ebd9c3883e5cbc235743908..c8bfa7310a91d282746ddd125d3f505b95ea16a6 100644 (file)
@@ -150,7 +150,7 @@ irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip,
 
        trace_ctx = tracing_gen_ctx_flags(flags);
 
-       trace_function(tr, ip, parent_ip, trace_ctx);
+       trace_function(tr, ip, parent_ip, trace_ctx, fregs);
 
        atomic_dec(&data->disabled);
 }
@@ -295,11 +295,17 @@ __trace_function(struct trace_array *tr,
        if (is_graph(tr))
                trace_graph_function(tr, ip, parent_ip, trace_ctx);
        else
-               trace_function(tr, ip, parent_ip, trace_ctx);
+               trace_function(tr, ip, parent_ip, trace_ctx, NULL);
 }
 
 #else
-#define __trace_function trace_function
+static inline void
+__trace_function(struct trace_array *tr,
+                unsigned long ip, unsigned long parent_ip,
+                unsigned int trace_ctx)
+{
+       return trace_function(tr, ip, parent_ip, trace_ctx, NULL);
+}
 
 static enum print_line_t irqsoff_print_line(struct trace_iterator *iter)
 {
index 4b721cd4f21dda3259b24fc6e7355d9da4f4ce71..b51ee937377342cb80d66c3493b5e394456f278f 100644 (file)
@@ -1090,12 +1090,15 @@ enum print_line_t trace_nop_print(struct trace_iterator *iter, int flags,
 }
 
 static void print_fn_trace(struct trace_seq *s, unsigned long ip,
-                          unsigned long parent_ip, long delta, int flags)
+                          unsigned long parent_ip, long delta,
+                          unsigned long *args, int flags)
 {
        ip += delta;
        parent_ip += delta;
 
        seq_print_ip_sym(s, ip, flags);
+       if (args)
+               print_function_args(s, args, ip);
 
        if ((flags & TRACE_ITER_PRINT_PARENT) && parent_ip) {
                trace_seq_puts(s, " <-");
@@ -1109,10 +1112,19 @@ static enum print_line_t trace_fn_trace(struct trace_iterator *iter, int flags,
 {
        struct ftrace_entry *field;
        struct trace_seq *s = &iter->seq;
+       unsigned long *args;
+       int args_size;
 
        trace_assign_type(field, iter->ent);
 
-       print_fn_trace(s, field->ip, field->parent_ip, iter->tr->text_delta, flags);
+       args_size = iter->ent_size - offsetof(struct ftrace_entry, args);
+       if (args_size >= FTRACE_REGS_MAX_ARGS * sizeof(long))
+               args = field->args;
+       else
+               args = NULL;
+
+       print_fn_trace(s, field->ip, field->parent_ip, iter->tr->text_delta,
+                      args, flags);
        trace_seq_putc(s, '\n');
 
        return trace_handle_return(s);
@@ -1785,7 +1797,7 @@ trace_func_repeats_print(struct trace_iterator *iter, int flags,
 
        trace_assign_type(field, iter->ent);
 
-       print_fn_trace(s, field->ip, field->parent_ip, iter->tr->text_delta, flags);
+       print_fn_trace(s, field->ip, field->parent_ip, iter->tr->text_delta, NULL, flags);
        trace_seq_printf(s, " (repeats: %u, last_ts:", field->count);
        trace_print_time(s, iter,
                         iter->ts - FUNC_REPEATS_GET_DELTA_TS(field));
index af30586f1aeacb0f7d8cf041bc51c6bfb77d604a..c9ba4259e03e9162cd66fc280180a03a63b14ad8 100644 (file)
@@ -242,7 +242,7 @@ wakeup_tracer_call(unsigned long ip, unsigned long parent_ip,
                return;
 
        local_irq_save(flags);
-       trace_function(tr, ip, parent_ip, trace_ctx);
+       trace_function(tr, ip, parent_ip, trace_ctx, fregs);
        local_irq_restore(flags);
 
        atomic_dec(&data->disabled);
@@ -327,7 +327,7 @@ __trace_function(struct trace_array *tr,
        if (is_graph(tr))
                trace_graph_function(tr, ip, parent_ip, trace_ctx);
        else
-               trace_function(tr, ip, parent_ip, trace_ctx);
+               trace_function(tr, ip, parent_ip, trace_ctx, NULL);
 }
 
 static int wakeup_flag_changed(struct trace_array *tr, u32 mask, int set)