Commit 88cefd99 authored by Steven Rostedt's avatar Steven Rostedt Committed by Steven Rostedt (Google)
Browse files

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: default avatarSteven Rostedt (Google) <rostedt@goodmis.org>
parent 54c53dfd
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
@@ -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!");
			}