aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/trace')
-rw-r--r--kernel/trace/trace.h11
-rw-r--r--kernel/trace/trace_entries.h6
-rw-r--r--kernel/trace/trace_event_perf.c86
-rw-r--r--kernel/trace/trace_export.c5
4 files changed, 106 insertions, 2 deletions
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)
595static inline int ftrace_is_dead(void) { return 0; } 595static inline int ftrace_is_dead(void) { return 0; }
596#endif 596#endif
597 597
598int 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
839int 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 */
58FTRACE_ENTRY(function, ftrace_entry, 58FTRACE_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;
24static int perf_trace_event_perm(struct ftrace_event_call *tp_event, 24static 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}
252EXPORT_SYMBOL_GPL(perf_trace_buf_prepare); 257EXPORT_SYMBOL_GPL(perf_trace_buf_prepare);
258
259#ifdef CONFIG_FUNCTION_TRACER
260static void
261perf_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(&regs);
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, &regs, head);
285
286#undef ENTRY_SIZE
287}
288
289static 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
298static 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
304static void perf_ftrace_function_enable(struct perf_event *event)
305{
306 ftrace_function_local_enable(&event->ftrace_ops);
307}
308
309static void perf_ftrace_function_disable(struct perf_event *event)
310{
311 ftrace_function_local_disable(&event->ftrace_ops);
312}
313
314int 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
187int 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"