Commit | Line | Data |
---|---|---|
718c602d AH |
1 | /* |
2 | * auxtrace.c: AUX area trace support | |
3 | * Copyright (c) 2013-2015, Intel Corporation. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms and conditions of the GNU General Public License, | |
7 | * version 2, as published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | */ | |
15 | ||
16 | #include <sys/types.h> | |
17 | #include <sys/mman.h> | |
18 | #include <stdbool.h> | |
19 | ||
20 | #include <linux/kernel.h> | |
21 | #include <linux/perf_event.h> | |
22 | #include <linux/types.h> | |
23 | #include <linux/bitops.h> | |
24 | #include <linux/log2.h> | |
25 | ||
9e0cc4fe AH |
26 | #include <stdlib.h> |
27 | #include <string.h> | |
28 | #include <errno.h> | |
29 | ||
718c602d AH |
30 | #include "../perf.h" |
31 | #include "util.h" | |
32 | #include "evlist.h" | |
33 | #include "cpumap.h" | |
34 | #include "thread_map.h" | |
35 | #include "asm/bug.h" | |
36 | #include "auxtrace.h" | |
37 | ||
9e0cc4fe AH |
38 | #include "event.h" |
39 | #include "debug.h" | |
40 | ||
718c602d AH |
41 | int auxtrace_mmap__mmap(struct auxtrace_mmap *mm, |
42 | struct auxtrace_mmap_params *mp, | |
43 | void *userpg, int fd) | |
44 | { | |
45 | struct perf_event_mmap_page *pc = userpg; | |
46 | ||
47 | #if BITS_PER_LONG != 64 && !defined(HAVE_SYNC_COMPARE_AND_SWAP_SUPPORT) | |
48 | pr_err("Cannot use AUX area tracing mmaps\n"); | |
49 | return -1; | |
50 | #endif | |
51 | ||
52 | WARN_ONCE(mm->base, "Uninitialized auxtrace_mmap\n"); | |
53 | ||
54 | mm->userpg = userpg; | |
55 | mm->mask = mp->mask; | |
56 | mm->len = mp->len; | |
57 | mm->prev = 0; | |
58 | mm->idx = mp->idx; | |
59 | mm->tid = mp->tid; | |
60 | mm->cpu = mp->cpu; | |
61 | ||
62 | if (!mp->len) { | |
63 | mm->base = NULL; | |
64 | return 0; | |
65 | } | |
66 | ||
67 | pc->aux_offset = mp->offset; | |
68 | pc->aux_size = mp->len; | |
69 | ||
70 | mm->base = mmap(NULL, mp->len, mp->prot, MAP_SHARED, fd, mp->offset); | |
71 | if (mm->base == MAP_FAILED) { | |
72 | pr_debug2("failed to mmap AUX area\n"); | |
73 | mm->base = NULL; | |
74 | return -1; | |
75 | } | |
76 | ||
77 | return 0; | |
78 | } | |
79 | ||
80 | void auxtrace_mmap__munmap(struct auxtrace_mmap *mm) | |
81 | { | |
82 | if (mm->base) { | |
83 | munmap(mm->base, mm->len); | |
84 | mm->base = NULL; | |
85 | } | |
86 | } | |
87 | ||
88 | void auxtrace_mmap_params__init(struct auxtrace_mmap_params *mp, | |
89 | off_t auxtrace_offset, | |
90 | unsigned int auxtrace_pages, | |
91 | bool auxtrace_overwrite) | |
92 | { | |
93 | if (auxtrace_pages) { | |
94 | mp->offset = auxtrace_offset; | |
95 | mp->len = auxtrace_pages * (size_t)page_size; | |
96 | mp->mask = is_power_of_2(mp->len) ? mp->len - 1 : 0; | |
97 | mp->prot = PROT_READ | (auxtrace_overwrite ? 0 : PROT_WRITE); | |
98 | pr_debug2("AUX area mmap length %zu\n", mp->len); | |
99 | } else { | |
100 | mp->len = 0; | |
101 | } | |
102 | } | |
103 | ||
104 | void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, | |
105 | struct perf_evlist *evlist, int idx, | |
106 | bool per_cpu) | |
107 | { | |
108 | mp->idx = idx; | |
109 | ||
110 | if (per_cpu) { | |
111 | mp->cpu = evlist->cpus->map[idx]; | |
112 | if (evlist->threads) | |
113 | mp->tid = evlist->threads->map[0]; | |
114 | else | |
115 | mp->tid = -1; | |
116 | } else { | |
117 | mp->cpu = -1; | |
118 | mp->tid = evlist->threads->map[idx]; | |
119 | } | |
120 | } | |
9e0cc4fe AH |
121 | |
122 | size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr) | |
123 | { | |
124 | if (itr) | |
125 | return itr->info_priv_size(itr); | |
126 | return 0; | |
127 | } | |
128 | ||
129 | static int auxtrace_not_supported(void) | |
130 | { | |
131 | pr_err("AUX area tracing is not supported on this architecture\n"); | |
132 | return -EINVAL; | |
133 | } | |
134 | ||
135 | int auxtrace_record__info_fill(struct auxtrace_record *itr, | |
136 | struct perf_session *session, | |
137 | struct auxtrace_info_event *auxtrace_info, | |
138 | size_t priv_size) | |
139 | { | |
140 | if (itr) | |
141 | return itr->info_fill(itr, session, auxtrace_info, priv_size); | |
142 | return auxtrace_not_supported(); | |
143 | } | |
144 | ||
145 | void auxtrace_record__free(struct auxtrace_record *itr) | |
146 | { | |
147 | if (itr) | |
148 | itr->free(itr); | |
149 | } | |
150 | ||
151 | int auxtrace_record__options(struct auxtrace_record *itr, | |
152 | struct perf_evlist *evlist, | |
153 | struct record_opts *opts) | |
154 | { | |
155 | if (itr) | |
156 | return itr->recording_options(itr, evlist, opts); | |
157 | return 0; | |
158 | } | |
159 | ||
160 | u64 auxtrace_record__reference(struct auxtrace_record *itr) | |
161 | { | |
162 | if (itr) | |
163 | return itr->reference(itr); | |
164 | return 0; | |
165 | } | |
166 | ||
167 | struct auxtrace_record *__weak | |
168 | auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, int *err) | |
169 | { | |
170 | *err = 0; | |
171 | return NULL; | |
172 | } | |
173 | ||
174 | int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, | |
175 | struct perf_tool *tool, | |
176 | struct perf_session *session, | |
177 | perf_event__handler_t process) | |
178 | { | |
179 | union perf_event *ev; | |
180 | size_t priv_size; | |
181 | int err; | |
182 | ||
183 | pr_debug2("Synthesizing auxtrace information\n"); | |
184 | priv_size = auxtrace_record__info_priv_size(itr); | |
185 | ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size); | |
186 | if (!ev) | |
187 | return -ENOMEM; | |
188 | ||
189 | ev->auxtrace_info.header.type = PERF_RECORD_AUXTRACE_INFO; | |
190 | ev->auxtrace_info.header.size = sizeof(struct auxtrace_info_event) + | |
191 | priv_size; | |
192 | err = auxtrace_record__info_fill(itr, session, &ev->auxtrace_info, | |
193 | priv_size); | |
194 | if (err) | |
195 | goto out_free; | |
196 | ||
197 | err = process(tool, ev, NULL, NULL); | |
198 | out_free: | |
199 | free(ev); | |
200 | return err; | |
201 | } | |
202 | ||
203 | int auxtrace_mmap__read(struct auxtrace_mmap *mm, struct auxtrace_record *itr, | |
204 | struct perf_tool *tool, process_auxtrace_t fn) | |
205 | { | |
206 | u64 head = auxtrace_mmap__read_head(mm); | |
207 | u64 old = mm->prev, offset, ref; | |
208 | unsigned char *data = mm->base; | |
209 | size_t size, head_off, old_off, len1, len2, padding; | |
210 | union perf_event ev; | |
211 | void *data1, *data2; | |
212 | ||
213 | if (old == head) | |
214 | return 0; | |
215 | ||
216 | pr_debug3("auxtrace idx %d old %#"PRIx64" head %#"PRIx64" diff %#"PRIx64"\n", | |
217 | mm->idx, old, head, head - old); | |
218 | ||
219 | if (mm->mask) { | |
220 | head_off = head & mm->mask; | |
221 | old_off = old & mm->mask; | |
222 | } else { | |
223 | head_off = head % mm->len; | |
224 | old_off = old % mm->len; | |
225 | } | |
226 | ||
227 | if (head_off > old_off) | |
228 | size = head_off - old_off; | |
229 | else | |
230 | size = mm->len - (old_off - head_off); | |
231 | ||
232 | ref = auxtrace_record__reference(itr); | |
233 | ||
234 | if (head > old || size <= head || mm->mask) { | |
235 | offset = head - size; | |
236 | } else { | |
237 | /* | |
238 | * When the buffer size is not a power of 2, 'head' wraps at the | |
239 | * highest multiple of the buffer size, so we have to subtract | |
240 | * the remainder here. | |
241 | */ | |
242 | u64 rem = (0ULL - mm->len) % mm->len; | |
243 | ||
244 | offset = head - size - rem; | |
245 | } | |
246 | ||
247 | if (size > head_off) { | |
248 | len1 = size - head_off; | |
249 | data1 = &data[mm->len - len1]; | |
250 | len2 = head_off; | |
251 | data2 = &data[0]; | |
252 | } else { | |
253 | len1 = size; | |
254 | data1 = &data[head_off - len1]; | |
255 | len2 = 0; | |
256 | data2 = NULL; | |
257 | } | |
258 | ||
259 | /* padding must be written by fn() e.g. record__process_auxtrace() */ | |
260 | padding = size & 7; | |
261 | if (padding) | |
262 | padding = 8 - padding; | |
263 | ||
264 | memset(&ev, 0, sizeof(ev)); | |
265 | ev.auxtrace.header.type = PERF_RECORD_AUXTRACE; | |
266 | ev.auxtrace.header.size = sizeof(ev.auxtrace); | |
267 | ev.auxtrace.size = size + padding; | |
268 | ev.auxtrace.offset = offset; | |
269 | ev.auxtrace.reference = ref; | |
270 | ev.auxtrace.idx = mm->idx; | |
271 | ev.auxtrace.tid = mm->tid; | |
272 | ev.auxtrace.cpu = mm->cpu; | |
273 | ||
274 | if (fn(tool, &ev, data1, len1, data2, len2)) | |
275 | return -1; | |
276 | ||
277 | mm->prev = head; | |
278 | ||
279 | auxtrace_mmap__write_tail(mm, head); | |
280 | if (itr->read_finish) { | |
281 | int err; | |
282 | ||
283 | err = itr->read_finish(itr, mm->idx); | |
284 | if (err < 0) | |
285 | return err; | |
286 | } | |
287 | ||
288 | return 1; | |
289 | } |