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:
committed by
Steven Rostedt (Google)
parent
54c53dfdb6
commit
88cefd99ee
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user