Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/tools/perf/util/evsel.c
10821 views
1
/*
2
* Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <[email protected]>
3
*
4
* Parts came from builtin-{top,stat,record}.c, see those files for further
5
* copyright notes.
6
*
7
* Released under the GPL v2. (and only v2, not any later version)
8
*/
9
10
#include "evsel.h"
11
#include "evlist.h"
12
#include "util.h"
13
#include "cpumap.h"
14
#include "thread_map.h"
15
16
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
17
18
int __perf_evsel__sample_size(u64 sample_type)
19
{
20
u64 mask = sample_type & PERF_SAMPLE_MASK;
21
int size = 0;
22
int i;
23
24
for (i = 0; i < 64; i++) {
25
if (mask & (1ULL << i))
26
size++;
27
}
28
29
size *= sizeof(u64);
30
31
return size;
32
}
33
34
void perf_evsel__init(struct perf_evsel *evsel,
35
struct perf_event_attr *attr, int idx)
36
{
37
evsel->idx = idx;
38
evsel->attr = *attr;
39
INIT_LIST_HEAD(&evsel->node);
40
}
41
42
struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
43
{
44
struct perf_evsel *evsel = zalloc(sizeof(*evsel));
45
46
if (evsel != NULL)
47
perf_evsel__init(evsel, attr, idx);
48
49
return evsel;
50
}
51
52
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
53
{
54
int cpu, thread;
55
evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int));
56
57
if (evsel->fd) {
58
for (cpu = 0; cpu < ncpus; cpu++) {
59
for (thread = 0; thread < nthreads; thread++) {
60
FD(evsel, cpu, thread) = -1;
61
}
62
}
63
}
64
65
return evsel->fd != NULL ? 0 : -ENOMEM;
66
}
67
68
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
69
{
70
evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id));
71
if (evsel->sample_id == NULL)
72
return -ENOMEM;
73
74
evsel->id = zalloc(ncpus * nthreads * sizeof(u64));
75
if (evsel->id == NULL) {
76
xyarray__delete(evsel->sample_id);
77
evsel->sample_id = NULL;
78
return -ENOMEM;
79
}
80
81
return 0;
82
}
83
84
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus)
85
{
86
evsel->counts = zalloc((sizeof(*evsel->counts) +
87
(ncpus * sizeof(struct perf_counts_values))));
88
return evsel->counts != NULL ? 0 : -ENOMEM;
89
}
90
91
void perf_evsel__free_fd(struct perf_evsel *evsel)
92
{
93
xyarray__delete(evsel->fd);
94
evsel->fd = NULL;
95
}
96
97
void perf_evsel__free_id(struct perf_evsel *evsel)
98
{
99
xyarray__delete(evsel->sample_id);
100
evsel->sample_id = NULL;
101
free(evsel->id);
102
evsel->id = NULL;
103
}
104
105
void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
106
{
107
int cpu, thread;
108
109
for (cpu = 0; cpu < ncpus; cpu++)
110
for (thread = 0; thread < nthreads; ++thread) {
111
close(FD(evsel, cpu, thread));
112
FD(evsel, cpu, thread) = -1;
113
}
114
}
115
116
void perf_evsel__exit(struct perf_evsel *evsel)
117
{
118
assert(list_empty(&evsel->node));
119
xyarray__delete(evsel->fd);
120
xyarray__delete(evsel->sample_id);
121
free(evsel->id);
122
}
123
124
void perf_evsel__delete(struct perf_evsel *evsel)
125
{
126
perf_evsel__exit(evsel);
127
close_cgroup(evsel->cgrp);
128
free(evsel->name);
129
free(evsel);
130
}
131
132
int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
133
int cpu, int thread, bool scale)
134
{
135
struct perf_counts_values count;
136
size_t nv = scale ? 3 : 1;
137
138
if (FD(evsel, cpu, thread) < 0)
139
return -EINVAL;
140
141
if (evsel->counts == NULL && perf_evsel__alloc_counts(evsel, cpu + 1) < 0)
142
return -ENOMEM;
143
144
if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0)
145
return -errno;
146
147
if (scale) {
148
if (count.run == 0)
149
count.val = 0;
150
else if (count.run < count.ena)
151
count.val = (u64)((double)count.val * count.ena / count.run + 0.5);
152
} else
153
count.ena = count.run = 0;
154
155
evsel->counts->cpu[cpu] = count;
156
return 0;
157
}
158
159
int __perf_evsel__read(struct perf_evsel *evsel,
160
int ncpus, int nthreads, bool scale)
161
{
162
size_t nv = scale ? 3 : 1;
163
int cpu, thread;
164
struct perf_counts_values *aggr = &evsel->counts->aggr, count;
165
166
aggr->val = aggr->ena = aggr->run = 0;
167
168
for (cpu = 0; cpu < ncpus; cpu++) {
169
for (thread = 0; thread < nthreads; thread++) {
170
if (FD(evsel, cpu, thread) < 0)
171
continue;
172
173
if (readn(FD(evsel, cpu, thread),
174
&count, nv * sizeof(u64)) < 0)
175
return -errno;
176
177
aggr->val += count.val;
178
if (scale) {
179
aggr->ena += count.ena;
180
aggr->run += count.run;
181
}
182
}
183
}
184
185
evsel->counts->scaled = 0;
186
if (scale) {
187
if (aggr->run == 0) {
188
evsel->counts->scaled = -1;
189
aggr->val = 0;
190
return 0;
191
}
192
193
if (aggr->run < aggr->ena) {
194
evsel->counts->scaled = 1;
195
aggr->val = (u64)((double)aggr->val * aggr->ena / aggr->run + 0.5);
196
}
197
} else
198
aggr->ena = aggr->run = 0;
199
200
return 0;
201
}
202
203
static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
204
struct thread_map *threads, bool group)
205
{
206
int cpu, thread;
207
unsigned long flags = 0;
208
int pid = -1;
209
210
if (evsel->fd == NULL &&
211
perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0)
212
return -1;
213
214
if (evsel->cgrp) {
215
flags = PERF_FLAG_PID_CGROUP;
216
pid = evsel->cgrp->fd;
217
}
218
219
for (cpu = 0; cpu < cpus->nr; cpu++) {
220
int group_fd = -1;
221
222
for (thread = 0; thread < threads->nr; thread++) {
223
224
if (!evsel->cgrp)
225
pid = threads->map[thread];
226
227
FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
228
pid,
229
cpus->map[cpu],
230
group_fd, flags);
231
if (FD(evsel, cpu, thread) < 0)
232
goto out_close;
233
234
if (group && group_fd == -1)
235
group_fd = FD(evsel, cpu, thread);
236
}
237
}
238
239
return 0;
240
241
out_close:
242
do {
243
while (--thread >= 0) {
244
close(FD(evsel, cpu, thread));
245
FD(evsel, cpu, thread) = -1;
246
}
247
thread = threads->nr;
248
} while (--cpu >= 0);
249
return -1;
250
}
251
252
static struct {
253
struct cpu_map map;
254
int cpus[1];
255
} empty_cpu_map = {
256
.map.nr = 1,
257
.cpus = { -1, },
258
};
259
260
static struct {
261
struct thread_map map;
262
int threads[1];
263
} empty_thread_map = {
264
.map.nr = 1,
265
.threads = { -1, },
266
};
267
268
int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
269
struct thread_map *threads, bool group)
270
{
271
if (cpus == NULL) {
272
/* Work around old compiler warnings about strict aliasing */
273
cpus = &empty_cpu_map.map;
274
}
275
276
if (threads == NULL)
277
threads = &empty_thread_map.map;
278
279
return __perf_evsel__open(evsel, cpus, threads, group);
280
}
281
282
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
283
struct cpu_map *cpus, bool group)
284
{
285
return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group);
286
}
287
288
int perf_evsel__open_per_thread(struct perf_evsel *evsel,
289
struct thread_map *threads, bool group)
290
{
291
return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group);
292
}
293
294
static int perf_event__parse_id_sample(const union perf_event *event, u64 type,
295
struct perf_sample *sample)
296
{
297
const u64 *array = event->sample.array;
298
299
array += ((event->header.size -
300
sizeof(event->header)) / sizeof(u64)) - 1;
301
302
if (type & PERF_SAMPLE_CPU) {
303
u32 *p = (u32 *)array;
304
sample->cpu = *p;
305
array--;
306
}
307
308
if (type & PERF_SAMPLE_STREAM_ID) {
309
sample->stream_id = *array;
310
array--;
311
}
312
313
if (type & PERF_SAMPLE_ID) {
314
sample->id = *array;
315
array--;
316
}
317
318
if (type & PERF_SAMPLE_TIME) {
319
sample->time = *array;
320
array--;
321
}
322
323
if (type & PERF_SAMPLE_TID) {
324
u32 *p = (u32 *)array;
325
sample->pid = p[0];
326
sample->tid = p[1];
327
}
328
329
return 0;
330
}
331
332
static bool sample_overlap(const union perf_event *event,
333
const void *offset, u64 size)
334
{
335
const void *base = event;
336
337
if (offset + size > base + event->header.size)
338
return true;
339
340
return false;
341
}
342
343
int perf_event__parse_sample(const union perf_event *event, u64 type,
344
int sample_size, bool sample_id_all,
345
struct perf_sample *data)
346
{
347
const u64 *array;
348
349
data->cpu = data->pid = data->tid = -1;
350
data->stream_id = data->id = data->time = -1ULL;
351
352
if (event->header.type != PERF_RECORD_SAMPLE) {
353
if (!sample_id_all)
354
return 0;
355
return perf_event__parse_id_sample(event, type, data);
356
}
357
358
array = event->sample.array;
359
360
if (sample_size + sizeof(event->header) > event->header.size)
361
return -EFAULT;
362
363
if (type & PERF_SAMPLE_IP) {
364
data->ip = event->ip.ip;
365
array++;
366
}
367
368
if (type & PERF_SAMPLE_TID) {
369
u32 *p = (u32 *)array;
370
data->pid = p[0];
371
data->tid = p[1];
372
array++;
373
}
374
375
if (type & PERF_SAMPLE_TIME) {
376
data->time = *array;
377
array++;
378
}
379
380
if (type & PERF_SAMPLE_ADDR) {
381
data->addr = *array;
382
array++;
383
}
384
385
data->id = -1ULL;
386
if (type & PERF_SAMPLE_ID) {
387
data->id = *array;
388
array++;
389
}
390
391
if (type & PERF_SAMPLE_STREAM_ID) {
392
data->stream_id = *array;
393
array++;
394
}
395
396
if (type & PERF_SAMPLE_CPU) {
397
u32 *p = (u32 *)array;
398
data->cpu = *p;
399
array++;
400
}
401
402
if (type & PERF_SAMPLE_PERIOD) {
403
data->period = *array;
404
array++;
405
}
406
407
if (type & PERF_SAMPLE_READ) {
408
fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n");
409
return -1;
410
}
411
412
if (type & PERF_SAMPLE_CALLCHAIN) {
413
if (sample_overlap(event, array, sizeof(data->callchain->nr)))
414
return -EFAULT;
415
416
data->callchain = (struct ip_callchain *)array;
417
418
if (sample_overlap(event, array, data->callchain->nr))
419
return -EFAULT;
420
421
array += 1 + data->callchain->nr;
422
}
423
424
if (type & PERF_SAMPLE_RAW) {
425
u32 *p = (u32 *)array;
426
427
if (sample_overlap(event, array, sizeof(u32)))
428
return -EFAULT;
429
430
data->raw_size = *p;
431
p++;
432
433
if (sample_overlap(event, p, data->raw_size))
434
return -EFAULT;
435
436
data->raw_data = p;
437
}
438
439
return 0;
440
}
441
442