blob: a1304f8c4440d768d36648483e790bb0b56f2434 [file] [log] [blame]
Peter Zijlstraac199db2009-03-19 20:26:15 +01001/*
Frederic Weisbecker97d5a222010-03-05 05:35:37 +01002 * trace event based perf event profiling/tracing
Peter Zijlstraac199db2009-03-19 20:26:15 +01003 *
4 * Copyright (C) 2009 Red Hat Inc, Peter Zijlstra <pzijlstr@redhat.com>
Frederic Weisbeckerc5306652010-03-03 07:16:16 +01005 * Copyright (C) 2009-2010 Frederic Weisbecker <fweisbec@gmail.com>
Peter Zijlstraac199db2009-03-19 20:26:15 +01006 */
7
Li Zefan558e6542009-08-24 12:19:47 +08008#include <linux/module.h>
Xiao Guangrong430ad5a2010-01-28 09:32:29 +08009#include <linux/kprobes.h>
Peter Zijlstraac199db2009-03-19 20:26:15 +010010#include "trace.h"
11
Frederic Weisbeckerdcd5c162010-03-16 01:05:02 +010012EXPORT_SYMBOL_GPL(perf_arch_fetch_caller_regs);
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020013
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020014static char *perf_trace_buf[4];
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020015
Frederic Weisbeckereb1e7962010-03-23 00:08:59 +010016/*
17 * Force it to be aligned to unsigned long to avoid misaligned accesses
18 * suprises
19 */
20typedef typeof(unsigned long [PERF_MAX_TRACE_SIZE / sizeof(unsigned long)])
21 perf_trace_t;
Frederic Weisbeckerce71b9d2009-11-22 05:26:55 +010022
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020023/* Count the events in use (per event id, not per instance) */
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010024static int total_ref_count;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020025
Peter Zijlstra4f41c012010-05-18 18:08:32 +020026static int perf_trace_event_enable(struct ftrace_event_call *event, void *data)
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020027{
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020028 int ret = -ENOMEM;
29
Peter Zijlstra4f41c012010-05-18 18:08:32 +020030 if (event->perf_refcount++ > 0) {
31 event->perf_data = NULL;
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020032 return 0;
Peter Zijlstra4f41c012010-05-18 18:08:32 +020033 }
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020034
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010035 if (!total_ref_count) {
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020036 char *buf;
37 int i;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020038
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020039 for (i = 0; i < 4; i++) {
40 buf = (char *)alloc_percpu(perf_trace_t);
41 if (!buf)
42 goto fail_buf;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020043
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020044 rcu_assign_pointer(perf_trace_buf[i], buf);
45 }
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020046 }
47
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010048 ret = event->perf_event_enable(event);
Frederic Weisbeckerfe8e5b52009-10-03 14:55:18 +020049 if (!ret) {
Peter Zijlstra4f41c012010-05-18 18:08:32 +020050 event->perf_data = data;
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010051 total_ref_count++;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020052 return 0;
Frederic Weisbeckerfe8e5b52009-10-03 14:55:18 +020053 }
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020054
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020055fail_buf:
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020056 if (!total_ref_count) {
57 int i;
58
59 for (i = 0; i < 4; i++) {
60 free_percpu(perf_trace_buf[i]);
61 perf_trace_buf[i] = NULL;
62 }
63 }
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010064 event->perf_refcount--;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020065
66 return ret;
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020067}
68
Peter Zijlstra4f41c012010-05-18 18:08:32 +020069int perf_trace_enable(int event_id, void *data)
Peter Zijlstraac199db2009-03-19 20:26:15 +010070{
71 struct ftrace_event_call *event;
Li Zefan20c89282009-05-06 10:33:45 +080072 int ret = -EINVAL;
Peter Zijlstraac199db2009-03-19 20:26:15 +010073
Li Zefan20c89282009-05-06 10:33:45 +080074 mutex_lock(&event_mutex);
Steven Rostedta59fd602009-04-10 13:52:20 -040075 list_for_each_entry(event, &ftrace_events, list) {
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010076 if (event->id == event_id && event->perf_event_enable &&
Li Zefan558e6542009-08-24 12:19:47 +080077 try_module_get(event->mod)) {
Peter Zijlstra4f41c012010-05-18 18:08:32 +020078 ret = perf_trace_event_enable(event, data);
Li Zefan20c89282009-05-06 10:33:45 +080079 break;
80 }
Peter Zijlstraac199db2009-03-19 20:26:15 +010081 }
Li Zefan20c89282009-05-06 10:33:45 +080082 mutex_unlock(&event_mutex);
Peter Zijlstraac199db2009-03-19 20:26:15 +010083
Li Zefan20c89282009-05-06 10:33:45 +080084 return ret;
Peter Zijlstraac199db2009-03-19 20:26:15 +010085}
86
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010087static void perf_trace_event_disable(struct ftrace_event_call *event)
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020088{
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010089 if (--event->perf_refcount > 0)
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020090 return;
91
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010092 event->perf_event_disable(event);
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020093
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010094 if (!--total_ref_count) {
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020095 char *buf[4];
96 int i;
Frederic Weisbecker20ab44252009-09-18 06:10:28 +020097
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020098 for (i = 0; i < 4; i++) {
99 buf[i] = perf_trace_buf[i];
100 rcu_assign_pointer(perf_trace_buf[i], NULL);
101 }
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200102
103 /*
104 * Ensure every events in profiling have finished before
105 * releasing the buffers
106 */
107 synchronize_sched();
108
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +0200109 for (i = 0; i < 4; i++)
110 free_percpu(buf[i]);
Frederic Weisbecker20ab44252009-09-18 06:10:28 +0200111 }
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +0200112}
113
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100114void perf_trace_disable(int event_id)
Peter Zijlstraac199db2009-03-19 20:26:15 +0100115{
116 struct ftrace_event_call *event;
117
Li Zefan20c89282009-05-06 10:33:45 +0800118 mutex_lock(&event_mutex);
Steven Rostedta59fd602009-04-10 13:52:20 -0400119 list_for_each_entry(event, &ftrace_events, list) {
Li Zefan20c89282009-05-06 10:33:45 +0800120 if (event->id == event_id) {
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100121 perf_trace_event_disable(event);
Li Zefan558e6542009-08-24 12:19:47 +0800122 module_put(event->mod);
Li Zefan20c89282009-05-06 10:33:45 +0800123 break;
124 }
Peter Zijlstraac199db2009-03-19 20:26:15 +0100125 }
Li Zefan20c89282009-05-06 10:33:45 +0800126 mutex_unlock(&event_mutex);
Peter Zijlstraac199db2009-03-19 20:26:15 +0100127}
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800128
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100129__kprobes void *perf_trace_buf_prepare(int size, unsigned short type,
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +0200130 struct pt_regs *regs, int *rctxp)
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800131{
132 struct trace_entry *entry;
133 char *trace_buf, *raw_data;
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +0200134 int pc;
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800135
Frederic Weisbeckereb1e7962010-03-23 00:08:59 +0100136 BUILD_BUG_ON(PERF_MAX_TRACE_SIZE % sizeof(unsigned long));
137
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800138 pc = preempt_count();
139
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800140 *rctxp = perf_swevent_get_recursion_context();
141 if (*rctxp < 0)
142 goto err_recursion;
143
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +0200144 trace_buf = rcu_dereference_sched(perf_trace_buf[*rctxp]);
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800145 if (!trace_buf)
146 goto err;
147
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +0200148 raw_data = per_cpu_ptr(trace_buf, smp_processor_id());
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800149
150 /* zero the dead bytes from align to not leak stack to user */
Frederic Weisbeckereb1e7962010-03-23 00:08:59 +0100151 memset(&raw_data[size - sizeof(u64)], 0, sizeof(u64));
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800152
153 entry = (struct trace_entry *)raw_data;
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +0200154 tracing_generic_entry_update(entry, regs->flags, pc);
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800155 entry->type = type;
156
157 return raw_data;
158err:
159 perf_swevent_put_recursion_context(*rctxp);
160err_recursion:
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800161 return NULL;
162}
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100163EXPORT_SYMBOL_GPL(perf_trace_buf_prepare);