[PATCH v3 1/2] fgraph: Enhance funcgraph-retval with BTF-based type-aware output

Donglin Peng posted 2 patches 1 week, 2 days ago
There is a newer version of this series
[PATCH v3 1/2] fgraph: Enhance funcgraph-retval with BTF-based type-aware output
Posted by Donglin Peng 1 week, 2 days ago
From: pengdonglin <pengdonglin@xiaomi.com>

The current funcgraph-retval implementation suffers from two accuracy
issues:

1. Void-returning functions still print a return value, creating
   misleading noise in the trace output.

2. For functions returning narrower types (e.g., char, short), the
   displayed value can be incorrect because high bits of the register
   may contain undefined data.

This patch addresses both problems by leveraging BTF to obtain the exact
return type of each traced kernel function. The key changes are:

1. Void function filtering: Functions with void return type no longer
   display any return value in the trace output, eliminating unnecessary
   clutter.

2. Type-aware value formatting: The return value is now properly truncated
   to match the actual width of the return type before being displayed.
   Additionally, the value is formatted according to its type for better
   human readability.

Here is an output comparison:

Before:
 # perf ftrace -G vfs_read --graph-opts retval
 ...
 1)               |   touch_atime() {
 1)               |     atime_needs_update() {
 1)   0.069 us    |       make_vfsuid(); /* ret=0x0 */
 1)   0.067 us    |       make_vfsgid(); /* ret=0x0 */
 1)               |       current_time() {
 1)   0.197 us    |         ktime_get_coarse_real_ts64_mg(); /* ret=0x187f886aec3ed6f5 */
 1)   0.352 us    |       } /* current_time ret=0x69380753 */
 1)   0.792 us    |     } /* atime_needs_update ret=0x0 */
 1)   0.937 us    |   } /* touch_atime ret=0x0 */

After:
 # perf ftrace -G vfs_read --graph-opts retval
 ...
 2)               |   touch_atime() {
 2)               |     atime_needs_update() {
 2)   0.070 us    |       make_vfsuid(); /* ret=0x0 */
 2)   0.070 us    |       make_vfsgid(); /* ret=0x0 */
 2)               |       current_time() {
 2)   0.162 us    |         ktime_get_coarse_real_ts64_mg();
 2)   0.312 us    |       } /* current_time ret=0x69380649(trunc) */
 2)   0.753 us    |     } /* atime_needs_update ret=false */
 2)   0.899 us    |   } /* touch_atime */

Cc: Steven Rostedt (Google) <rostedt@goodmis.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
---
 kernel/trace/trace_functions_graph.c | 124 ++++++++++++++++++++++++---
 1 file changed, 111 insertions(+), 13 deletions(-)

diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index 17c75cf2348e..46b66b1cfc16 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -15,6 +15,7 @@
 
 #include "trace.h"
 #include "trace_output.h"
+#include "trace_btf.h"
 
 /* When set, irq functions might be ignored */
 static int ftrace_graph_skip_irqs;
@@ -120,6 +121,13 @@ enum {
 	FLAGS_FILL_END   = 3 << TRACE_GRAPH_PRINT_FILL_SHIFT,
 };
 
+enum {
+	RETVAL_FMT_HEX   = BIT(0),
+	RETVAL_FMT_DEC   = BIT(1),
+	RETVAL_FMT_BOOL  = BIT(2),
+	RETVAL_FMT_TRUNC = BIT(3),
+};
+
 static void
 print_graph_duration(struct trace_array *tr, unsigned long long duration,
 		     struct trace_seq *s, u32 flags);
