Skip to content

Commit 21e9280

Browse files
Donglin Pengrostedt
authored andcommitted
function_graph: Support recording and printing the function return address
When using function_graph tracer to analyze the flow of kernel function execution, it is often necessary to quickly locate the exact line of code where the call occurs. While this may be easy at times, it can be more time-consuming when some functions are inlined or the flow is too long. This feature aims to simplify the process by recording the return address of traced funcions and printing it when outputing trace logs. To enhance human readability, the prefix 'ret=' is used for the kernel return value, while '<-' serves as the prefix for the return address in trace logs to make it look more like the function tracer. A new trace option named 'funcgraph-retaddr' has been introduced, and the existing option 'sym-addr' can be used to control the format of the return address. See below logs with both funcgraph-retval and funcgraph-retaddr enabled. 0) | load_elf_binary() { /* <-bprm_execve+0x249/0x600 */ 0) | load_elf_phdrs() { /* <-load_elf_binary+0x84/0x1730 */ 0) | __kmalloc_noprof() { /* <-load_elf_phdrs+0x4a/0xb0 */ 0) 3.657 us | __cond_resched(); /* <-__kmalloc_noprof+0x28c/0x390 ret=0x0 */ 0) + 24.335 us | } /* __kmalloc_noprof ret=0xffff8882007f3000 */ 0) | kernel_read() { /* <-load_elf_phdrs+0x6c/0xb0 */ 0) | rw_verify_area() { /* <-kernel_read+0x2b/0x50 */ 0) | security_file_permission() { /* <-kernel_read+0x2b/0x50 */ 0) | selinux_file_permission() { /* <-security_file_permission+0x26/0x40 */ 0) | __inode_security_revalidate() { /* <-selinux_file_permission+0x6d/0x140 */ 0) 2.034 us | __cond_resched(); /* <-__inode_security_revalidate+0x5f/0x80 ret=0x0 */ 0) 6.602 us | } /* __inode_security_revalidate ret=0x0 */ 0) 2.214 us | avc_policy_seqno(); /* <-selinux_file_permission+0x107/0x140 ret=0x0 */ 0) + 16.670 us | } /* selinux_file_permission ret=0x0 */ 0) + 20.809 us | } /* security_file_permission ret=0x0 */ 0) + 25.217 us | } /* rw_verify_area ret=0x0 */ 0) | __kernel_read() { /* <-load_elf_phdrs+0x6c/0xb0 */ 0) | ext4_file_read_iter() { /* <-__kernel_read+0x160/0x2e0 */ Then, we can use the faddr2line to locate the source code, for example: $ ./scripts/faddr2line ./vmlinux load_elf_phdrs+0x6c/0xb0 load_elf_phdrs+0x6c/0xb0: elf_read at fs/binfmt_elf.c:471 (inlined by) load_elf_phdrs at fs/binfmt_elf.c:531 Link: https://lore.kernel.org/[email protected] Reported-by: kernel test robot <[email protected]> Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/ Signed-off-by: Donglin Peng <[email protected]> [ Rebased to handle text_delta offsets ] Signed-off-by: Steven Rostedt (Google) <[email protected]>
1 parent f1f36e2 commit 21e9280

File tree

11 files changed

+274
-61
lines changed

11 files changed

+274
-61
lines changed

include/linux/ftrace.h

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,17 @@ struct ftrace_graph_ent {
10401040
int depth;
10411041
} __packed;
10421042

1043+
/*
1044+
* Structure that defines an entry function trace with retaddr.
1045+
* It's already packed but the attribute "packed" is needed
1046+
* to remove extra padding at the end.
1047+
*/
1048+
struct fgraph_retaddr_ent {
1049+
unsigned long func; /* Current function */
1050+
int depth;
1051+
unsigned long retaddr; /* Return address */
1052+
} __packed;
1053+
10431054
/*
10441055
* Structure that defines a return function trace.
10451056
* It's already packed but the attribute "packed" is needed
@@ -1057,19 +1068,29 @@ struct ftrace_graph_ret {
10571068
unsigned long long rettime;
10581069
} __packed;
10591070

1071+
struct fgraph_extras;
10601072
struct fgraph_ops;
10611073

10621074
/* Type of the callback handlers for tracing function graph*/
10631075
typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *,
10641076
struct fgraph_ops *); /* return */
10651077
typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
1066-
struct fgraph_ops *); /* entry */
1078+
struct fgraph_ops *,
1079+
struct fgraph_extras *); /* entry */
10671080

1068-
extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
1081+
extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
1082+
struct fgraph_ops *gops,
1083+
struct fgraph_extras *extras);
10691084
bool ftrace_pids_enabled(struct ftrace_ops *ops);
10701085

10711086
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
10721087

