[PATCH] libtracecmd: Add support to display parent function in function graph

Steven Rostedt <[email protected]> Tue, 20 Jan 2026 11:55:40 -0500
Newsgroups org.kernel.vger.linux-trace-devel
Message-ID <[email protected]>
From: "Steven Rostedt (Google)" <[email protected]>

If the trace was recorded with the function graph option: funcgraph-retaddr
The function trace includes the parent function in the address space.
Add support to show the parent function in a /* <-[parent] */ format.

 fgraph_retaddr_entry:              |  irq_enter_rcu() /* <-sysvec_call_function_single */ {
 fgraph_retaddr_entry:   0.748 us   |    preempt_count_add(val=65536); /* <-irq_enter_rcu */ (ret=0x10001)
 fgraph_retaddr_entry:              |    tick_irq_enter() /* <-irq_enter_rcu */ {
 fgraph_retaddr_entry:   0.524 us   |      tick_check_oneshot_broadcast_this_cpu(); /* <-tick_irq_enter */ (ret=0x6)
 fgraph_retaddr_entry:   2.030 us   |      ktime_get(); /* <-tick_irq_enter */ (ret=0x4695e53ad9c)
 fgraph_retaddr_entry:              |      tick_nohz_stop_idle(ts=0xffff90273df22610, now=4850600619420) /* <-tick_irq_enter */ {
 fgraph_retaddr_entry:   0.437 us   |        nr_iowait_cpu(cpu=6); /* <-tick_nohz_stop_idle */ (ret=0x0)
 funcgraph_exit:         2.043 us   |      } (ret=0xb882194c0316dc00)
 funcgraph_exit:         6.764 us   |    } (ret=0x31)
 funcgraph_exit:       + 11.033 us  |  } (ret=0x31)

Signed-off-by: Steven Rostedt (Google) <[email protected]>
---
 lib/trace-cmd/trace-ftrace.c | 30 +++++++++++++++++++++++++++---
 1 file changed, 27 insertions(+), 3 deletions(-)

diff --git a/lib/trace-cmd/trace-ftrace.c b/lib/trace-cmd/trace-ftrace.c
index c61dd543..f52b203c 100644
--- a/lib/trace-cmd/trace-ftrace.c
+++ b/lib/trace-cmd/trace-ftrace.c
@@ -253,6 +253,7 @@ print_graph_entry_leaf(struct trace_seq *s,
 	unsigned long long val;
 	unsigned long long retval;
 	bool fgraph_retval_supported = true;
+	const char *retfunc = NULL;
 	const char *func;
 	int ret;
 	int i;
@@ -270,6 +271,10 @@ print_graph_entry_leaf(struct trace_seq *s,
 			return trace_seq_putc(s, '!');
 	}
 
+	/* In case this is a retaddr event */
+	if (!tep_get_field_val(s, event, "retaddr", record, &val, 1))
+		retfunc = tep_find_function(pevent, val);
+
 	duration = rettime - calltime;
 
 	/* Overhead */
@@ -299,6 +304,9 @@ print_graph_entry_leaf(struct trace_seq *s,
 	if (ret && fgraph_depth->set)
 		ret = trace_seq_printf(s, " (%lld)", depth);
 
+	if (retfunc)
+		ret = trace_seq_printf(s, " /* <-%s */", retfunc);
+
 	/* Return Value */
 	if (ret && fgraph_retval_supported && !fgraph_retval_skip->set) {
 		if (fgraph_retval_dec->set) {
@@ -324,6 +332,7 @@ static int print_graph_nested(struct trace_seq *s,
 	struct tep_handle *pevent = event->tep;
 	unsigned long long depth;
 	unsigned long long val;
+	const char *retfunc = NULL;
 	const char *func;
 	int ret;
 	int i;
@@ -337,6 +346,10 @@ static int print_graph_nested(struct trace_seq *s,
 	if (tep_get_field_val(s, event, "depth", record, &depth, 1))
 		return trace_seq_putc(s, '!');
 
+	/* In case this is a retaddr event */
+	if (!tep_get_field_val(s, event, "retaddr", record, &val, 1))
+		retfunc = tep_find_function(pevent, val);
+
 	/* Function */
 	for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
 		trace_seq_putc(s, ' ');
@@ -346,12 +359,20 @@ static int print_graph_nested(struct trace_seq *s,
 
 	func = tep_find_function(pevent, val);
 
+
 	if (func) {
 		ret = trace_seq_printf(s, "%s(", func);
 		print_args(s, event, record, func);
-		ret = trace_seq_puts(s, ") {");
-	} else
-		ret = trace_seq_printf(s, "%llx() {", val);
+		if (retfunc)
+			ret = trace_seq_printf(s, ") /* <-%s */ {", retfunc);
+		else
+			ret = trace_seq_puts(s, ") {");
+	} else {
+		if (retfunc)
+			ret = trace_seq_printf(s, "%llx() /* <-%s */ {", val, retfunc);
+		else
+			ret = trace_seq_puts(s, ") {");
+	}
 
 	if (ret && fgraph_depth->set)
 		ret = trace_seq_printf(s, " (%lld)", depth);
@@ -504,6 +525,9 @@ int tracecmd_ftrace_overrides(struct tracecmd_input *handle,
 	tep_register_event_handler(pevent, -1, "ftrace", "funcgraph_entry",
 				      fgraph_ent_handler, finfo);
 
+	tep_register_event_handler(pevent, -1, "ftrace", "fgraph_retaddr_entry",
+				      fgraph_ent_handler, finfo);
+
 	tep_register_event_handler(pevent, -1, "ftrace", "funcgraph_exit",
 				      fgraph_ret_handler, finfo);
 
-- 
2.50.1