aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/session.c')
-rw-r--r--tools/perf/util/session.c431
1 files changed, 427 insertions, 4 deletions
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index ce3a6c8abe76..0de7258e70a5 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1,5 +1,8 @@
1#define _FILE_OFFSET_BITS 64
2
1#include <linux/kernel.h> 3#include <linux/kernel.h>
2 4
5#include <byteswap.h>
3#include <unistd.h> 6#include <unistd.h>
4#include <sys/types.h> 7#include <sys/types.h>
5 8
@@ -49,6 +52,11 @@ out_close:
49 return -1; 52 return -1;
50} 53}
51 54
55static inline int perf_session__create_kernel_maps(struct perf_session *self)
56{
57 return map_groups__create_kernel_maps(&self->kmaps, self->vmlinux_maps);
58}
59
52struct perf_session *perf_session__new(const char *filename, int mode, bool force) 60struct perf_session *perf_session__new(const char *filename, int mode, bool force)
53{ 61{
54 size_t len = filename ? strlen(filename) + 1 : 0; 62 size_t len = filename ? strlen(filename) + 1 : 0;
@@ -66,13 +74,22 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc
66 self->mmap_window = 32; 74 self->mmap_window = 32;
67 self->cwd = NULL; 75 self->cwd = NULL;
68 self->cwdlen = 0; 76 self->cwdlen = 0;
77 self->unknown_events = 0;
69 map_groups__init(&self->kmaps); 78 map_groups__init(&self->kmaps);
70 79
71 if (perf_session__create_kernel_maps(self) < 0) 80 if (mode == O_RDONLY) {
72 goto out_delete; 81 if (perf_session__open(self, force) < 0)
82 goto out_delete;
83 } else if (mode == O_WRONLY) {
84 /*
85 * In O_RDONLY mode this will be performed when reading the
86 * kernel MMAP event, in event__process_mmap().
87 */
88 if (perf_session__create_kernel_maps(self) < 0)
89 goto out_delete;
90 }
73 91
74 if (mode == O_RDONLY && perf_session__open(self, force) < 0) 92 self->sample_type = perf_header__sample_type(&self->header);
75 goto out_delete;
76out: 93out:
77 return self; 94 return self;
78out_free: 95out_free:
@@ -148,3 +165,409 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self,
148 165
149 return syms; 166 return syms;
150} 167}
168
169static int process_event_stub(event_t *event __used,
170 struct perf_session *session __used)
171{
172 dump_printf(": unhandled!\n");
173 return 0;
174}
175
176static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
177{
178 if (handler->sample == NULL)
179 handler->sample = process_event_stub;
180 if (handler->mmap == NULL)
181 handler->mmap = process_event_stub;
182 if (handler->comm == NULL)
183 handler->comm = process_event_stub;
184 if (handler->fork == NULL)
185 handler->fork = process_event_stub;
186 if (handler->exit == NULL)
187 handler->exit = process_event_stub;
188 if (handler->lost == NULL)
189 handler->lost = process_event_stub;
190 if (handler->read == NULL)
191 handler->read = process_event_stub;
192 if (handler->throttle == NULL)
193 handler->throttle = process_event_stub;
194 if (handler->unthrottle == NULL)
195 handler->unthrottle = process_event_stub;
196}
197
198static const char *event__name[] = {
199 [0] = "TOTAL",
200 [PERF_RECORD_MMAP] = "MMAP",
201 [PERF_RECORD_LOST] = "LOST",
202 [PERF_RECORD_COMM] = "COMM",
203 [PERF_RECORD_EXIT] = "EXIT",
204 [PERF_RECORD_THROTTLE] = "THROTTLE",
205 [PERF_RECORD_UNTHROTTLE] = "UNTHROTTLE",
206 [PERF_RECORD_FORK] = "FORK",
207 [PERF_RECORD_READ] = "READ",
208 [PERF_RECORD_SAMPLE] = "SAMPLE",
209};
210
211unsigned long event__total[PERF_RECORD_MAX];
212
213void event__print_totals(void)
214{
215 int i;
216 for (i = 0; i < PERF_RECORD_MAX; ++i)
217 pr_info("%10s events: %10ld\n",
218 event__name[i], event__total[i]);
219}
220
221void mem_bswap_64(void *src, int byte_size)
222{
223 u64 *m = src;
224
225 while (byte_size > 0) {
226 *m = bswap_64(*m);
227 byte_size -= sizeof(u64);
228 ++m;
229 }
230}
231
232static void event__all64_swap(event_t *self)
233{
234 struct perf_event_header *hdr = &self->header;
235 mem_bswap_64(hdr + 1, self->header.size - sizeof(*hdr));
236}
237
238static void event__comm_swap(event_t *self)
239{
240 self->comm.pid = bswap_32(self->comm.pid);
241 self->comm.tid = bswap_32(self->comm.tid);
242}
243
244static void event__mmap_swap(event_t *self)
245{
246 self->mmap.pid = bswap_32(self->mmap.pid);
247 self->mmap.tid = bswap_32(self->mmap.tid);
248 self->mmap.start = bswap_64(self->mmap.start);
249 self->mmap.len = bswap_64(self->mmap.len);
250 self->mmap.pgoff = bswap_64(self->mmap.pgoff);
251}
252
253static void event__task_swap(event_t *self)
254{
255 self->fork.pid = bswap_32(self->fork.pid);
256 self->fork.tid = bswap_32(self->fork.tid);
257 self->fork.ppid = bswap_32(self->fork.ppid);
258 self->fork.ptid = bswap_32(self->fork.ptid);
259 self->fork.time = bswap_64(self->fork.time);
260}
261
262static void event__read_swap(event_t *self)
263{
264 self->read.pid = bswap_32(self->read.pid);
265 self->read.tid = bswap_32(self->read.tid);
266 self->read.value = bswap_64(self->read.value);
267 self->read.time_enabled = bswap_64(self->read.time_enabled);
268 self->read.time_running = bswap_64(self->read.time_running);
269 self->read.id = bswap_64(self->read.id);
270}
271
272typedef void (*event__swap_op)(event_t *self);
273
274static event__swap_op event__swap_ops[] = {
275 [PERF_RECORD_MMAP] = event__mmap_swap,
276 [PERF_RECORD_COMM] = event__comm_swap,
277 [PERF_RECORD_FORK] = event__task_swap,
278 [PERF_RECORD_EXIT] = event__task_swap,
279 [PERF_RECORD_LOST] = event__all64_swap,
280 [PERF_RECORD_READ] = event__read_swap,
281 [PERF_RECORD_SAMPLE] = event__all64_swap,
282 [PERF_RECORD_MAX] = NULL,
283};
284
285static int perf_session__process_event(struct perf_session *self,
286 event_t *event,
287 struct perf_event_ops *ops,
288 u64 offset, u64 head)
289{
290 trace_event(event);
291
292 if (event->header.type < PERF_RECORD_MAX) {
293 dump_printf("%#Lx [%#x]: PERF_RECORD_%s",
294 offset + head, event->header.size,
295 event__name[event->header.type]);
296 ++event__total[0];
297 ++event__total[event->header.type];
298 }
299
300 if (self->header.needs_swap && event__swap_ops[event->header.type])
301 event__swap_ops[event->header.type](event);
302
303 switch (event->header.type) {
304 case PERF_RECORD_SAMPLE:
305 return ops->sample(event, self);
306 case PERF_RECORD_MMAP:
307 return ops->mmap(event, self);
308 case PERF_RECORD_COMM:
309 return ops->comm(event, self);
310 case PERF_RECORD_FORK:
311 return ops->fork(event, self);
312 case PERF_RECORD_EXIT:
313 return ops->exit(event, self);
314 case PERF_RECORD_LOST:
315 return ops->lost(event, self);
316 case PERF_RECORD_READ:
317 return ops->read(event, self);
318 case PERF_RECORD_THROTTLE:
319 return ops->throttle(event, self);
320 case PERF_RECORD_UNTHROTTLE:
321 return ops->unthrottle(event, self);
322 default:
323 self->unknown_events++;
324 return -1;
325 }
326}
327
328void perf_event_header__bswap(struct perf_event_header *self)
329{
330 self->type = bswap_32(self->type);
331 self->misc = bswap_16(self->misc);
332 self->size = bswap_16(self->size);
333}
334
335int perf_header__read_build_ids(struct perf_header *self,
336 int input, u64 offset, u64 size)
337{
338 struct build_id_event bev;
339 char filename[PATH_MAX];
340 u64 limit = offset + size;
341 int err = -1;
342
343 while (offset < limit) {
344 struct dso *dso;
345 ssize_t len;
346 struct list_head *head = &dsos__user;
347
348 if (read(input, &bev, sizeof(bev)) != sizeof(bev))
349 goto out;
350
351 if (self->needs_swap)
352 perf_event_header__bswap(&bev.header);
353
354 len = bev.header.size - sizeof(bev);
355 if (read(input, filename, len) != len)
356 goto out;
357
358 if (bev.header.misc & PERF_RECORD_MISC_KERNEL)
359 head = &dsos__kernel;
360
361 dso = __dsos__findnew(head, filename);
362 if (dso != NULL) {
363 dso__set_build_id(dso, &bev.build_id);
364 if (head == &dsos__kernel && filename[0] == '[')
365 dso->kernel = 1;
366 }
367
368 offset += bev.header.size;
369 }
370 err = 0;
371out:
372 return err;
373}
374
375static struct thread *perf_session__register_idle_thread(struct perf_session *self)
376{
377 struct thread *thread = perf_session__findnew(self, 0);
378
379 if (thread == NULL || thread__set_comm(thread, "swapper")) {
380 pr_err("problem inserting idle task.\n");
381 thread = NULL;
382 }
383
384 return thread;
385}
386
387int __perf_session__process_events(struct perf_session *self,
388 u64 data_offset, u64 data_size,
389 u64 file_size, struct perf_event_ops *ops)
390{
391 int err, mmap_prot, mmap_flags;
392 u64 head, shift;
393 u64 offset = 0;
394 size_t page_size;
395 event_t *event;
396 uint32_t size;
397 char *buf;
398
399 perf_event_ops__fill_defaults(ops);
400
401 page_size = sysconf(_SC_PAGESIZE);
402
403 head = data_offset;
404 shift = page_size * (head / page_size);
405 offset += shift;
406 head -= shift;
407
408 mmap_prot = PROT_READ;
409 mmap_flags = MAP_SHARED;
410
411 if (self->header.needs_swap) {
412 mmap_prot |= PROT_WRITE;
413 mmap_flags = MAP_PRIVATE;
414 }
415remap:
416 buf = mmap(NULL, page_size * self->mmap_window, mmap_prot,
417 mmap_flags, self->fd, offset);
418 if (buf == MAP_FAILED) {
419 pr_err("failed to mmap file\n");
420 err = -errno;
421 goto out_err;
422 }
423
424more:
425 event = (event_t *)(buf + head);
426
427 if (self->header.needs_swap)
428 perf_event_header__bswap(&event->header);
429 size = event->header.size;
430 if (size == 0)
431 size = 8;
432
433 if (head + event->header.size >= page_size * self->mmap_window) {
434 int munmap_ret;
435
436 shift = page_size * (head / page_size);
437
438 munmap_ret = munmap(buf, page_size * self->mmap_window);
439 assert(munmap_ret == 0);
440
441 offset += shift;
442 head -= shift;
443 goto remap;
444 }
445
446 size = event->header.size;
447
448 dump_printf("\n%#Lx [%#x]: event: %d\n",
449 offset + head, event->header.size, event->header.type);
450
451 if (size == 0 ||
452 perf_session__process_event(self, event, ops, offset, head) < 0) {
453 dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
454 offset + head, event->header.size,
455 event->header.type);
456 /*
457 * assume we lost track of the stream, check alignment, and
458 * increment a single u64 in the hope to catch on again 'soon'.
459 */
460 if (unlikely(head & 7))
461 head &= ~7ULL;
462
463 size = 8;
464 }
465
466 head += size;
467
468 if (offset + head >= data_offset + data_size)
469 goto done;
470
471 if (offset + head < file_size)
472 goto more;
473done:
474 err = 0;
475out_err:
476 return err;
477}
478
479int perf_session__process_events(struct perf_session *self,
480 struct perf_event_ops *ops)
481{
482 int err;
483
484 if (perf_session__register_idle_thread(self) == NULL)
485 return -ENOMEM;
486
487 if (!symbol_conf.full_paths) {
488 char bf[PATH_MAX];
489
490 if (getcwd(bf, sizeof(bf)) == NULL) {
491 err = -errno;
492out_getcwd_err:
493 pr_err("failed to get the current directory\n");
494 goto out_err;
495 }
496 self->cwd = strdup(bf);
497 if (self->cwd == NULL) {
498 err = -ENOMEM;
499 goto out_getcwd_err;
500 }
501 self->cwdlen = strlen(self->cwd);
502 }
503
504 err = __perf_session__process_events(self, self->header.data_offset,
505 self->header.data_size,
506 self->size, ops);
507out_err:
508 return err;
509}
510
511bool perf_session__has_traces(struct perf_session *self, const char *msg)
512{
513 if (!(self->sample_type & PERF_SAMPLE_RAW)) {
514 pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
515 return false;
516 }
517
518 return true;
519}
520
521int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self,
522 const char *symbol_name,
523 u64 addr)
524{
525 char *bracket;
526 enum map_type i;
527
528 self->ref_reloc_sym.name = strdup(symbol_name);
529 if (self->ref_reloc_sym.name == NULL)
530 return -ENOMEM;
531
532 bracket = strchr(self->ref_reloc_sym.name, ']');
533 if (bracket)
534 *bracket = '\0';
535
536 self->ref_reloc_sym.addr = addr;
537
538 for (i = 0; i < MAP__NR_TYPES; ++i) {
539 struct kmap *kmap = map__kmap(self->vmlinux_maps[i]);
540 kmap->ref_reloc_sym = &self->ref_reloc_sym;
541 }
542
543 return 0;
544}
545
546static u64 map__reloc_map_ip(struct map *map, u64 ip)
547{
548 return ip + (s64)map->pgoff;
549}
550
551static u64 map__reloc_unmap_ip(struct map *map, u64 ip)
552{
553 return ip - (s64)map->pgoff;
554}
555
556void map__reloc_vmlinux(struct map *self)
557{
558 struct kmap *kmap = map__kmap(self);
559 s64 reloc;
560
561 if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr)
562 return;
563
564 reloc = (kmap->ref_reloc_sym->unrelocated_addr -
565 kmap->ref_reloc_sym->addr);
566
567 if (!reloc)
568 return;
569
570 self->map_ip = map__reloc_map_ip;
571 self->unmap_ip = map__reloc_unmap_ip;
572 self->pgoff = reloc;
573}