1088+
/* Used to convey some extra datas when creating a graph entry */
1089+
struct fgraph_extras {
1090+
u32 flags;
1091+
unsigned long retaddr;
1092+
};
1093+
10731094
struct fgraph_ops {
10741095
trace_func_graph_ent_t entryfunc;
10751096
trace_func_graph_ret_t retfunc;
@@ -1115,6 +1136,8 @@ unsigned long ftrace_graph_ret_addr(struct task_struct *task, int *idx,
11151136
unsigned long ret, unsigned long *retp);
11161137
unsigned long *fgraph_get_task_var(struct fgraph_ops *gops);
11171138

1139+
u32 graph_tracer_flags_get(u32 flags);
1140+
11181141
/*
11191142
* Sometimes we don't want to trace a function with the function
11201143
* graph tracer but we want them to keep traced by the usual function

kernel/trace/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,16 @@ config FUNCTION_GRAPH_RETVAL
242242
enable it via the trace option funcgraph-retval.
243243
See Documentation/trace/ftrace.rst
244244

245+
config FUNCTION_GRAPH_RETADDR
246+
bool "Kernel Function Graph Return Address"
247+
depends on FUNCTION_GRAPH_TRACER
248+
default n
249+
help
250+
Support recording and printing the function return address when
251+
using function graph tracer. It can be helpful to locate code line that
252+
the function is called. This feature is off by default, and you can
253+
enable it via the trace option funcgraph-retaddr.
254+
245255
config DYNAMIC_FTRACE
246256
bool "enable/disable function tracing dynamically"
247257
depends on FUNCTION_TRACER

kernel/trace/fgraph.c

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,8 @@ static inline unsigned long make_data_type_val(int idx, int size, int offset)
290290
}
291291

292292
/* ftrace_graph_entry set to this to tell some archs to run function graph */
293-
static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops)
293+
static int entry_run(struct ftrace_graph_ent *trace, struct fgraph_ops *ops,
294+
struct fgraph_extras *extras)
294295
{
295296
return 0;
296297
}
@@ -518,7 +519,8 @@ int __weak ftrace_disable_ftrace_graph_caller(void)
518519
#endif
519520

