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

perf: generalize perf_callchain

. avoid walking the stack when there is no room left in the buffer
. generalize get_perf_callchain() to be called from bpf helper

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Alexei Starovoitov and committed by
David S. Miller
568b329a 6b83d28a

+51 -29
+1 -1
arch/x86/include/asm/stacktrace.h
··· 37 37 /* Generic stack tracer with callbacks */ 38 38 39 39 struct stacktrace_ops { 40 - void (*address)(void *data, unsigned long address, int reliable); 40 + int (*address)(void *data, unsigned long address, int reliable); 41 41 /* On negative return stop dumping */ 42 42 int (*stack)(void *data, char *name); 43 43 walk_stack_t walk_stack;
+2 -2
arch/x86/kernel/cpu/perf_event.c
··· 2180 2180 return 0; 2181 2181 } 2182 2182 2183 - static void backtrace_address(void *data, unsigned long addr, int reliable) 2183 + static int backtrace_address(void *data, unsigned long addr, int reliable) 2184 2184 { 2185 2185 struct perf_callchain_entry *entry = data; 2186 2186 2187 - perf_callchain_store(entry, addr); 2187 + return perf_callchain_store(entry, addr); 2188 2188 } 2189 2189 2190 2190 static const struct stacktrace_ops backtrace_ops = {
+4 -2
arch/x86/kernel/dumpstack.c
··· 135 135 if (!__kernel_text_address(addr)) 136 136 break; 137 137 138 - ops->address(data, addr, 1); 138 + if (ops->address(data, addr, 1)) 139 + break; 139 140 frame = frame->next_frame; 140 141 ret_addr = &frame->return_address; 141 142 print_ftrace_graph_addr(addr, data, ops, tinfo, graph); ··· 155 154 /* 156 155 * Print one address/symbol entries per line. 157 156 */ 158 - static void print_trace_address(void *data, unsigned long addr, int reliable) 157 + static int print_trace_address(void *data, unsigned long addr, int reliable) 159 158 { 160 159 touch_nmi_watchdog(); 161 160 printk_stack_address(addr, reliable, data); 161 + return 0; 162 162 } 163 163 164 164 static const struct stacktrace_ops print_trace_ops = {
+11 -7
arch/x86/kernel/stacktrace.c
··· 14 14 return 0; 15 15 } 16 16 17 - static void 17 + static int 18 18 __save_stack_address(void *data, unsigned long addr, bool reliable, bool nosched) 19 19 { 20 20 struct stack_trace *trace = data; 21 21 #ifdef CONFIG_FRAME_POINTER 22 22 if (!reliable) 23 - return; 23 + return 0; 24 24 #endif 25 25 if (nosched && in_sched_functions(addr)) 26 - return; 26 + return 0; 27 27 if (trace->skip > 0) { 28 28 trace->skip--; 29 - return; 29 + return 0; 30 30 } 31 - if (trace->nr_entries < trace->max_entries) 31 + if (trace->nr_entries < trace->max_entries) { 32 32 trace->entries[trace->nr_entries++] = addr; 33 + return 0; 34 + } else { 35 + return -1; /* no more room, stop walking the stack */ 36 + } 33 37 } 34 38 35 - static void save_stack_address(void *data, unsigned long addr, int reliable) 39 + static int save_stack_address(void *data, unsigned long addr, int reliable) 36 40 { 37 41 return __save_stack_address(data, addr, reliable, false); 38 42 } 39 43 40 - static void 44 + static int 41 45 save_stack_address_nosched(void *data, unsigned long addr, int reliable) 42 46 { 43 47 return __save_stack_address(data, addr, reliable, true);
+2 -1
arch/x86/oprofile/backtrace.c
··· 23 23 return 0; 24 24 } 25 25 26 - static void backtrace_address(void *data, unsigned long addr, int reliable) 26 + static int backtrace_address(void *data, unsigned long addr, int reliable) 27 27 { 28 28 unsigned int *depth = data; 29 29 30 30 if ((*depth)--) 31 31 oprofile_add_trace(addr); 32 + return 0; 32 33 } 33 34 34 35 static struct stacktrace_ops backtrace_ops = {
+11 -2
include/linux/perf_event.h
··· 964 964 965 965 extern void perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs); 966 966 extern void perf_callchain_kernel(struct perf_callchain_entry *entry, struct pt_regs *regs); 967 + extern struct perf_callchain_entry * 968 + get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user, 969 + bool crosstask, bool add_mark); 970 + extern int get_callchain_buffers(void); 971 + extern void put_callchain_buffers(void); 967 972 968 - static inline void perf_callchain_store(struct perf_callchain_entry *entry, u64 ip) 973 + static inline int perf_callchain_store(struct perf_callchain_entry *entry, u64 ip) 969 974 { 970 - if (entry->nr < PERF_MAX_STACK_DEPTH) 975 + if (entry->nr < PERF_MAX_STACK_DEPTH) { 971 976 entry->ip[entry->nr++] = ip; 977 + return 0; 978 + } else { 979 + return -1; /* no more room, stop walking the stack */ 980 + } 972 981 } 973 982 974 983 extern int sysctl_perf_event_paranoid;
+20 -12
kernel/events/callchain.c
··· 159 159 struct perf_callchain_entry * 160 160 perf_callchain(struct perf_event *event, struct pt_regs *regs) 161 161 { 162 - int rctx; 163 - struct perf_callchain_entry *entry; 164 - 165 - int kernel = !event->attr.exclude_callchain_kernel; 166 - int user = !event->attr.exclude_callchain_user; 162 + bool kernel = !event->attr.exclude_callchain_kernel; 163 + bool user = !event->attr.exclude_callchain_user; 164 + /* Disallow cross-task user callchains. */ 165 + bool crosstask = event->ctx->task && event->ctx->task != current; 167 166 168 167 if (!kernel && !user) 169 168 return NULL; 169 + 170 + return get_perf_callchain(regs, 0, kernel, user, crosstask, true); 171 + } 172 + 173 + struct perf_callchain_entry * 174 + get_perf_callchain(struct pt_regs *regs, u32 init_nr, bool kernel, bool user, 175 + bool crosstask, bool add_mark) 176 + { 177 + struct perf_callchain_entry *entry; 178 + int rctx; 170 179 171 180 entry = get_callchain_entry(&rctx); 172 181 if (rctx == -1) ··· 184 175 if (!entry) 185 176 goto exit_put; 186 177 187 - entry->nr = 0; 178 + entry->nr = init_nr; 188 179 189 180 if (kernel && !user_mode(regs)) { 190 - perf_callchain_store(entry, PERF_CONTEXT_KERNEL); 181 + if (add_mark) 182 + perf_callchain_store(entry, PERF_CONTEXT_KERNEL); 191 183 perf_callchain_kernel(entry, regs); 192 184 } 193 185 ··· 201 191 } 202 192 203 193 if (regs) { 204 - /* 205 - * Disallow cross-task user callchains. 206 - */ 207 - if (event->ctx->task && event->ctx->task != current) 194 + if (crosstask) 208 195 goto exit_put; 209 196 210 - perf_callchain_store(entry, PERF_CONTEXT_USER); 197 + if (add_mark) 198 + perf_callchain_store(entry, PERF_CONTEXT_USER); 211 199 perf_callchain_user(entry, regs); 212 200 } 213 201 }
-2
kernel/events/internal.h
··· 182 182 /* Callchain handling */ 183 183 extern struct perf_callchain_entry * 184 184 perf_callchain(struct perf_event *event, struct pt_regs *regs); 185 - extern int get_callchain_buffers(void); 186 - extern void put_callchain_buffers(void); 187 185 188 186 static inline int get_recursion_context(int *recursion) 189 187 {