[PATCH] libtracecmd: Print call offset in parent function for funcgraph-retaddr

Donglin Peng <[email protected]> Mon, 9 Feb 2026 12:35:30 +0800
Newsgroups org.kernel.vger.linux-trace-devel
Message-ID <[email protected]>
From: Donglin Peng <[email protected]>

When the "funcgraph-retaddr" option is enabled, trace-cmd currently
prints only the parent function name using the return address.

Enhance it to include the call offset within the parent function,
enabling tools like faddr2line to locate the exact call site.

Before:
  preempt_count_add(val=65536) /* <-irq_enter_rcu */

After:
  preempt_count_add(val=65536) /* <-irq_enter_rcu+0x17 */

Example usage with faddr2line:
  $ ./scripts/faddr2line vmlinux irq_enter_rcu+0x17
  irq_enter_rcu+0x17/0x80:
  irq_enter_rcu at kernel/softirq.c:664

Signed-off-by: Donglin Peng <[email protected]>
---
 lib/trace-cmd/trace-ftrace.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/lib/trace-cmd/trace-ftrace.c b/lib/trace-cmd/trace-ftrace.c
index 53c958653c28..069ed51bf3fe 100644
--- a/lib/trace-cmd/trace-ftrace.c
+++ b/lib/trace-cmd/trace-ftrace.c
@@ -237,6 +237,7 @@ print_graph_entry_leaf(struct trace_seq *s,
 	unsigned long long duration, depth;
 	unsigned long long val;
 	unsigned long long retval;
+	unsigned long long offset;
 	bool fgraph_retval_supported = true;
 	const char *retfunc = NULL;
 	const char *func;
@@ -257,8 +258,10 @@ print_graph_entry_leaf(struct trace_seq *s,
 	}
 
 	/* In case this is a retaddr event */
-	if (!tep_get_field_val(s, event, "retaddr", record, &val, 0))
+	if (!tep_get_field_val(s, event, "retaddr", record, &val, 0)) {
 		retfunc = tep_find_function(pevent, val);
+		offset = val - tep_find_function_address(pevent, val);
+	}
 
 	duration = rettime - calltime;
 
@@ -290,7 +293,7 @@ print_graph_entry_leaf(struct trace_seq *s,
 		ret = trace_seq_printf(s, " (%lld)", depth);
 
 	if (retfunc)
-		ret = trace_seq_printf(s, " /* <-%s */", retfunc);
+		ret = trace_seq_printf(s, " /* <-%s+0x%x */", retfunc, (int)offset);
 
 	/* Return Value */
 	if (ret && fgraph_retval_supported && !fgraph_retval_skip->set) {
@@ -317,6 +320,7 @@ static int print_graph_nested(struct trace_seq *s,
 	struct tep_handle *pevent = event->tep;
 	unsigned long long depth;
 	unsigned long long val;
+	unsigned long long offset;
 	const char *retfunc = NULL;
 	const char *func;
 	int ret;
@@ -332,8 +336,10 @@ static int print_graph_nested(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, 0))
+	if (!tep_get_field_val(s, event, "retaddr", record, &val, 0)) {
 		retfunc = tep_find_function(pevent, val);
+		offset = val - tep_find_function_address(pevent, val);
+	}
 
 	/* Function */
 	for (i = 0; i < (int)(depth * TRACE_GRAPH_INDENT); i++)
@@ -344,17 +350,18 @@ 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);
 		if (retfunc)
-			ret = trace_seq_printf(s, ") /* <-%s */ {", retfunc);
+			ret = trace_seq_printf(s, ") /* <-%s+0x%x */ {", retfunc,
+					       (int)offset);
 		else
 			ret = trace_seq_puts(s, ") {");
 	} else {
 		if (retfunc)
-			ret = trace_seq_printf(s, "%llx() /* <-%s */ {", val, retfunc);
+			ret = trace_seq_printf(s, "%llx() /* <-%s+0x%x */ {", val,
+					       retfunc, (int)offset);
 		else
 			ret = trace_seq_puts(s, ") {");
 	}
-- 
2.34.1