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" |