520521
int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
521-
struct fgraph_ops *gops)
522+
struct fgraph_ops *gops,
523+
struct fgraph_extras *extras)
522524
{
523525
return 0;
524526
}
@@ -646,13 +648,20 @@ int function_graph_enter(unsigned long ret, unsigned long func,
646648
unsigned long frame_pointer, unsigned long *retp)
647649
{
648650
struct ftrace_graph_ent trace;
651+
struct fgraph_extras extras;
649652
unsigned long bitmap = 0;
650653
int offset;
651654
int i;
655+
int idx = 0;
652656

653657
trace.func = func;
654658
trace.depth = ++current->curr_ret_depth;
655659

660+
extras.flags = graph_tracer_flags_get(TRACE_GRAPH_PRINT_RETADDR);
661+
if (IS_ENABLED(CONFIG_FUNCTION_GRAPH_RETADDR)
662+
&& extras.flags & TRACE_GRAPH_PRINT_RETADDR)
663+
extras.retaddr = ftrace_graph_ret_addr(current, &idx, ret, retp);
664+
656665
offset = ftrace_push_return_trace(ret, func, frame_pointer, retp, 0);
657666
if (offset < 0)
658667
goto out;
@@ -661,7 +670,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
661670
if (static_branch_likely(&fgraph_do_direct)) {
662671
int save_curr_ret_stack = current->curr_ret_stack;
663672

664-
if (static_call(fgraph_func)(&trace, fgraph_direct_gops))
673+
if (static_call(fgraph_func)(&trace, fgraph_direct_gops, &extras))
665674
bitmap |= BIT(fgraph_direct_gops->idx);
666675
else
667676
/* Clear out any saved storage */
@@ -679,7 +688,7 @@ int function_graph_enter(unsigned long ret, unsigned long func,
679688

680689
save_curr_ret_stack = current->curr_ret_stack;
681690
if (ftrace_ops_test(&gops->ops, func, NULL) &&
682-
gops->entryfunc(&trace, gops))
691+
gops->entryfunc(&trace, gops, &extras))
683692
bitmap |= BIT(i);
684693
else
685694
/* Clear out any saved storage */
@@ -1136,7 +1145,8 @@ void ftrace_graph_exit_task(struct task_struct *t)
11361145

11371146
#ifdef CONFIG_DYNAMIC_FTRACE
11381147
static int fgraph_pid_func(struct ftrace_graph_ent *trace,
1139-
struct fgraph_ops *gops)
1148+
struct fgraph_ops *gops,
1149+
struct fgraph_extras *extras)
11401150
{
11411151
struct trace_array *tr = gops->ops.private;
11421152
int pid;
@@ -1150,7 +1160,7 @@ static int fgraph_pid_func(struct ftrace_graph_ent *trace,
11501160
return 0;
11511161
}
11521162

1153-
return gops->saved_func(trace, gops);
1163+
return gops->saved_func(trace, gops, NULL);
11541164
}
11551165

11561166
void fgraph_update_pid_func(void)

kernel/trace/ftrace.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,8 @@ struct profile_fgraph_data {
827827
};
828828

829829
static int profile_graph_entry(struct ftrace_graph_ent *trace,
830-
struct fgraph_ops *gops)
830+
struct fgraph_ops *gops,
831+
struct fgraph_extras *extras)
831832
{
832833
struct profile_fgraph_data *profile_data;
833834

kernel/trace/trace.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ enum trace_type {
4646
TRACE_BRANCH,
4747
TRACE_GRAPH_RET,
4848
TRACE_GRAPH_ENT,
49+
TRACE_GRAPH_RETADDR_ENT,
4950
TRACE_USER_STACK,
5051
TRACE_BLK,
5152
TRACE_BPUTS,
@@ -512,6 +513,8 @@ extern void __ftrace_bad_type(void);
512513
IF_ASSIGN(var, ent, struct trace_branch, TRACE_BRANCH); \
513514
IF_ASSIGN(var, ent, struct ftrace_graph_ent_entry, \
514515
TRACE_GRAPH_ENT); \
516+
IF_ASSIGN(var, ent, struct fgraph_retaddr_ent_entry,\
517+
TRACE_GRAPH_RETADDR_ENT); \
515518
IF_ASSIGN(var, ent, struct ftrace_graph_ret_entry, \
516519
TRACE_GRAPH_RET); \
517520
IF_ASSIGN(var, ent, struct func_repeats_entry, \
@@ -692,7 +695,8 @@ void trace_default_header(struct seq_file *m);
692695
void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
693696

694697
void trace_graph_return(struct ftrace_graph_ret *trace, struct fgraph_ops *gops);
695-
int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops);
698+
int trace_graph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops,
699+
struct fgraph_extras *extras);
696700

697701
void tracing_start_cmdline_record(void);
698702
void tracing_stop_cmdline_record(void);
@@ -879,6 +883,7 @@ static __always_inline bool ftrace_hash_empty(struct ftrace_hash *hash)
879883
#define TRACE_GRAPH_GRAPH_TIME 0x400
880884
#define TRACE_GRAPH_PRINT_RETVAL 0x800
881885
#define TRACE_GRAPH_PRINT_RETVAL_HEX 0x1000
886+
#define TRACE_GRAPH_PRINT_RETADDR 0x2000
882887
#define TRACE_GRAPH_PRINT_FILL_SHIFT 28
883888
#define TRACE_GRAPH_PRINT_FILL_MASK (0x3 << TRACE_GRAPH_PRINT_FILL_SHIFT)
884889

@@ -900,6 +905,10 @@ extern void graph_trace_close(struct trace_iterator *iter);
900905
extern int __trace_graph_entry(struct trace_array *tr,
901906
struct ftrace_graph_ent *trace,
902907
unsigned int trace_ctx);
908+
extern int __trace_graph_retaddr_entry(struct trace_array *tr,
909+
struct ftrace_graph_ent *trace,
910+
unsigned int trace_ctx,
911+
unsigned long retaddr);
903912
extern void __trace_graph_return(struct trace_array *tr,
904913
struct ftrace_graph_ret *trace,
905914
unsigned int trace_ctx);

kernel/trace/trace_entries.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,35 @@ FTRACE_ENTRY_PACKED(funcgraph_entry, ftrace_graph_ent_entry,
8585
F_printk("--> %ps (%d)", (void *)__entry->func, __entry->depth)
8686
);
8787

88-
/* Function return entry */
88+
#ifdef CONFIG_FUNCTION_GRAPH_RETADDR
89+
90+
/* Function call entry with a return address */
91+
FTRACE_ENTRY_PACKED(fgraph_retaddr_entry, fgraph_retaddr_ent_entry,
92+
93+
TRACE_GRAPH_RETADDR_ENT,
94+
95+
F_STRUCT(
96+
__field_struct( struct fgraph_retaddr_ent, graph_ent )
97+
__field_packed( unsigned long, graph_ent, func )
98+
__field_packed( int, graph_ent, depth )
99+
__field_packed( unsigned long, graph_ent, retaddr )
100+
),
101+
102+
F_printk("--> %ps (%d) <- %ps", (void *)__entry->func, __entry->depth,
103+
(void *)__entry->retaddr)
104+
);
105+
106+
#else
107+
108+
#ifndef fgraph_retaddr_ent_entry
109+
#define fgraph_retaddr_ent_entry ftrace_graph_ent_entry
110+
#endif
111+
112+
#endif
113+
89114
#ifdef CONFIG_FUNCTION_GRAPH_RETVAL
90115

116+
/* Function return entry */
91117
FTRACE_ENTRY_PACKED(funcgraph_exit, ftrace_graph_ret_entry,
92118

93119
TRACE_GRAPH_RET,
@@ -110,6 +136,7 @@ FTRACE_ENTRY_PACKED(funcgraph_exit, ftrace_graph_ret_entry,
110136

111137
#else
112138

139+
/* Function return entry */
113140
FTRACE_ENTRY_PACKED(funcgraph_exit, ftrace_graph_ret_entry,
114141

115142
TRACE_GRAPH_RET,

0 commit comments

Comments
 (0)