diff options
Diffstat (limited to 'tools/perf/util/event.c')
-rw-r--r-- | tools/perf/util/event.c | 302 |
1 files changed, 58 insertions, 244 deletions
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 6715b1938725..3cf2c3e0605f 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include <linux/types.h> | 1 | #include <linux/types.h> |
2 | #include "event.h" | 2 | #include "event.h" |
3 | #include "debug.h" | 3 | #include "debug.h" |
4 | #include "machine.h" | ||
4 | #include "sort.h" | 5 | #include "sort.h" |
5 | #include "string.h" | 6 | #include "string.h" |
6 | #include "strlist.h" | 7 | #include "strlist.h" |
@@ -192,55 +193,43 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
192 | event->header.misc = PERF_RECORD_MISC_USER; | 193 | event->header.misc = PERF_RECORD_MISC_USER; |
193 | 194 | ||
194 | while (1) { | 195 | while (1) { |
195 | char bf[BUFSIZ], *pbf = bf; | 196 | char bf[BUFSIZ]; |
196 | int n; | 197 | char prot[5]; |
198 | char execname[PATH_MAX]; | ||
199 | char anonstr[] = "//anon"; | ||
197 | size_t size; | 200 | size_t size; |
201 | |||
198 | if (fgets(bf, sizeof(bf), fp) == NULL) | 202 | if (fgets(bf, sizeof(bf), fp) == NULL) |
199 | break; | 203 | break; |
200 | 204 | ||
205 | /* ensure null termination since stack will be reused. */ | ||
206 | strcpy(execname, ""); | ||
207 | |||
201 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | 208 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ |
202 | n = hex2u64(pbf, &event->mmap.start); | 209 | sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n", |
203 | if (n < 0) | 210 | &event->mmap.start, &event->mmap.len, prot, |
204 | continue; | 211 | &event->mmap.pgoff, execname); |
205 | pbf += n + 1; | 212 | |
206 | n = hex2u64(pbf, &event->mmap.len); | 213 | if (prot[2] != 'x') |
207 | if (n < 0) | ||
208 | continue; | 214 | continue; |
209 | pbf += n + 3; | 215 | |
210 | if (*pbf == 'x') { /* vm_exec */ | 216 | if (!strcmp(execname, "")) |
211 | char anonstr[] = "//anon\n"; | 217 | strcpy(execname, anonstr); |
212 | char *execname = strchr(bf, '/'); | 218 | |
213 | 219 | size = strlen(execname) + 1; | |
214 | /* Catch VDSO */ | 220 | memcpy(event->mmap.filename, execname, size); |
215 | if (execname == NULL) | 221 | size = PERF_ALIGN(size, sizeof(u64)); |
216 | execname = strstr(bf, "[vdso]"); | 222 | event->mmap.len -= event->mmap.start; |
217 | 223 | event->mmap.header.size = (sizeof(event->mmap) - | |
218 | /* Catch anonymous mmaps */ | 224 | (sizeof(event->mmap.filename) - size)); |
219 | if ((execname == NULL) && !strstr(bf, "[")) | 225 | memset(event->mmap.filename + size, 0, machine->id_hdr_size); |
220 | execname = anonstr; | 226 | event->mmap.header.size += machine->id_hdr_size; |
221 | 227 | event->mmap.pid = tgid; | |
222 | if (execname == NULL) | 228 | event->mmap.tid = pid; |
223 | continue; | 229 | |
224 | 230 | if (process(tool, event, &synth_sample, machine) != 0) { | |
225 | pbf += 3; | 231 | rc = -1; |
226 | n = hex2u64(pbf, &event->mmap.pgoff); | 232 | break; |
227 | |||
228 | size = strlen(execname); | ||
229 | execname[size - 1] = '\0'; /* Remove \n */ | ||
230 | memcpy(event->mmap.filename, execname, size); | ||
231 | size = PERF_ALIGN(size, sizeof(u64)); | ||
232 | event->mmap.len -= event->mmap.start; | ||
233 | event->mmap.header.size = (sizeof(event->mmap) - | ||
234 | (sizeof(event->mmap.filename) - size)); | ||
235 | memset(event->mmap.filename + size, 0, machine->id_hdr_size); | ||
236 | event->mmap.header.size += machine->id_hdr_size; | ||
237 | event->mmap.pid = tgid; | ||
238 | event->mmap.tid = pid; | ||
239 | |||
240 | if (process(tool, event, &synth_sample, machine) != 0) { | ||
241 | rc = -1; | ||
242 | break; | ||
243 | } | ||
244 | } | 233 | } |
245 | } | 234 | } |
246 | 235 | ||
@@ -404,16 +393,15 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
404 | 393 | ||
405 | if (*end) /* only interested in proper numerical dirents */ | 394 | if (*end) /* only interested in proper numerical dirents */ |
406 | continue; | 395 | continue; |
407 | 396 | /* | |
408 | if (__event__synthesize_thread(comm_event, mmap_event, pid, 1, | 397 | * We may race with exiting thread, so don't stop just because |
409 | process, tool, machine) != 0) { | 398 | * one thread couldn't be synthesized. |
410 | err = -1; | 399 | */ |
411 | goto out_closedir; | 400 | __event__synthesize_thread(comm_event, mmap_event, pid, 1, |
412 | } | 401 | process, tool, machine); |
413 | } | 402 | } |
414 | 403 | ||
415 | err = 0; | 404 | err = 0; |
416 | out_closedir: | ||
417 | closedir(proc); | 405 | closedir(proc); |
418 | out_free_mmap: | 406 | out_free_mmap: |
419 | free(mmap_event); | 407 | free(mmap_event); |
@@ -519,134 +507,15 @@ int perf_event__process_comm(struct perf_tool *tool __maybe_unused, | |||
519 | struct perf_sample *sample __maybe_unused, | 507 | struct perf_sample *sample __maybe_unused, |
520 | struct machine *machine) | 508 | struct machine *machine) |
521 | { | 509 | { |
522 | struct thread *thread = machine__findnew_thread(machine, event->comm.tid); | 510 | return machine__process_comm_event(machine, event); |
523 | |||
524 | if (dump_trace) | ||
525 | perf_event__fprintf_comm(event, stdout); | ||
526 | |||
527 | if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { | ||
528 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
529 | return -1; | ||
530 | } | ||
531 | |||
532 | return 0; | ||
533 | } | 511 | } |
534 | 512 | ||
535 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, | 513 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, |
536 | union perf_event *event, | 514 | union perf_event *event, |
537 | struct perf_sample *sample __maybe_unused, | 515 | struct perf_sample *sample __maybe_unused, |
538 | struct machine *machine __maybe_unused) | 516 | struct machine *machine) |
539 | { | ||
540 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", | ||
541 | event->lost.id, event->lost.lost); | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | static void perf_event__set_kernel_mmap_len(union perf_event *event, | ||
546 | struct map **maps) | ||
547 | { | ||
548 | maps[MAP__FUNCTION]->start = event->mmap.start; | ||
549 | maps[MAP__FUNCTION]->end = event->mmap.start + event->mmap.len; | ||
550 | /* | ||
551 | * Be a bit paranoid here, some perf.data file came with | ||
552 | * a zero sized synthesized MMAP event for the kernel. | ||
553 | */ | ||
554 | if (maps[MAP__FUNCTION]->end == 0) | ||
555 | maps[MAP__FUNCTION]->end = ~0ULL; | ||
556 | } | ||
557 | |||
558 | static int perf_event__process_kernel_mmap(struct perf_tool *tool | ||
559 | __maybe_unused, | ||
560 | union perf_event *event, | ||
561 | struct machine *machine) | ||
562 | { | 517 | { |
563 | struct map *map; | 518 | return machine__process_lost_event(machine, event); |
564 | char kmmap_prefix[PATH_MAX]; | ||
565 | enum dso_kernel_type kernel_type; | ||
566 | bool is_kernel_mmap; | ||
567 | |||
568 | machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); | ||
569 | if (machine__is_host(machine)) | ||
570 | kernel_type = DSO_TYPE_KERNEL; | ||
571 | else | ||
572 | kernel_type = DSO_TYPE_GUEST_KERNEL; | ||
573 | |||
574 | is_kernel_mmap = memcmp(event->mmap.filename, | ||
575 | kmmap_prefix, | ||
576 | strlen(kmmap_prefix) - 1) == 0; | ||
577 | if (event->mmap.filename[0] == '/' || | ||
578 | (!is_kernel_mmap && event->mmap.filename[0] == '[')) { | ||
579 | |||
580 | char short_module_name[1024]; | ||
581 | char *name, *dot; | ||
582 | |||
583 | if (event->mmap.filename[0] == '/') { | ||
584 | name = strrchr(event->mmap.filename, '/'); | ||
585 | if (name == NULL) | ||
586 | goto out_problem; | ||
587 | |||
588 | ++name; /* skip / */ | ||
589 | dot = strrchr(name, '.'); | ||
590 | if (dot == NULL) | ||
591 | goto out_problem; | ||
592 | snprintf(short_module_name, sizeof(short_module_name), | ||
593 | "[%.*s]", (int)(dot - name), name); | ||
594 | strxfrchar(short_module_name, '-', '_'); | ||
595 | } else | ||
596 | strcpy(short_module_name, event->mmap.filename); | ||
597 | |||
598 | map = machine__new_module(machine, event->mmap.start, | ||
599 | event->mmap.filename); | ||
600 | if (map == NULL) | ||
601 | goto out_problem; | ||
602 | |||
603 | name = strdup(short_module_name); | ||
604 | if (name == NULL) | ||
605 | goto out_problem; | ||
606 | |||
607 | map->dso->short_name = name; | ||
608 | map->dso->sname_alloc = 1; | ||
609 | map->end = map->start + event->mmap.len; | ||
610 | } else if (is_kernel_mmap) { | ||
611 | const char *symbol_name = (event->mmap.filename + | ||
612 | strlen(kmmap_prefix)); | ||
613 | /* | ||
614 | * Should be there already, from the build-id table in | ||
615 | * the header. | ||
616 | */ | ||
617 | struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, | ||
618 | kmmap_prefix); | ||
619 | if (kernel == NULL) | ||
620 | goto out_problem; | ||
621 | |||
622 | kernel->kernel = kernel_type; | ||
623 | if (__machine__create_kernel_maps(machine, kernel) < 0) | ||
624 | goto out_problem; | ||
625 | |||
626 | perf_event__set_kernel_mmap_len(event, machine->vmlinux_maps); | ||
627 | |||
628 | /* | ||
629 | * Avoid using a zero address (kptr_restrict) for the ref reloc | ||
630 | * symbol. Effectively having zero here means that at record | ||
631 | * time /proc/sys/kernel/kptr_restrict was non zero. | ||
632 | */ | ||
633 | if (event->mmap.pgoff != 0) { | ||
634 | maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, | ||
635 | symbol_name, | ||
636 | event->mmap.pgoff); | ||
637 | } | ||
638 | |||
639 | if (machine__is_default_guest(machine)) { | ||
640 | /* | ||
641 | * preload dso of guest kernel and modules | ||
642 | */ | ||
643 | dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION], | ||
644 | NULL); | ||
645 | } | ||
646 | } | ||
647 | return 0; | ||
648 | out_problem: | ||
649 | return -1; | ||
650 | } | 519 | } |
651 | 520 | ||
652 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | 521 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) |
@@ -656,43 +525,12 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | |||
656 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); | 525 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); |
657 | } | 526 | } |
658 | 527 | ||
659 | int perf_event__process_mmap(struct perf_tool *tool, | 528 | int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, |
660 | union perf_event *event, | 529 | union perf_event *event, |
661 | struct perf_sample *sample __maybe_unused, | 530 | struct perf_sample *sample __maybe_unused, |
662 | struct machine *machine) | 531 | struct machine *machine) |
663 | { | 532 | { |
664 | struct thread *thread; | 533 | return machine__process_mmap_event(machine, event); |
665 | struct map *map; | ||
666 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
667 | int ret = 0; | ||
668 | |||
669 | if (dump_trace) | ||
670 | perf_event__fprintf_mmap(event, stdout); | ||
671 | |||
672 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || | ||
673 | cpumode == PERF_RECORD_MISC_KERNEL) { | ||
674 | ret = perf_event__process_kernel_mmap(tool, event, machine); | ||
675 | if (ret < 0) | ||
676 | goto out_problem; | ||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | thread = machine__findnew_thread(machine, event->mmap.pid); | ||
681 | if (thread == NULL) | ||
682 | goto out_problem; | ||
683 | map = map__new(&machine->user_dsos, event->mmap.start, | ||
684 | event->mmap.len, event->mmap.pgoff, | ||
685 | event->mmap.pid, event->mmap.filename, | ||
686 | MAP__FUNCTION); | ||
687 | if (map == NULL) | ||
688 | goto out_problem; | ||
689 | |||
690 | thread__insert_map(thread, map); | ||
691 | return 0; | ||
692 | |||
693 | out_problem: | ||
694 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
695 | return 0; | ||
696 | } | 534 | } |
697 | 535 | ||
698 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) | 536 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) |
@@ -702,29 +540,20 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) | |||
702 | event->fork.ppid, event->fork.ptid); | 540 | event->fork.ppid, event->fork.ptid); |
703 | } | 541 | } |
704 | 542 | ||
705 | int perf_event__process_task(struct perf_tool *tool __maybe_unused, | 543 | int perf_event__process_fork(struct perf_tool *tool __maybe_unused, |
706 | union perf_event *event, | 544 | union perf_event *event, |
707 | struct perf_sample *sample __maybe_unused, | 545 | struct perf_sample *sample __maybe_unused, |
708 | struct machine *machine) | 546 | struct machine *machine) |
709 | { | 547 | { |
710 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); | 548 | return machine__process_fork_event(machine, event); |
711 | struct thread *parent = machine__findnew_thread(machine, event->fork.ptid); | 549 | } |
712 | |||
713 | if (dump_trace) | ||
714 | perf_event__fprintf_task(event, stdout); | ||
715 | |||
716 | if (event->header.type == PERF_RECORD_EXIT) { | ||
717 | machine__remove_thread(machine, thread); | ||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | if (thread == NULL || parent == NULL || | ||
722 | thread__fork(thread, parent) < 0) { | ||
723 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | ||
724 | return -1; | ||
725 | } | ||
726 | 550 | ||
727 | return 0; | 551 | int perf_event__process_exit(struct perf_tool *tool __maybe_unused, |
552 | union perf_event *event, | ||
553 | struct perf_sample *sample __maybe_unused, | ||
554 | struct machine *machine) | ||
555 | { | ||
556 | return machine__process_exit_event(machine, event); | ||
728 | } | 557 | } |
729 | 558 | ||
730 | size_t perf_event__fprintf(union perf_event *event, FILE *fp) | 559 | size_t perf_event__fprintf(union perf_event *event, FILE *fp) |
@@ -750,27 +579,12 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp) | |||
750 | return ret; | 579 | return ret; |
751 | } | 580 | } |
752 | 581 | ||
753 | int perf_event__process(struct perf_tool *tool, union perf_event *event, | 582 | int perf_event__process(struct perf_tool *tool __maybe_unused, |
754 | struct perf_sample *sample, struct machine *machine) | 583 | union perf_event *event, |
584 | struct perf_sample *sample __maybe_unused, | ||
585 | struct machine *machine) | ||
755 | { | 586 | { |
756 | switch (event->header.type) { | 587 | return machine__process_event(machine, event); |
757 | case PERF_RECORD_COMM: | ||
758 | perf_event__process_comm(tool, event, sample, machine); | ||
759 | break; | ||
760 | case PERF_RECORD_MMAP: | ||
761 | perf_event__process_mmap(tool, event, sample, machine); | ||
762 | break; | ||
763 | case PERF_RECORD_FORK: | ||
764 | case PERF_RECORD_EXIT: | ||
765 | perf_event__process_task(tool, event, sample, machine); | ||
766 | break; | ||
767 | case PERF_RECORD_LOST: | ||
768 | perf_event__process_lost(tool, event, sample, machine); | ||
769 | default: | ||
770 | break; | ||
771 | } | ||
772 | |||
773 | return 0; | ||
774 | } | 588 | } |
775 | 589 | ||
776 | void thread__find_addr_map(struct thread *self, | 590 | void thread__find_addr_map(struct thread *self, |