Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme (at) redhat.com>
      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 /* ANDROID_CHANGE_BEGIN */
    207 #ifndef __APPLE__
    208 	int cpu, thread;
    209 	unsigned long flags = 0;
    210 	int pid = -1;
    211 
    212 	if (evsel->fd == NULL &&
    213 	    perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0)
    214 		return -1;
    215 
    216 	if (evsel->cgrp) {
    217 		flags = PERF_FLAG_PID_CGROUP;
    218 		pid = evsel->cgrp->fd;
    219 	}
    220 
    221 	for (cpu = 0; cpu < cpus->nr; cpu++) {
    222 		int group_fd = -1;
    223 
    224 		for (thread = 0; thread < threads->nr; thread++) {
    225 
    226 			if (!evsel->cgrp)
    227 				pid = threads->map[thread];
    228 
    229 			FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
    230 								     pid,
    231 								     cpus->map[cpu],
    232 								     group_fd, flags);
    233 			if (FD(evsel, cpu, thread) < 0)
    234 				goto out_close;
    235 
    236 			if (group && group_fd == -1)
    237 				group_fd = FD(evsel, cpu, thread);
    238 		}
    239 	}
    240 
    241 	return 0;
    242 
    243 out_close:
    244 	do {
    245 		while (--thread >= 0) {
    246 			close(FD(evsel, cpu, thread));
    247 			FD(evsel, cpu, thread) = -1;
    248 		}
    249 		thread = threads->nr;
    250 	} while (--cpu >= 0);
    251 	return -1;
    252 #else
    253 	return -1;
    254 #endif
    255 /* ANDROID_CHANGE_END */
    256 }
    257 
    258 static struct {
    259 	struct cpu_map map;
    260 	int cpus[1];
    261 } empty_cpu_map = {
    262 	.map.nr	= 1,
    263 	.cpus	= { -1, },
    264 };
    265 
    266 static struct {
    267 	struct thread_map map;
    268 	int threads[1];
    269 } empty_thread_map = {
    270 	.map.nr	 = 1,
    271 	.threads = { -1, },
    272 };
    273 
    274 int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
    275 		     struct thread_map *threads, bool group)
    276 {
    277 	if (cpus == NULL) {
    278 		/* Work around old compiler warnings about strict aliasing */
    279 		cpus = &empty_cpu_map.map;
    280 	}
    281 
    282 	if (threads == NULL)
    283 		threads = &empty_thread_map.map;
    284 
    285 	return __perf_evsel__open(evsel, cpus, threads, group);
    286 }
    287 
    288 int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
    289 			     struct cpu_map *cpus, bool group)
    290 {
    291 	return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group);
    292 }
    293 
    294 int perf_evsel__open_per_thread(struct perf_evsel *evsel,
    295 				struct thread_map *threads, bool group)
    296 {
    297 	return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group);
    298 }
    299 
    300 static int perf_event__parse_id_sample(const union perf_event *event, u64 type,
    301 				       struct perf_sample *sample)
    302 {
    303 	const u64 *array = event->sample.array;
    304 
    305 	array += ((event->header.size -
    306 		   sizeof(event->header)) / sizeof(u64)) - 1;
    307 
    308 	if (type & PERF_SAMPLE_CPU) {
    309 		u32 *p = (u32 *)array;
    310 		sample->cpu = *p;
    311 		array--;
    312 	}
    313 
    314 	if (type & PERF_SAMPLE_STREAM_ID) {
    315 		sample->stream_id = *array;
    316 		array--;
    317 	}
    318 
    319 	if (type & PERF_SAMPLE_ID) {
    320 		sample->id = *array;
    321 		array--;
    322 	}
    323 
    324 	if (type & PERF_SAMPLE_TIME) {
    325 		sample->time = *array;
    326 		array--;
    327 	}
    328 
    329 	if (type & PERF_SAMPLE_TID) {
    330 		u32 *p = (u32 *)array;
    331 		sample->pid = p[0];
    332 		sample->tid = p[1];
    333 	}
    334 
    335 	return 0;
    336 }
    337 
    338 static bool sample_overlap(const union perf_event *event,
    339 			   const void *offset, u64 size)
    340 {
    341 	const void *base = event;
    342 
    343 	if (offset + size > base + event->header.size)
    344 		return true;
    345 
    346 	return false;
    347 }
    348 
    349 int perf_event__parse_sample(const union perf_event *event, u64 type,
    350 			     int sample_size, bool sample_id_all,
    351 			     struct perf_sample *data)
    352 {
    353 	const u64 *array;
    354 
    355 	data->cpu = data->pid = data->tid = -1;
    356 	data->stream_id = data->id = data->time = -1ULL;
    357 
    358 	if (event->header.type != PERF_RECORD_SAMPLE) {
    359 		if (!sample_id_all)
    360 			return 0;
    361 		return perf_event__parse_id_sample(event, type, data);
    362 	}
    363 
    364 	array = event->sample.array;
    365 
    366 	if (sample_size + sizeof(event->header) > event->header.size)
    367 		return -EFAULT;
    368 
    369 	if (type & PERF_SAMPLE_IP) {
    370 		data->ip = event->ip.ip;
    371 		array++;
    372 	}
    373 
    374 	if (type & PERF_SAMPLE_TID) {
    375 		u32 *p = (u32 *)array;
    376 		data->pid = p[0];
    377 		data->tid = p[1];
    378 		array++;
    379 	}
    380 
    381 	if (type & PERF_SAMPLE_TIME) {
    382 		data->time = *array;
    383 		array++;
    384 	}
    385 
    386 	if (type & PERF_SAMPLE_ADDR) {
    387 		data->addr = *array;
    388 		array++;
    389 	}
    390 
    391 	data->id = -1ULL;
    392 	if (type & PERF_SAMPLE_ID) {
    393 		data->id = *array;
    394 		array++;
    395 	}
    396 
    397 	if (type & PERF_SAMPLE_STREAM_ID) {
    398 		data->stream_id = *array;
    399 		array++;
    400 	}
    401 
    402 	if (type & PERF_SAMPLE_CPU) {
    403 		u32 *p = (u32 *)array;
    404 		data->cpu = *p;
    405 		array++;
    406 	}
    407 
    408 	if (type & PERF_SAMPLE_PERIOD) {
    409 		data->period = *array;
    410 		array++;
    411 	}
    412 
    413 	if (type & PERF_SAMPLE_READ) {
    414 		fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n");
    415 		return -1;
    416 	}
    417 
    418 	if (type & PERF_SAMPLE_CALLCHAIN) {
    419 		if (sample_overlap(event, array, sizeof(data->callchain->nr)))
    420 			return -EFAULT;
    421 
    422 		data->callchain = (struct ip_callchain *)array;
    423 
    424 		if (sample_overlap(event, array, data->callchain->nr))
    425 			return -EFAULT;
    426 
    427 		array += 1 + data->callchain->nr;
    428 	}
    429 
    430 	if (type & PERF_SAMPLE_RAW) {
    431 		u32 *p = (u32 *)array;
    432 
    433 		if (sample_overlap(event, array, sizeof(u32)))
    434 			return -EFAULT;
    435 
    436 		data->raw_size = *p;
    437 		p++;
    438 
    439 		if (sample_overlap(event, p, data->raw_size))
    440 			return -EFAULT;
    441 
    442 		data->raw_data = p;
    443 	}
    444 
    445 	return 0;
    446 }
    447