ftrace: Show subops in enabled_functions

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>
This commit is contained in:
Steven Rostedt
2025-04-10 15:38:30 -04:00
committed by Steven Rostedt (Google)
parent 54c53dfdb6
commit 88cefd99ee
3 changed files with 42 additions and 0 deletions
+2
View 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
+2
View 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;
+38
View 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!");
}