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