@@ -865,6 +873,73 @@ static void print_graph_retaddr(struct trace_seq *s, struct fgraph_retaddr_ent_e
 
 #if defined(CONFIG_FUNCTION_GRAPH_RETVAL) || defined(CONFIG_FUNCTION_GRAPH_RETADDR)
 
+static void trim_retval(unsigned long func, unsigned long *retval, bool *print_retval,
+			int *fmt)
+{
+	const struct btf_type *t;
+	char name[KSYM_NAME_LEN];
+	struct btf *btf;
+	u32 v, msb;
+	int kind;
+
+	if (!IS_ENABLED(CONFIG_DEBUG_INFO_BTF))
+		return;
+
+	if (lookup_symbol_name(func, name))
+		return;
+
+	t = btf_find_func_proto(name, &btf);
+	if (IS_ERR_OR_NULL(t))
+		return;
+
+	t = btf_type_skip_modifiers(btf, t->type, NULL);
+	kind = t ? BTF_INFO_KIND(t->info) : BTF_KIND_UNKN;
+	switch (kind) {
+	case BTF_KIND_UNKN:
+		*print_retval = false;
+		break;
+	case BTF_KIND_STRUCT:
+	case BTF_KIND_UNION:
+	case BTF_KIND_ENUM:
+	case BTF_KIND_ENUM64:
+		if (kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION)
+			*fmt = RETVAL_FMT_HEX;
+		else
+			*fmt = RETVAL_FMT_DEC;
+
+		if (t->size > sizeof(unsigned long)) {
+			*fmt |= RETVAL_FMT_TRUNC;
+		} else {
+			msb = BITS_PER_BYTE * t->size - 1;
+			*retval &= GENMASK(msb, 0);
+		}
+		break;
+	case BTF_KIND_INT:
+		v = *(u32 *)(t + 1);
+		if (BTF_INT_ENCODING(v) == BTF_INT_BOOL) {
+			*fmt = RETVAL_FMT_BOOL;
+			msb = 0;
+		} else {
+			if (BTF_INT_ENCODING(v) == BTF_INT_SIGNED)
+				*fmt = RETVAL_FMT_DEC;
+			else
+				*fmt = RETVAL_FMT_HEX;
+
+			if (t->size > sizeof(unsigned long)) {
+				*fmt |= RETVAL_FMT_TRUNC;
+				msb = BITS_PER_LONG - 1;
+			} else {
+				msb = BTF_INT_BITS(v) - 1;
+			}
+		}
+		*retval &= GENMASK(msb, 0);
+		break;
+	default:
+		*fmt = RETVAL_FMT_HEX;
+		break;
+	}
+}
+
 static void print_graph_retval(struct trace_seq *s, struct ftrace_graph_ent_entry *entry,
 				struct ftrace_graph_ret *graph_ret, void *func,
 				u32 opt_flags, u32 trace_flags, int args_size)
@@ -873,7 +948,7 @@ static void print_graph_retval(struct trace_seq *s, struct ftrace_graph_ent_entr
 	unsigned long retval = 0;
 	bool print_retaddr = false;
 	bool print_retval = false;
-	bool hex_format = !!(opt_flags & TRACE_GRAPH_PRINT_RETVAL_HEX);
+	int retval_fmt = 0;
 
 #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
 	retval = graph_ret->retval;
@@ -884,17 +959,35 @@ static void print_graph_retval(struct trace_seq *s, struct ftrace_graph_ent_entr
 	print_retaddr = !!(opt_flags & TRACE_GRAPH_PRINT_RETADDR);
 #endif
 
-	if (print_retval && retval && !hex_format) {
-		/* Check if the return value matches the negative format */
-		if (IS_ENABLED(CONFIG_64BIT) && (retval & BIT(31)) &&
-			(((u64)retval) >> 32) == 0) {
-			err_code = sign_extend64(retval, 31);
-		} else {
-			err_code = retval;
+	if (print_retval) {
+		int fmt = RETVAL_FMT_HEX;
+
+		trim_retval((unsigned long)func, &retval, &print_retval, &fmt);
+		if (print_retval) {
+			if (opt_flags & TRACE_GRAPH_PRINT_RETVAL_HEX)
+				retval_fmt = RETVAL_FMT_HEX;
+
+			if (retval && retval_fmt != RETVAL_FMT_HEX) {
+				/* Check if the return value matches the negative format */
+				if (IS_ENABLED(CONFIG_64BIT) && (retval & BIT(31)) &&
+					(((u64)retval) >> 32) == 0) {
+					err_code = sign_extend64(retval, 31);
+				} else {
+					err_code = retval;
+				}
+
+				if (!IS_ERR_VALUE(err_code))
+					err_code = 0;
+			}
+
+			if (retval_fmt == RETVAL_FMT_HEX) {
+				retval_fmt |= (fmt & RETVAL_FMT_TRUNC);
+			} else {
+				if (err_code && fmt & RETVAL_FMT_HEX)
+					fmt = (fmt & ~RETVAL_FMT_HEX) | RETVAL_FMT_DEC;
+				retval_fmt = fmt;
+			}
 		}
-
-		if (!IS_ERR_VALUE(err_code))
-			err_code = 0;
 	}
 
 	if (entry) {
@@ -921,10 +1014,15 @@ static void print_graph_retval(struct trace_seq *s, struct ftrace_graph_ent_entr
 				    trace_flags, false);
 
 	if (print_retval) {
-		if (hex_format || (err_code == 0))
+		if (retval_fmt & RETVAL_FMT_HEX)
 			trace_seq_printf(s, " ret=0x%lx", retval);
+		else if (retval_fmt & RETVAL_FMT_BOOL)
+			trace_seq_printf(s, " ret=%s", retval ? "true" : "false");
 		else
-			trace_seq_printf(s, " ret=%ld", err_code);
+			trace_seq_printf(s, " ret=%ld", err_code ?: retval);
+
+		if (retval_fmt & RETVAL_FMT_TRUNC)
+			trace_seq_printf(s, "(trunc)");
 	}
 
 	if (!entry || print_retval || print_retaddr)
-- 
2.34.1
Re: [PATCH v3 1/2] fgraph: Enhance funcgraph-retval with BTF-based type-aware output
Posted by kernel test robot 5 days, 8 hours ago
Hi Donglin,

kernel test robot noticed the following build errors:

[auto build test ERROR on trace/for-next]
[also build test ERROR on linus/master v6.18 next-20251212]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Donglin-Peng/fgraph-Enhance-funcgraph-retval-with-BTF-based-type-aware-output/20251209-201633
base:   https://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace for-next
patch link:    https://lore.kernel.org/r/20251209121349.525641-2-dolinux.peng%40gmail.com
patch subject: [PATCH v3 1/2] fgraph: Enhance funcgraph-retval with BTF-based type-aware output
config: arm-randconfig-002-20251214 (https://download.01.org/0day-ci/archive/20251214/202512140850.JdD1lPmn-lkp@intel.com/config)
compiler: arm-linux-gnueabi-gcc (GCC) 10.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251214/202512140850.JdD1lPmn-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202512140850.JdD1lPmn-lkp@intel.com/

All errors (new ones prefixed by >>):

   arm-linux-gnueabi-ld: kernel/trace/trace_functions_graph.o: in function `trim_retval':
>> kernel/trace/trace_functions_graph.c:888: undefined reference to `btf_find_func_proto'


vim +888 kernel/trace/trace_functions_graph.c

   872	
   873	static void trim_retval(unsigned long func, unsigned long *retval, bool *print_retval,
   874				int *fmt)
   875	{
   876		const struct btf_type *t;
   877		char name[KSYM_NAME_LEN];
   878		struct btf *btf;
   879		u32 v, msb;
   880		int kind;
   881	
   882		if (!IS_ENABLED(CONFIG_DEBUG_INFO_BTF))
   883			return;
   884	
   885		if (lookup_symbol_name(func, name))
   886			return;
   887	
 > 888		t = btf_find_func_proto(name, &btf);
   889		if (IS_ERR_OR_NULL(t))
   890			return;
   891	
   892		t = btf_type_skip_modifiers(btf, t->type, NULL);
   893		kind = t ? BTF_INFO_KIND(t->info) : BTF_KIND_UNKN;
   894		switch (kind) {
   895		case BTF_KIND_UNKN:
   896			*print_retval = false;
   897			break;
   898		case BTF_KIND_STRUCT:
   899		case BTF_KIND_UNION:
   900		case BTF_KIND_ENUM:
   901		case BTF_KIND_ENUM64:
   902			if (kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION)
   903				*fmt = RETVAL_FMT_HEX;
   904			else
   905				*fmt = RETVAL_FMT_DEC;
   906	
   907			if (t->size > sizeof(unsigned long)) {
   908				*fmt |= RETVAL_FMT_TRUNC;
   909			} else {
   910				msb = BITS_PER_BYTE * t->size - 1;
   911				*retval &= GENMASK(msb, 0);
   912			}
   913			break;
   914		case BTF_KIND_INT:
   915			v = *(u32 *)(t + 1);
   916			if (BTF_INT_ENCODING(v) == BTF_INT_BOOL) {
   917				*fmt = RETVAL_FMT_BOOL;
   918				msb = 0;
   919			} else {
   920				if (BTF_INT_ENCODING(v) == BTF_INT_SIGNED)
   921					*fmt = RETVAL_FMT_DEC;
   922				else
   923					*fmt = RETVAL_FMT_HEX;
   924	
   925				if (t->size > sizeof(unsigned long)) {
   926					*fmt |= RETVAL_FMT_TRUNC;
   927					msb = BITS_PER_LONG - 1;
   928				} else {
   929					msb = BTF_INT_BITS(v) - 1;
   930				}
   931			}
   932			*retval &= GENMASK(msb, 0);
   933			break;
   934		default:
   935			*fmt = RETVAL_FMT_HEX;
   936			break;
   937		}
   938	}
   939	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v3 1/2] fgraph: Enhance funcgraph-retval with BTF-based type-aware output
Posted by Donglin Peng 4 days, 4 hours ago
On Sun, Dec 14, 2025 at 9:15 AM kernel test robot <lkp@intel.com> wrote:
>
> Hi Donglin,
>
> kernel test robot noticed the following build errors:
>
> [auto build test ERROR on trace/for-next]
> [also build test ERROR on linus/master v6.18 next-20251212]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url:    https://github.com/intel-lab-lkp/linux/commits/Donglin-Peng/fgraph-Enhance-funcgraph-retval-with-BTF-based-type-aware-output/20251209-201633
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace for-next
> patch link:    https://lore.kernel.org/r/20251209121349.525641-2-dolinux.peng%40gmail.com
> patch subject: [PATCH v3 1/2] fgraph: Enhance funcgraph-retval with BTF-based type-aware output
> config: arm-randconfig-002-20251214 (https://download.01.org/0day-ci/archive/20251214/202512140850.JdD1lPmn-lkp@intel.com/config)
> compiler: arm-linux-gnueabi-gcc (GCC) 10.5.0
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251214/202512140850.JdD1lPmn-lkp@intel.com/reproduce)
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202512140850.JdD1lPmn-lkp@intel.com/
>
> All errors (new ones prefixed by >>):
>
>    arm-linux-gnueabi-ld: kernel/trace/trace_functions_graph.o: in function `trim_retval':
> >> kernel/trace/trace_functions_graph.c:888: undefined reference to `btf_find_func_proto'

Thanks. I will address this in the next version. The issue occurs because
CONFIG_FUNCTION_GRAPH_RETVAL and CONFIG_DEBUG_INFO_BTF
are enabled, but CONFIG_PROBE_EVENTS_BTF_ARGS is disabled.
This prevents trace_btf.c from being compiled, while the function
btf_find_func_proto
it provides is still required.

Thanks,
Donglin
>
>
> vim +888 kernel/trace/trace_functions_graph.c
>
>    872
>    873  static void trim_retval(unsigned long func, unsigned long *retval, bool *print_retval,
>    874                          int *fmt)
>    875  {
>    876          const struct btf_type *t;
>    877          char name[KSYM_NAME_LEN];
>    878          struct btf *btf;
>    879          u32 v, msb;
>    880          int kind;
>    881
>    882          if (!IS_ENABLED(CONFIG_DEBUG_INFO_BTF))
>    883                  return;
>    884
>    885          if (lookup_symbol_name(func, name))
>    886                  return;
>    887
>  > 888          t = btf_find_func_proto(name, &btf);
>    889          if (IS_ERR_OR_NULL(t))
>    890                  return;
>    891
>    892          t = btf_type_skip_modifiers(btf, t->type, NULL);
>    893          kind = t ? BTF_INFO_KIND(t->info) : BTF_KIND_UNKN;
>    894          switch (kind) {
>    895          case BTF_KIND_UNKN:
>    896                  *print_retval = false;
>    897                  break;
>    898          case BTF_KIND_STRUCT:
>    899          case BTF_KIND_UNION:
>    900          case BTF_KIND_ENUM:
>    901          case BTF_KIND_ENUM64:
>    902                  if (kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION)
>    903                          *fmt = RETVAL_FMT_HEX;
>    904                  else
>    905                          *fmt = RETVAL_FMT_DEC;
>    906
>    907                  if (t->size > sizeof(unsigned long)) {
>    908                          *fmt |= RETVAL_FMT_TRUNC;
>    909                  } else {
>    910                          msb = BITS_PER_BYTE * t->size - 1;
>    911                          *retval &= GENMASK(msb, 0);
>    912                  }
>    913                  break;
>    914          case BTF_KIND_INT:
>    915                  v = *(u32 *)(t + 1);
>    916                  if (BTF_INT_ENCODING(v) == BTF_INT_BOOL) {
>    917                          *fmt = RETVAL_FMT_BOOL;
>    918                          msb = 0;
>    919                  } else {
>    920                          if (BTF_INT_ENCODING(v) == BTF_INT_SIGNED)
>    921                                  *fmt = RETVAL_FMT_DEC;
>    922                          else
>    923                                  *fmt = RETVAL_FMT_HEX;
>    924
>    925                          if (t->size > sizeof(unsigned long)) {
>    926                                  *fmt |= RETVAL_FMT_TRUNC;
>    927                                  msb = BITS_PER_LONG - 1;
>    928                          } else {
>    929                                  msb = BTF_INT_BITS(v) - 1;
>    930                          }
>    931                  }
>    932                  *retval &= GENMASK(msb, 0);
>    933                  break;
>    934          default:
>    935                  *fmt = RETVAL_FMT_HEX;
>    936                  break;
>    937          }
>    938  }
>    939
>
> --
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests/wiki