perf session: Add hooks to allow transparent decoding of AUX area tracing data
[deliverable/linux.git] / tools / perf / util / auxtrace.c
CommitLineData
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
41int 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
80void 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
88void 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
104void 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
122size_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
129static int auxtrace_not_supported(void)
130{
131 pr_err("AUX area tracing is not supported on this architecture\n");
132 return -EINVAL;
133}
134
135int 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
145void auxtrace_record__free(struct auxtrace_record *itr)
146{
147 if (itr)
148 itr->free(itr);
149}
150
151int 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
160u64 auxtrace_record__reference(struct auxtrace_record *itr)
161{
162 if (itr)
163 return itr->reference(itr);
164 return 0;
165}
166
167struct auxtrace_record *__weak
168auxtrace_record__init(struct perf_evlist *evlist __maybe_unused, int *err)
169{
170 *err = 0;
171 return NULL;
172}
173
174int 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);
198out_free:
199 free(ev);
200 return err;
201}
202
203int 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}
This page took 0.036178 seconds and 5 git commands to generate.