Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

function_graph: Enable funcgraph-args and funcgraph-retaddr to work simultaneously

Currently, the funcgraph-args and funcgraph-retaddr features are
mutually exclusive. This patch resolves this limitation by allowing
funcgraph-retaddr to have an args array.

To verify the change, use perf to trace vfs_write with both options
enabled:

Before:
# perf ftrace -G vfs_write --graph-opts args,retaddr
......
down_read() { /* <-n_tty_write+0xa3/0x540 */
__cond_resched(); /* <-down_read+0x12/0x160 */
preempt_count_add(); /* <-down_read+0x3b/0x160 */
preempt_count_sub(); /* <-down_read+0x8b/0x160 */
}

After:
# perf ftrace -G vfs_write --graph-opts args,retaddr
......
down_read(sem=0xffff8880100bea78) { /* <-n_tty_write+0xa3/0x540 */
__cond_resched(); /* <-down_read+0x12/0x160 */
preempt_count_add(val=1); /* <-down_read+0x3b/0x160 */
preempt_count_sub(val=1); /* <-down_read+0x8b/0x160 */
}

Cc: Steven Rostedt (Google) <rostedt@goodmis.org>
Cc: Sven Schnelle <svens@linux.ibm.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Xiaoqin Zhang <zhangxiaoqin@xiaomi.com>
Link: https://patch.msgid.link/20251125093425.2563849-1-dolinux.peng@gmail.com
Signed-off-by: pengdonglin <pengdonglin@xiaomi.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>

authored by

pengdonglin and committed by
Steven Rostedt (Google)
f83ac754 20e71683

