diff options
-rw-r--r-- | include/linux/perf_event.h | 3 | ||||
-rw-r--r-- | kernel/trace/trace.h | 11 | ||||
-rw-r--r-- | kernel/trace/trace_entries.h | 6 | ||||
-rw-r--r-- | kernel/trace/trace_event_perf.c | 86 | ||||
-rw-r--r-- | kernel/trace/trace_export.c | 5 |
5 files changed, 109 insertions, 2 deletions
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 412b790f5da6..92a056f6d18d 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h | |||
@@ -859,6 +859,9 @@ struct perf_event { | |||
859 | #ifdef CONFIG_EVENT_TRACING | 859 | #ifdef CONFIG_EVENT_TRACING |
860 | struct ftrace_event_call *tp_event; | 860 | struct ftrace_event_call *tp_event; |
861 | struct event_filter *filter; | 861 | struct event_filter *filter; |
862 | #ifdef CONFIG_FUNCTION_TRACER | ||
863 | struct ftrace_ops ftrace_ops; | ||
864 | #endif | ||
862 | #endif | 865 | #endif |
863 | 866 | ||
864 | #ifdef CONFIG_CGROUP_PERF | 867 | #ifdef CONFIG_CGROUP_PERF |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 638476af8d7b..76a1c5094bbf 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
@@ -595,6 +595,8 @@ static inline int ftrace_trace_task(struct task_struct *task) | |||
595 | static inline int ftrace_is_dead(void) { return 0; } | 595 | static inline int ftrace_is_dead(void) { return 0; } |
596 | #endif | 596 | #endif |
597 | 597 | ||
598 | int ftrace_event_is_function(struct ftrace_event_call *call); | ||
599 | |||
598 | /* | 600 | /* |
599 | * struct trace_parser - servers for reading the user input separated by spaces | 601 | * struct trace_parser - servers for reading the user input separated by spaces |
600 | * @cont: set if the input is not complete - no final space char was found | 602 | * @cont: set if the input is not complete - no final space char was found |
@@ -832,4 +834,13 @@ extern const char *__stop___trace_bprintk_fmt[]; | |||
832 | FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print)) | 834 | FTRACE_ENTRY(call, struct_name, id, PARAMS(tstruct), PARAMS(print)) |
833 | #include "trace_entries.h" | 835 | #include "trace_entries.h" |
834 | 836 | ||
837 | #ifdef CONFIG_PERF_EVENTS | ||
838 | #ifdef CONFIG_FUNCTION_TRACER | ||
839 | int perf_ftrace_event_register(struct ftrace_event_call *call, | ||
840 | enum trace_reg type, void *data); | ||
841 | #else | ||
842 | #define perf_ftrace_event_register NULL | ||
843 | #endif /* CONFIG_FUNCTION_TRACER */ | ||
844 | #endif /* CONFIG_PERF_EVENTS */ | ||
845 | |||
835 | #endif /* _LINUX_KERNEL_TRACE_H */ | 846 | #endif /* _LINUX_KERNEL_TRACE_H */ |
diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h index 93365907f219..47db7eda0531 100644 --- a/kernel/trace/trace_entries.h +++ b/kernel/trace/trace_entries.h | |||
@@ -55,7 +55,7 @@ | |||
55 | /* | 55 | /* |
56 | * Function trace entry - function address and parent function address: | 56 | * Function trace entry - function address and parent function address: |
57 | */ | 57 | */ |
58 | FTRACE_ENTRY(function, ftrace_entry, | 58 | FTRACE_ENTRY_REG(function, ftrace_entry, |
59 | 59 | ||
60 | TRACE_FN, | 60 | TRACE_FN, |
61 | 61 | ||
@@ -64,7 +64,9 @@ FTRACE_ENTRY(function, ftrace_entry, | |||
64 | __field( unsigned long, parent_ip ) | 64 | __field( unsigned long, parent_ip ) |
65 | ), | 65 | ), |
66 | 66 | ||
67 | F_printk(" %lx <-- %lx", __entry->ip, __entry->parent_ip) | 67 | F_printk(" %lx <-- %lx", __entry->ip, __entry->parent_ip), |
68 | |||
69 | perf_ftrace_event_register | ||
68 | ); | 70 | ); |
69 | 71 | ||
70 | /* Function call entry */ | 72 | /* Function call entry */ |
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index d72af0b03822..fdeeb5c49627 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c | |||
@@ -24,6 +24,11 @@ static int total_ref_count; | |||
24 | static int perf_trace_event_perm(struct ftrace_event_call *tp_event, | 24 | static int perf_trace_event_perm(struct ftrace_event_call *tp_event, |
25 | struct perf_event *p_event) | 25 | struct perf_event *p_event) |
26 | { | 26 | { |
27 | /* The ftrace function trace is allowed only for root. */ | ||
28 | if (ftrace_event_is_function(tp_event) && | ||
29 | perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) | ||
30 | return -EPERM; | ||
31 | |||
27 | /* No tracing, just counting, so no obvious leak */ | 32 | /* No tracing, just counting, so no obvious leak */ |
28 | if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW)) | 33 | if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW)) |
29 | return 0; | 34 | return 0; |
@@ -250,3 +255,84 @@ __kprobes void *perf_trace_buf_prepare(int size, unsigned short type, | |||
250 | return raw_data; | 255 | return raw_data; |
251 | } | 256 | } |
252 | EXPORT_SYMBOL_GPL(perf_trace_buf_prepare); | 257 | EXPORT_SYMBOL_GPL(perf_trace_buf_prepare); |
258 | |||
259 | #ifdef CONFIG_FUNCTION_TRACER | ||
260 | static void | ||
261 | perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip) | ||
262 | { | ||
263 | struct ftrace_entry *entry; | ||
264 | struct hlist_head *head; | ||
265 | struct pt_regs regs; | ||
266 | int rctx; | ||
267 | |||
268 | #define ENTRY_SIZE (ALIGN(sizeof(struct ftrace_entry) + sizeof(u32), \ | ||
269 | sizeof(u64)) - sizeof(u32)) | ||
270 | |||
271 | BUILD_BUG_ON(ENTRY_SIZE > PERF_MAX_TRACE_SIZE); | ||
272 | |||
273 | perf_fetch_caller_regs(®s); | ||
274 | |||
275 | entry = perf_trace_buf_prepare(ENTRY_SIZE, TRACE_FN, NULL, &rctx); | ||
276 | if (!entry) | ||
277 | return; | ||
278 | |||
279 | entry->ip = ip; | ||
280 | entry->parent_ip = parent_ip; | ||
281 | |||
282 | head = this_cpu_ptr(event_function.perf_events); | ||
283 | perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, 0, | ||
284 | 1, ®s, head); | ||
285 | |||
286 | #undef ENTRY_SIZE | ||
287 | } | ||
288 | |||
289 | static int perf_ftrace_function_register(struct perf_event *event) | ||
290 | { | ||
291 | struct ftrace_ops *ops = &event->ftrace_ops; | ||
292 | |||
293 | ops->flags |= FTRACE_OPS_FL_CONTROL; | ||
294 | ops->func = perf_ftrace_function_call; | ||
295 | return register_ftrace_function(ops); | ||
296 | } | ||
297 | |||
298 | static int perf_ftrace_function_unregister(struct perf_event *event) | ||
299 | { | ||
300 | struct ftrace_ops *ops = &event->ftrace_ops; | ||
301 | return unregister_ftrace_function(ops); | ||
302 | } | ||
303 | |||
304 | static void perf_ftrace_function_enable(struct perf_event *event) | ||
305 | { | ||
306 | ftrace_function_local_enable(&event->ftrace_ops); | ||
307 | } | ||
308 | |||
309 | static void perf_ftrace_function_disable(struct perf_event *event) | ||
310 | { | ||
311 | ftrace_function_local_disable(&event->ftrace_ops); | ||
312 | } | ||
313 | |||
314 | int perf_ftrace_event_register(struct ftrace_event_call *call, | ||
315 | enum trace_reg type, void *data) | ||
316 | { | ||
317 | switch (type) { | ||
318 | case TRACE_REG_REGISTER: | ||
319 | case TRACE_REG_UNREGISTER: | ||
320 | break; | ||
321 | case TRACE_REG_PERF_REGISTER: | ||
322 | case TRACE_REG_PERF_UNREGISTER: | ||
323 | return 0; | ||
324 | case TRACE_REG_PERF_OPEN: | ||
325 | return perf_ftrace_function_register(data); | ||
326 | case TRACE_REG_PERF_CLOSE: | ||
327 | return perf_ftrace_function_unregister(data); | ||
328 | case TRACE_REG_PERF_ADD: | ||
329 | perf_ftrace_function_enable(data); | ||
330 | return 0; | ||
331 | case TRACE_REG_PERF_DEL: | ||
332 | perf_ftrace_function_disable(data); | ||
333 | return 0; | ||
334 | } | ||
335 | |||
336 | return -EINVAL; | ||
337 | } | ||
338 | #endif /* CONFIG_FUNCTION_TRACER */ | ||
diff --git a/kernel/trace/trace_export.c b/kernel/trace/trace_export.c index f74de861cde9..a3dbee6d04cd 100644 --- a/kernel/trace/trace_export.c +++ b/kernel/trace/trace_export.c | |||
@@ -184,4 +184,9 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call; | |||
184 | FTRACE_ENTRY_REG(call, struct_name, etype, \ | 184 | FTRACE_ENTRY_REG(call, struct_name, etype, \ |
185 | PARAMS(tstruct), PARAMS(print), NULL) | 185 | PARAMS(tstruct), PARAMS(print), NULL) |
186 | 186 | ||
187 | int ftrace_event_is_function(struct ftrace_event_call *call) | ||
188 | { | ||
189 | return call == &event_function; | ||
190 | } | ||
191 | |||
187 | #include "trace_entries.h" | 192 | #include "trace_entries.h" |