1 /* 2 * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@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 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