+80 -37
+2 -5
include/linux/ftrace.h
··· 1126 1126 */ 1127 1127 struct ftrace_graph_ent { 1128 1128 unsigned long func; /* Current function */ 1129 - int depth; 1129 + unsigned long depth; 1130 1130 } __packed; 1131 1131 1132 1132 /* 1133 1133 * Structure that defines an entry function trace with retaddr. 1134 - * It's already packed but the attribute "packed" is needed 1135 - * to remove extra padding at the end. 1136 1134 */ 1137 1135 struct fgraph_retaddr_ent { 1138 - unsigned long func; /* Current function */ 1139 - int depth; 1136 + struct ftrace_graph_ent ent; 1140 1137 unsigned long retaddr; /* Return address */ 1141 1138 } __packed; 1142 1139
+23 -1
kernel/trace/trace.h
··· 964 964 extern int __trace_graph_retaddr_entry(struct trace_array *tr, 965 965 struct ftrace_graph_ent *trace, 966 966 unsigned int trace_ctx, 967 - unsigned long retaddr); 967 + unsigned long retaddr, 968 + struct ftrace_regs *fregs); 968 969 extern void __trace_graph_return(struct trace_array *tr, 969 970 struct ftrace_graph_ret *trace, 970 971 unsigned int trace_ctx, ··· 2276 2275 * So this value has no meaning. 2277 2276 */ 2278 2277 #define FTRACE_TRAMPOLINE_MARKER ((unsigned long) INT_MAX) 2278 + 2279 + /* 2280 + * This is used to get the address of the args array based on 2281 + * the type of the entry. 2282 + */ 2283 + #define FGRAPH_ENTRY_ARGS(e) \ 2284 + ({ \ 2285 + unsigned long *_args; \ 2286 + struct ftrace_graph_ent_entry *_e = e; \ 2287 + \ 2288 + if (IS_ENABLED(CONFIG_FUNCTION_GRAPH_RETADDR) && \ 2289 + e->ent.type == TRACE_GRAPH_RETADDR_ENT) { \ 2290 + struct fgraph_retaddr_ent_entry *_re; \ 2291 + \ 2292 + _re = (typeof(_re))_e; \ 2293 + _args = _re->args; \ 2294 + } else { \ 2295 + _args = _e->args; \ 2296 + } \ 2297 + _args; \ 2298 + }) 2279 2299 2280 2300 #endif /* _LINUX_KERNEL_TRACE_H */
+8 -7
kernel/trace/trace_entries.h
··· 80 80 F_STRUCT( 81 81 __field_struct( struct ftrace_graph_ent, graph_ent ) 82 82 __field_packed( unsigned long, graph_ent, func ) 83 - __field_packed( unsigned int, graph_ent, depth ) 83 + __field_packed( unsigned long, graph_ent, depth ) 84 84 __dynamic_array(unsigned long, args ) 85 85 ), 86 86 87 - F_printk("--> %ps (%u)", (void *)__entry->func, __entry->depth) 87 + F_printk("--> %ps (%lu)", (void *)__entry->func, __entry->depth) 88 88 ); 89 89 90 90 #ifdef CONFIG_FUNCTION_GRAPH_RETADDR ··· 95 95 TRACE_GRAPH_RETADDR_ENT, 96 96 97 97 F_STRUCT( 98 - __field_struct( struct fgraph_retaddr_ent, graph_ent ) 99 - __field_packed( unsigned long, graph_ent, func ) 100 - __field_packed( unsigned int, graph_ent, depth ) 101 - __field_packed( unsigned long, graph_ent, retaddr ) 98 + __field_struct( struct fgraph_retaddr_ent, graph_rent ) 99 + __field_packed( unsigned long, graph_rent.ent, func ) 100 + __field_packed( unsigned long, graph_rent.ent, depth ) 101 + __field_packed( unsigned long, graph_rent, retaddr ) 102 + __dynamic_array(unsigned long, args ) 102 103 ), 103 104 104 - F_printk("--> %ps (%u) <- %ps", (void *)__entry->func, __entry->depth, 105 + F_printk("--> %ps (%lu) <- %ps", (void *)__entry->func, __entry->depth, 105 106 (void *)__entry->retaddr) 106 107 ); 107 108
+47 -24
kernel/trace/trace_functions_graph.c
··· 36 36 unsigned long args[FTRACE_REGS_MAX_ARGS]; 37 37 }; 38 38 39 + struct fgraph_retaddr_ent_args { 40 + struct fgraph_retaddr_ent_entry ent; 41 + /* Force the sizeof of args[] to have FTRACE_REGS_MAX_ARGS entries */ 42 + unsigned long args[FTRACE_REGS_MAX_ARGS]; 43 + }; 44 + 39 45 struct fgraph_data { 40 46 struct fgraph_cpu_data __percpu *cpu_data; 41 47 42 48 /* Place to preserve last processed entry. */ 43 49 union { 44 50 struct fgraph_ent_args ent; 45 - /* TODO allow retaddr to have args */ 46 - struct fgraph_retaddr_ent_entry rent; 51 + struct fgraph_retaddr_ent_args rent; 47 52 }; 48 53 struct ftrace_graph_ret_entry ret; 49 54 int failed; ··· 165 160 int __trace_graph_retaddr_entry(struct trace_array *tr, 166 161 struct ftrace_graph_ent *trace, 167 162 unsigned int trace_ctx, 168 - unsigned long retaddr) 163 + unsigned long retaddr, 164 + struct ftrace_regs *fregs) 169 165 { 170 166 struct ring_buffer_event *event; 171 167 struct trace_buffer *buffer = tr->array_buffer.buffer; 172 168 struct fgraph_retaddr_ent_entry *entry; 169 + int size; 170 + 171 + /* If fregs is defined, add FTRACE_REGS_MAX_ARGS long size words */ 172 + size = sizeof(*entry) + (FTRACE_REGS_MAX_ARGS * !!fregs * sizeof(long)); 173 173 174 174 event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_RETADDR_ENT, 175 - sizeof(*entry), trace_ctx); 175 + size, trace_ctx); 176 176 if (!event) 177 177 return 0; 178 178 entry = ring_buffer_event_data(event); 179 - entry->graph_ent.func = trace->func; 180 - entry->graph_ent.depth = trace->depth; 181 - entry->graph_ent.retaddr = retaddr; 179 + entry->graph_rent.ent = *trace; 180 + entry->graph_rent.retaddr = retaddr; 181 + 182 + #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API 183 + if (fregs) { 184 + for (int i = 0; i < FTRACE_REGS_MAX_ARGS; i++) 185 + entry->args[i] = ftrace_regs_get_argument(fregs, i); 186 + } 187 + #endif 188 + 182 189 trace_buffer_unlock_commit_nostack(buffer, event); 183 190 184 191 return 1; ··· 199 182 int __trace_graph_retaddr_entry(struct trace_array *tr, 200 183 struct ftrace_graph_ent *trace, 201 184 unsigned int trace_ctx, 202 - unsigned long retaddr) 185 + unsigned long retaddr, 186 + struct ftrace_regs *fregs) 203 187 { 204 188 return 1; 205 189 } ··· 285 267 if (IS_ENABLED(CONFIG_FUNCTION_GRAPH_RETADDR) && 286 268 tracer_flags_is_set(tr, TRACE_GRAPH_PRINT_RETADDR)) { 287 269 unsigned long retaddr = ftrace_graph_top_ret_addr(current); 288 - ret = __trace_graph_retaddr_entry(tr, trace, trace_ctx, retaddr); 270 + ret = __trace_graph_retaddr_entry(tr, trace, trace_ctx, 271 + retaddr, fregs); 289 272 } else { 290 273 ret = __graph_entry(tr, trace, trace_ctx, fregs); 291 274 } ··· 673 654 * Save current and next entries for later reference 674 655 * if the output fails. 675 656 */ 676 - if (unlikely(curr->ent.type == TRACE_GRAPH_RETADDR_ENT)) { 677 - data->rent = *(struct fgraph_retaddr_ent_entry *)curr; 678 - } else { 679 - int size = min((int)sizeof(data->ent), (int)iter->ent_size); 657 + int size = min_t(int, sizeof(data->rent), iter->ent_size); 680 658 681 - memcpy(&data->ent, curr, size); 682 - } 659 + memcpy(&data->rent, curr, size); 683 660 /* 684 661 * If the next event is not a return type, then 685 662 * we only care about what type it is. Otherwise we can ··· 853 838 trace_seq_puts(s, " /*"); 854 839 855 840 trace_seq_puts(s, " <-"); 856 - seq_print_ip_sym_offset(s, entry->graph_ent.retaddr, trace_flags); 841 + seq_print_ip_sym_offset(s, entry->graph_rent.retaddr, trace_flags); 857 842 858 843 if (comment) 859 844 trace_seq_puts(s, " */"); ··· 999 984 trace_seq_printf(s, "%ps", (void *)ret_func); 1000 985 1001 986 if (args_size >= FTRACE_REGS_MAX_ARGS * sizeof(long)) { 1002 - print_function_args(s, entry->args, ret_func); 987 + print_function_args(s, FGRAPH_ENTRY_ARGS(entry), ret_func); 1003 988 trace_seq_putc(s, ';'); 1004 989 } else 1005 990 trace_seq_puts(s, "();"); ··· 1051 1036 args_size = iter->ent_size - offsetof(struct ftrace_graph_ent_entry, args); 1052 1037 1053 1038 if (args_size >= FTRACE_REGS_MAX_ARGS * sizeof(long)) 1054 - print_function_args(s, entry->args, func); 1039 + print_function_args(s, FGRAPH_ENTRY_ARGS(entry), func); 1055 1040 else 1056 1041 trace_seq_puts(s, "()"); 1057 1042 ··· 1233 1218 /* 1234 1219 * print_graph_entry() may consume the current event, 1235 1220 * thus @field may become invalid, so we need to save it. 1236 - * sizeof(struct ftrace_graph_ent_entry) is very small, 1237 - * it can be safely saved at the stack. 1221 + * This function is shared by ftrace_graph_ent_entry and 1222 + * fgraph_retaddr_ent_entry, the size of the latter one 1223 + * is larger, but it is very small and can be safely saved 1224 + * at the stack. 1238 1225 */ 1239 1226 struct ftrace_graph_ent_entry *entry; 1240 - u8 save_buf[sizeof(*entry) + FTRACE_REGS_MAX_ARGS * sizeof(long)]; 1227 + struct fgraph_retaddr_ent_entry *rentry; 1228 + u8 save_buf[sizeof(*rentry) + FTRACE_REGS_MAX_ARGS * sizeof(long)]; 1241 1229 1242 1230 /* The ent_size is expected to be as big as the entry */ 1243 1231 if (iter->ent_size > sizeof(save_buf)) ··· 1469 1451 } 1470 1452 #ifdef CONFIG_FUNCTION_GRAPH_RETADDR 1471 1453 case TRACE_GRAPH_RETADDR_ENT: { 1472 - struct fgraph_retaddr_ent_entry saved; 1454 + /* 1455 + * ftrace_graph_ent_entry and fgraph_retaddr_ent_entry have 1456 + * similar functions and memory layouts. The only difference 1457 + * is that the latter one has an extra retaddr member, so 1458 + * they can share most of the logic. 1459 + */ 1473 1460 struct fgraph_retaddr_ent_entry *rfield; 1474 1461 1475 1462 trace_assign_type(rfield, entry); 1476 - saved = *rfield; 1477 - return print_graph_entry((struct ftrace_graph_ent_entry *)&saved, s, iter, flags); 1463 + return print_graph_entry((struct ftrace_graph_ent_entry *)rfield, 1464 + s, iter, flags); 1478 1465 } 1479 1466 #endif 1480 1467 case TRACE_GRAPH_RET: {