blob: 0cfcc37f63de74d4274b5e69eb263257331704bc [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
Namhyung Kim6016ee12010-08-11 12:47:59 +090012static char __percpu *perf_trace_buf[PERF_NR_CONTEXTS];
Frederic Weisbecker20ab4422009-09-18 06:10:28 +020013
Frederic Weisbeckereb1e7962010-03-23 00:08:59 +010014/*
15 * Force it to be aligned to unsigned long to avoid misaligned accesses
16 * suprises
17 */
18typedef typeof(unsigned long [PERF_MAX_TRACE_SIZE / sizeof(unsigned long)])
19 perf_trace_t;
Frederic Weisbeckerce71b9d2009-11-22 05:26:55 +010020
Frederic Weisbecker20ab4422009-09-18 06:10:28 +020021/* Count the events in use (per event id, not per instance) */
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010022static int total_ref_count;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +020023
Frederic Weisbecker61c32652010-11-18 01:39:17 +010024static int perf_trace_event_perm(struct ftrace_event_call *tp_event,
25 struct perf_event *p_event)
26{
27 /* No tracing, just counting, so no obvious leak */
28 if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW))
29 return 0;
30
31 /* Some events are ok to be traced by non-root users... */
32 if (p_event->attach_state == PERF_ATTACH_TASK) {
33 if (tp_event->flags & TRACE_EVENT_FL_CAP_ANY)
34 return 0;
35 }
36
37 /*
38 * ...otherwise raw tracepoint data can be a severe data leak,
39 * only allow root to have these.
40 */
41 if (perf_paranoid_tracepoint_raw() && !capable(CAP_SYS_ADMIN))
42 return -EPERM;
43
44 return 0;
45}
46
Jiri Olsaceec0b62012-02-15 15:51:49 +010047static int perf_trace_event_reg(struct ftrace_event_call *tp_event,
48 struct perf_event *p_event)
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020049{
Namhyung Kim6016ee12010-08-11 12:47:59 +090050 struct hlist_head __percpu *list;
Jiri Olsaceec0b62012-02-15 15:51:49 +010051 int ret = -ENOMEM;
Peter Zijlstra1c024eca2010-05-19 14:02:22 +020052 int cpu;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +020053
Peter Zijlstra1c024eca2010-05-19 14:02:22 +020054 p_event->tp_event = tp_event;
55 if (tp_event->perf_refcount++ > 0)
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020056 return 0;
57
Peter Zijlstra1c024eca2010-05-19 14:02:22 +020058 list = alloc_percpu(struct hlist_head);
59 if (!list)
60 goto fail;
61
62 for_each_possible_cpu(cpu)
63 INIT_HLIST_HEAD(per_cpu_ptr(list, cpu));
64
65 tp_event->perf_events = list;
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +020066
Frederic Weisbecker97d5a222010-03-05 05:35:37 +010067 if (!total_ref_count) {
Namhyung Kim6016ee12010-08-11 12:47:59 +090068 char __percpu *buf;
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020069 int i;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +020070
Frederic Weisbecker7ae07ea2010-08-14 20:45:13 +020071 for (i = 0; i < PERF_NR_CONTEXTS; i++) {
Namhyung Kim6016ee12010-08-11 12:47:59 +090072 buf = (char __percpu *)alloc_percpu(perf_trace_t);
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020073 if (!buf)
Peter Zijlstra1c024eca2010-05-19 14:02:22 +020074 goto fail;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +020075
Peter Zijlstra1c024eca2010-05-19 14:02:22 +020076 perf_trace_buf[i] = buf;
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020077 }
Frederic Weisbecker20ab4422009-09-18 06:10:28 +020078 }
79
Jiri Olsaceec0b62012-02-15 15:51:49 +010080 ret = tp_event->class->reg(tp_event, TRACE_REG_PERF_REGISTER, NULL);
Peter Zijlstra1c024eca2010-05-19 14:02:22 +020081 if (ret)
82 goto fail;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +020083
Peter Zijlstra1c024eca2010-05-19 14:02:22 +020084 total_ref_count++;
85 return 0;
86
87fail:
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020088 if (!total_ref_count) {
89 int i;
90
Frederic Weisbecker7ae07ea2010-08-14 20:45:13 +020091 for (i = 0; i < PERF_NR_CONTEXTS; i++) {
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +020092 free_percpu(perf_trace_buf[i]);
93 perf_trace_buf[i] = NULL;
94 }
Frederic Weisbecker20ab4422009-09-18 06:10:28 +020095 }
96
Peter Zijlstra1c024eca2010-05-19 14:02:22 +020097 if (!--tp_event->perf_refcount) {
98 free_percpu(tp_event->perf_events);
99 tp_event->perf_events = NULL;
Frederic Weisbeckerfe8e5b52009-10-03 14:55:18 +0200100 }
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200101
102 return ret;
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +0200103}
104
Jiri Olsaceec0b62012-02-15 15:51:49 +0100105static void perf_trace_event_unreg(struct perf_event *p_event)
106{
107 struct ftrace_event_call *tp_event = p_event->tp_event;
108 int i;
109
110 if (--tp_event->perf_refcount > 0)
111 goto out;
112
113 tp_event->class->reg(tp_event, TRACE_REG_PERF_UNREGISTER, NULL);
114
115 /*
116 * Ensure our callback won't be called anymore. The buffers
117 * will be freed after that.
118 */
119 tracepoint_synchronize_unregister();
120
121 free_percpu(tp_event->perf_events);
122 tp_event->perf_events = NULL;
123
124 if (!--total_ref_count) {
125 for (i = 0; i < PERF_NR_CONTEXTS; i++) {
126 free_percpu(perf_trace_buf[i]);
127 perf_trace_buf[i] = NULL;
128 }
129 }
130out:
131 module_put(tp_event->mod);
132}
133
134static int perf_trace_event_open(struct perf_event *p_event)
135{
136 struct ftrace_event_call *tp_event = p_event->tp_event;
137 return tp_event->class->reg(tp_event, TRACE_REG_PERF_OPEN, p_event);
138}
139
140static void perf_trace_event_close(struct perf_event *p_event)
141{
142 struct ftrace_event_call *tp_event = p_event->tp_event;
143 tp_event->class->reg(tp_event, TRACE_REG_PERF_CLOSE, p_event);
144}
145
146static int perf_trace_event_init(struct ftrace_event_call *tp_event,
147 struct perf_event *p_event)
148{
149 int ret;
150
151 ret = perf_trace_event_perm(tp_event, p_event);
152 if (ret)
153 return ret;
154
155 ret = perf_trace_event_reg(tp_event, p_event);
156 if (ret)
157 return ret;
158
159 ret = perf_trace_event_open(p_event);
160 if (ret) {
161 perf_trace_event_unreg(p_event);
162 return ret;
163 }
164
165 return 0;
166}
167
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200168int perf_trace_init(struct perf_event *p_event)
Peter Zijlstraac199db2009-03-19 20:26:15 +0100169{
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200170 struct ftrace_event_call *tp_event;
171 int event_id = p_event->attr.config;
Li Zefan20c89282009-05-06 10:33:45 +0800172 int ret = -EINVAL;
Peter Zijlstraac199db2009-03-19 20:26:15 +0100173
Li Zefan20c89282009-05-06 10:33:45 +0800174 mutex_lock(&event_mutex);
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200175 list_for_each_entry(tp_event, &ftrace_events, list) {
Steven Rostedtff5f1492010-05-21 11:49:57 -0400176 if (tp_event->event.type == event_id &&
Steven Rostedta1d0ce82010-06-08 11:22:06 -0400177 tp_event->class && tp_event->class->reg &&
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200178 try_module_get(tp_event->mod)) {
179 ret = perf_trace_event_init(tp_event, p_event);
Li Zefan9cb627d2010-09-01 12:58:43 +0200180 if (ret)
181 module_put(tp_event->mod);
Li Zefan20c89282009-05-06 10:33:45 +0800182 break;
183 }
Peter Zijlstraac199db2009-03-19 20:26:15 +0100184 }
Li Zefan20c89282009-05-06 10:33:45 +0800185 mutex_unlock(&event_mutex);
Peter Zijlstraac199db2009-03-19 20:26:15 +0100186
Li Zefan20c89282009-05-06 10:33:45 +0800187 return ret;
Peter Zijlstraac199db2009-03-19 20:26:15 +0100188}
189
Jiri Olsaceec0b62012-02-15 15:51:49 +0100190void perf_trace_destroy(struct perf_event *p_event)
191{
192 mutex_lock(&event_mutex);
193 perf_trace_event_close(p_event);
194 perf_trace_event_unreg(p_event);
195 mutex_unlock(&event_mutex);
196}
197
Peter Zijlstraa4eaf7f2010-06-16 14:37:10 +0200198int perf_trace_add(struct perf_event *p_event, int flags)
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +0200199{
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200200 struct ftrace_event_call *tp_event = p_event->tp_event;
Namhyung Kim6016ee12010-08-11 12:47:59 +0900201 struct hlist_head __percpu *pcpu_list;
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200202 struct hlist_head *list;
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200203
Namhyung Kim6016ee12010-08-11 12:47:59 +0900204 pcpu_list = tp_event->perf_events;
205 if (WARN_ON_ONCE(!pcpu_list))
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200206 return -EINVAL;
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +0200207
Peter Zijlstraa4eaf7f2010-06-16 14:37:10 +0200208 if (!(flags & PERF_EF_START))
209 p_event->hw.state = PERF_HES_STOPPED;
210
Namhyung Kim6016ee12010-08-11 12:47:59 +0900211 list = this_cpu_ptr(pcpu_list);
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200212 hlist_add_head_rcu(&p_event->hlist_entry, list);
Frederic Weisbecker20ab4422009-09-18 06:10:28 +0200213
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200214 return 0;
Frederic Weisbeckere5e25cf2009-09-18 00:54:43 +0200215}
216
Peter Zijlstraa4eaf7f2010-06-16 14:37:10 +0200217void perf_trace_del(struct perf_event *p_event, int flags)
Peter Zijlstraac199db2009-03-19 20:26:15 +0100218{
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200219 hlist_del_rcu(&p_event->hlist_entry);
220}
Peter Zijlstraac199db2009-03-19 20:26:15 +0100221
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100222__kprobes void *perf_trace_buf_prepare(int size, unsigned short type,
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +0200223 struct pt_regs *regs, int *rctxp)
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800224{
225 struct trace_entry *entry;
Peter Zijlstra87f44bb2010-05-25 11:02:55 +0200226 unsigned long flags;
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200227 char *raw_data;
Peter Zijlstrab7e2ece2010-05-19 10:52:27 +0200228 int pc;
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800229
Frederic Weisbeckereb1e7962010-03-23 00:08:59 +0100230 BUILD_BUG_ON(PERF_MAX_TRACE_SIZE % sizeof(unsigned long));
231
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800232 pc = preempt_count();
233
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800234 *rctxp = perf_swevent_get_recursion_context();
235 if (*rctxp < 0)
Peter Zijlstra1c024eca2010-05-19 14:02:22 +0200236 return NULL;
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800237
Peter Zijlstra3771f072010-05-21 12:31:09 +0200238 raw_data = this_cpu_ptr(perf_trace_buf[*rctxp]);
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800239
240 /* zero the dead bytes from align to not leak stack to user */
Frederic Weisbeckereb1e7962010-03-23 00:08:59 +0100241 memset(&raw_data[size - sizeof(u64)], 0, sizeof(u64));
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800242
243 entry = (struct trace_entry *)raw_data;
Peter Zijlstra87f44bb2010-05-25 11:02:55 +0200244 local_save_flags(flags);
245 tracing_generic_entry_update(entry, flags, pc);
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800246 entry->type = type;
247
248 return raw_data;
Xiao Guangrong430ad5a2010-01-28 09:32:29 +0800249}
Frederic Weisbecker97d5a222010-03-05 05:35:37 +0100250EXPORT_SYMBOL_GPL(perf_trace_buf_prepare);