Blame view
kernel/trace/trace_event_perf.c
4.68 KB
ac199db01 ftrace: event pro... |
1 |
/* |
97d5a2200 perf: Drop the ob... |
2 |
* trace event based perf event profiling/tracing |
ac199db01 ftrace: event pro... |
3 4 |
* * Copyright (C) 2009 Red Hat Inc, Peter Zijlstra <pzijlstr@redhat.com> |
c530665c3 perf: Take a hot ... |
5 |
* Copyright (C) 2009-2010 Frederic Weisbecker <fweisbec@gmail.com> |
ac199db01 ftrace: event pro... |
6 |
*/ |
558e6547e tracing/profile: ... |
7 |
#include <linux/module.h> |
430ad5a60 perf: Factorize t... |
8 |
#include <linux/kprobes.h> |
ac199db01 ftrace: event pro... |
9 |
#include "trace.h" |
6016ee13d perf, tracing: ad... |
10 |
static char __percpu *perf_trace_buf[PERF_NR_CONTEXTS]; |
20ab4425a tracing: Allocate... |
11 |
|
eb1e79611 perf: Correctly a... |
12 13 14 15 16 17 |
/* * Force it to be aligned to unsigned long to avoid misaligned accesses * suprises */ typedef typeof(unsigned long [PERF_MAX_TRACE_SIZE / sizeof(unsigned long)]) perf_trace_t; |
ce71b9df8 tracing: Use the ... |
18 |
|
20ab4425a tracing: Allocate... |
19 |
/* Count the events in use (per event id, not per instance) */ |
97d5a2200 perf: Drop the ob... |
20 |
static int total_ref_count; |
20ab4425a tracing: Allocate... |
21 |
|
61c32659b tracing: New flag... |
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
static int perf_trace_event_perm(struct ftrace_event_call *tp_event, struct perf_event *p_event) { /* No tracing, just counting, so no obvious leak */ if (!(p_event->attr.sample_type & PERF_SAMPLE_RAW)) return 0; /* Some events are ok to be traced by non-root users... */ if (p_event->attach_state == PERF_ATTACH_TASK) { if (tp_event->flags & TRACE_EVENT_FL_CAP_ANY) return 0; } /* * ...otherwise raw tracepoint data can be a severe data leak, * only allow root to have these. */ if (perf_paranoid_tracepoint_raw() && !capable(CAP_SYS_ADMIN)) return -EPERM; return 0; } |
1c024eca5 perf, trace: Opti... |
44 45 |
static int perf_trace_event_init(struct ftrace_event_call *tp_event, struct perf_event *p_event) |
e5e25cf47 tracing: Factoriz... |
46 |
{ |
6016ee13d perf, tracing: ad... |
47 |
struct hlist_head __percpu *list; |
61c32659b tracing: New flag... |
48 |
int ret; |
1c024eca5 perf, trace: Opti... |
49 |
int cpu; |
20ab4425a tracing: Allocate... |
50 |
|
61c32659b tracing: New flag... |
51 52 53 |
ret = perf_trace_event_perm(tp_event, p_event); if (ret) return ret; |
1c024eca5 perf, trace: Opti... |
54 55 |
p_event->tp_event = tp_event; if (tp_event->perf_refcount++ > 0) |
e5e25cf47 tracing: Factoriz... |
56 |
return 0; |
61c32659b tracing: New flag... |
57 |
ret = -ENOMEM; |
1c024eca5 perf, trace: Opti... |
58 59 60 61 62 63 |
list = alloc_percpu(struct hlist_head); if (!list) goto fail; for_each_possible_cpu(cpu) INIT_HLIST_HEAD(per_cpu_ptr(list, cpu)); |
20ab4425a tracing: Allocate... |
64 |
|
1c024eca5 perf, trace: Opti... |
65 |
tp_event->perf_events = list; |
e5e25cf47 tracing: Factoriz... |
66 |
|
97d5a2200 perf: Drop the ob... |
67 |
if (!total_ref_count) { |
6016ee13d perf, tracing: ad... |
68 |
char __percpu *buf; |
b7e2ecef9 perf, trace: Opti... |
69 |
int i; |
20ab4425a tracing: Allocate... |
70 |
|
7ae07ea3a perf: Humanize th... |
71 |
for (i = 0; i < PERF_NR_CONTEXTS; i++) { |
6016ee13d perf, tracing: ad... |
72 |
buf = (char __percpu *)alloc_percpu(perf_trace_t); |
b7e2ecef9 perf, trace: Opti... |
73 |
if (!buf) |
1c024eca5 perf, trace: Opti... |
74 |
goto fail; |
20ab4425a tracing: Allocate... |
75 |
|
1c024eca5 perf, trace: Opti... |
76 |
perf_trace_buf[i] = buf; |
b7e2ecef9 perf, trace: Opti... |
77 |
} |
20ab4425a tracing: Allocate... |
78 |
} |
a1d0ce821 tracing: Use clas... |
79 |
ret = tp_event->class->reg(tp_event, TRACE_REG_PERF_REGISTER); |
1c024eca5 perf, trace: Opti... |
80 81 |
if (ret) goto fail; |
20ab4425a tracing: Allocate... |
82 |
|
1c024eca5 perf, trace: Opti... |
83 84 85 86 |
total_ref_count++; return 0; fail: |
97d5a2200 perf: Drop the ob... |
87 |
if (!total_ref_count) { |
b7e2ecef9 perf, trace: Opti... |
88 |
int i; |
7ae07ea3a perf: Humanize th... |
89 |
for (i = 0; i < PERF_NR_CONTEXTS; i++) { |
b7e2ecef9 perf, trace: Opti... |
90 91 92 |
free_percpu(perf_trace_buf[i]); perf_trace_buf[i] = NULL; } |
fe8e5b5a6 tracing: Check to... |
93 |
} |
1c024eca5 perf, trace: Opti... |
94 95 96 97 |
if (!--tp_event->perf_refcount) { free_percpu(tp_event->perf_events); tp_event->perf_events = NULL; |
fe8e5b5a6 tracing: Check to... |
98 |
} |
20ab4425a tracing: Allocate... |
99 100 |
return ret; |
e5e25cf47 tracing: Factoriz... |
101 |
} |
1c024eca5 perf, trace: Opti... |
102 |
int perf_trace_init(struct perf_event *p_event) |
ac199db01 ftrace: event pro... |
103 |
{ |
1c024eca5 perf, trace: Opti... |
104 105 |
struct ftrace_event_call *tp_event; int event_id = p_event->attr.config; |
20c8928ab tracing/events: f... |
106 |
int ret = -EINVAL; |
ac199db01 ftrace: event pro... |
107 |
|
20c8928ab tracing/events: f... |
108 |
mutex_lock(&event_mutex); |
1c024eca5 perf, trace: Opti... |
109 |
list_for_each_entry(tp_event, &ftrace_events, list) { |
ff5f149b6 Merge branch 'per... |
110 |
if (tp_event->event.type == event_id && |
a1d0ce821 tracing: Use clas... |
111 |
tp_event->class && tp_event->class->reg && |
1c024eca5 perf, trace: Opti... |
112 113 |
try_module_get(tp_event->mod)) { ret = perf_trace_event_init(tp_event, p_event); |
9cb627d5f perf, trace: Fix ... |
114 115 |
if (ret) module_put(tp_event->mod); |
20c8928ab tracing/events: f... |
116 117 |
break; } |
ac199db01 ftrace: event pro... |
118 |
} |
20c8928ab tracing/events: f... |
119 |
mutex_unlock(&event_mutex); |
ac199db01 ftrace: event pro... |
120 |
|
20c8928ab tracing/events: f... |
121 |
return ret; |
ac199db01 ftrace: event pro... |
122 |
} |
a4eaf7f14 perf: Rework the ... |
123 |
int perf_trace_add(struct perf_event *p_event, int flags) |
e5e25cf47 tracing: Factoriz... |
124 |
{ |
1c024eca5 perf, trace: Opti... |
125 |
struct ftrace_event_call *tp_event = p_event->tp_event; |
6016ee13d perf, tracing: ad... |
126 |
struct hlist_head __percpu *pcpu_list; |
1c024eca5 perf, trace: Opti... |
127 |
struct hlist_head *list; |
20ab4425a tracing: Allocate... |
128 |
|
6016ee13d perf, tracing: ad... |
129 130 |
pcpu_list = tp_event->perf_events; if (WARN_ON_ONCE(!pcpu_list)) |
1c024eca5 perf, trace: Opti... |
131 |
return -EINVAL; |
20ab4425a tracing: Allocate... |
132 |
|
a4eaf7f14 perf: Rework the ... |
133 134 |
if (!(flags & PERF_EF_START)) p_event->hw.state = PERF_HES_STOPPED; |
6016ee13d perf, tracing: ad... |
135 |
list = this_cpu_ptr(pcpu_list); |
1c024eca5 perf, trace: Opti... |
136 |
hlist_add_head_rcu(&p_event->hlist_entry, list); |
20ab4425a tracing: Allocate... |
137 |
|
1c024eca5 perf, trace: Opti... |
138 139 |
return 0; } |
20ab4425a tracing: Allocate... |
140 |
|
a4eaf7f14 perf: Rework the ... |
141 |
void perf_trace_del(struct perf_event *p_event, int flags) |
1c024eca5 perf, trace: Opti... |
142 143 |
{ hlist_del_rcu(&p_event->hlist_entry); |
e5e25cf47 tracing: Factoriz... |
144 |
} |
1c024eca5 perf, trace: Opti... |
145 |
void perf_trace_destroy(struct perf_event *p_event) |
ac199db01 ftrace: event pro... |
146 |
{ |
1c024eca5 perf, trace: Opti... |
147 148 |
struct ftrace_event_call *tp_event = p_event->tp_event; int i; |
ac199db01 ftrace: event pro... |
149 |
|
2e97942fe perf_events, trac... |
150 |
mutex_lock(&event_mutex); |
1c024eca5 perf, trace: Opti... |
151 |
if (--tp_event->perf_refcount > 0) |
2e97942fe perf_events, trac... |
152 |
goto out; |
1c024eca5 perf, trace: Opti... |
153 |
|
a1d0ce821 tracing: Use clas... |
154 |
tp_event->class->reg(tp_event, TRACE_REG_PERF_UNREGISTER); |
1c024eca5 perf, trace: Opti... |
155 |
|
3771f0771 perf_events, trac... |
156 |
/* |
669336e4c perf: Use tracepo... |
157 158 |
* Ensure our callback won't be called anymore. The buffers * will be freed after that. |
3771f0771 perf_events, trac... |
159 |
*/ |
669336e4c perf: Use tracepo... |
160 |
tracepoint_synchronize_unregister(); |
3771f0771 perf_events, trac... |
161 |
|
1c024eca5 perf, trace: Opti... |
162 163 164 165 |
free_percpu(tp_event->perf_events); tp_event->perf_events = NULL; if (!--total_ref_count) { |
7ae07ea3a perf: Humanize th... |
166 |
for (i = 0; i < PERF_NR_CONTEXTS; i++) { |
1c024eca5 perf, trace: Opti... |
167 168 |
free_percpu(perf_trace_buf[i]); perf_trace_buf[i] = NULL; |
20c8928ab tracing/events: f... |
169 |
} |
ac199db01 ftrace: event pro... |
170 |
} |
2e97942fe perf_events, trac... |
171 |
out: |
9cb627d5f perf, trace: Fix ... |
172 |
module_put(tp_event->mod); |
2e97942fe perf_events, trac... |
173 |
mutex_unlock(&event_mutex); |
ac199db01 ftrace: event pro... |
174 |
} |
430ad5a60 perf: Factorize t... |
175 |
|
97d5a2200 perf: Drop the ob... |
176 |
__kprobes void *perf_trace_buf_prepare(int size, unsigned short type, |
b7e2ecef9 perf, trace: Opti... |
177 |
struct pt_regs *regs, int *rctxp) |
430ad5a60 perf: Factorize t... |
178 179 |
{ struct trace_entry *entry; |
87f44bbc2 perf, trace: Fix ... |
180 |
unsigned long flags; |
1c024eca5 perf, trace: Opti... |
181 |
char *raw_data; |
b7e2ecef9 perf, trace: Opti... |
182 |
int pc; |
430ad5a60 perf: Factorize t... |
183 |
|
eb1e79611 perf: Correctly a... |
184 |
BUILD_BUG_ON(PERF_MAX_TRACE_SIZE % sizeof(unsigned long)); |
430ad5a60 perf: Factorize t... |
185 |
pc = preempt_count(); |
430ad5a60 perf: Factorize t... |
186 187 |
*rctxp = perf_swevent_get_recursion_context(); if (*rctxp < 0) |
1c024eca5 perf, trace: Opti... |
188 |
return NULL; |
430ad5a60 perf: Factorize t... |
189 |
|
3771f0771 perf_events, trac... |
190 |
raw_data = this_cpu_ptr(perf_trace_buf[*rctxp]); |
430ad5a60 perf: Factorize t... |
191 192 |
/* zero the dead bytes from align to not leak stack to user */ |
eb1e79611 perf: Correctly a... |
193 |
memset(&raw_data[size - sizeof(u64)], 0, sizeof(u64)); |
430ad5a60 perf: Factorize t... |
194 195 |
entry = (struct trace_entry *)raw_data; |
87f44bbc2 perf, trace: Fix ... |
196 197 |
local_save_flags(flags); tracing_generic_entry_update(entry, flags, pc); |
430ad5a60 perf: Factorize t... |
198 199 200 |
entry->type = type; return raw_data; |
430ad5a60 perf: Factorize t... |
201 |
} |
97d5a2200 perf: Drop the ob... |
202 |
EXPORT_SYMBOL_GPL(perf_trace_buf_prepare); |