ftrace: Show subops in enabled_functions
authorSteven Rostedt <rostedt@goodmis.org>
Thu, 10 Apr 2025 19:38:30 +0000 (15:38 -0400)
committerSteven Rostedt (Google) <rostedt@goodmis.org>
Thu, 8 May 2025 13:36:08 +0000 (09:36 -0400)
The function graph infrastructure uses subops of the function tracer.
These are not shown in enabled_functions. Add a "subops:" section to the
enabled_functions line to show what functions are attached via subops. If
the subops is from the function_graph infrastructure, then show the entry
and return callbacks that are attached.

Here's an example of the output:

schedule_on_each_cpu (1)                tramp: 0xffffffffc03ef000 (ftrace_graph_func+0x0/0x60) ->ftrace_graph_func+0x0/0x60     subops: {ent:trace_graph_entry+0x0/0x20 ret:trace_graph_return+0x0/0x150}

Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Link: https://lore.kernel.org/20250410153830.5d97f108@gandalf.local.home
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
include/linux/ftrace.h
kernel/trace/fgraph.c
kernel/trace/ftrace.c

index fbabc3d848b375a114936b635bc695d59ab018a6..fc939ca2ff662aefa95d709e145e1f19323a6f5f 100644 (file)
@@ -328,6 +328,7 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
  * DIRECT - Used by the direct ftrace_ops helper for direct functions
  *            (internal ftrace only, should not be used by others)
  * SUBOP  - Is controlled by another op in field managed.
+ * GRAPH  - Is a component of the fgraph_ops structure
  */
 enum {
        FTRACE_OPS_FL_ENABLED                   = BIT(0),
@@ -349,6 +350,7 @@ enum {
        FTRACE_OPS_FL_PERMANENT                 = BIT(16),
        FTRACE_OPS_FL_DIRECT                    = BIT(17),
        FTRACE_OPS_FL_SUBOP                     = BIT(18),
+       FTRACE_OPS_FL_GRAPH                     = BIT(19),
 };
 
 #ifndef CONFIG_DYNAMIC_FTRACE_WITH_ARGS
index 8d925cbdce3a51f3fddc59873e72aae6a1d218c7..c5b207992fb49e369ae7f6777952067899256729 100644 (file)
@@ -1382,6 +1382,8 @@ int register_ftrace_graph(struct fgraph_ops *gops)
        /* Always save the function, and reset at unregistering */
        gops->saved_func = gops->entryfunc;
 
+       gops->ops.flags |= FTRACE_OPS_FL_GRAPH;
+
        ret = ftrace_startup_subops(&graph_ops, &gops->ops, command);
        if (!ret)
                fgraph_array[i] = gops;
index 6981830c3128595164bfeb5059ca6160a0697638..014cd2cedae35f79769a49ff3901dd6c5b43994b 100644 (file)
@@ -4373,6 +4373,42 @@ static inline int print_rec(struct seq_file *m, unsigned long ip)
 }
 #endif
 
+static void print_subops(struct seq_file *m, struct ftrace_ops *ops, struct dyn_ftrace *rec)
+{
+       struct ftrace_ops *subops;
+       bool first = true;
+
+       list_for_each_entry(subops, &ops->subop_list, list) {
+               if (!((subops->flags & FTRACE_OPS_FL_ENABLED) &&
+                     hash_contains_ip(rec->ip, subops->func_hash)))
+                       continue;
+               if (first) {
+                       seq_printf(m, "\tsubops:");
+                       first = false;
+               }
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+               if (subops->flags & FTRACE_OPS_FL_GRAPH) {
+                       struct fgraph_ops *gops;
+
+                       gops = container_of(subops, struct fgraph_ops, ops);
+                       seq_printf(m, " {ent:%pS ret:%pS}",
+                                  (void *)gops->entryfunc,
+                                  (void *)gops->retfunc);
+                       continue;
+               }
+#endif
+               if (subops->trampoline) {
+                       seq_printf(m, " {%pS (%pS)}",
+                                  (void *)subops->trampoline,
+                                  (void *)subops->func);
+                       add_trampoline_func(m, subops, rec);
+               } else {
+                       seq_printf(m, " {%pS}",
+                                  (void *)subops->func);
+               }
+       }
+}
+
 static int t_show(struct seq_file *m, void *v)
 {
        struct ftrace_iterator *iter = m->private;
@@ -4425,6 +4461,7 @@ static int t_show(struct seq_file *m, void *v)
                                                   (void *)ops->trampoline,
                                                   (void *)ops->func);
                                        add_trampoline_func(m, ops, rec);
+                                       print_subops(m, ops, rec);
                                        ops = ftrace_find_tramp_ops_next(rec, ops);
                                } while (ops);
                        } else
@@ -4437,6 +4474,7 @@ static int t_show(struct seq_file *m, void *v)
                        if (ops) {
                                seq_printf(m, "\tops: %pS (%pS)",
                                           ops, ops->func);
+                               print_subops(m, ops, rec);
                        } else {
                                seq_puts(m, "\tops: ERROR!");
                        }