diff options
Diffstat (limited to 'tools/perf/util/annotate.c')
-rw-r--r-- | tools/perf/util/annotate.c | 128 |
1 files changed, 125 insertions, 3 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 03b7bc70eb66..d1eece70b84d 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -473,17 +473,73 @@ int symbol__alloc_hist(struct symbol *sym) | |||
473 | return 0; | 473 | return 0; |
474 | } | 474 | } |
475 | 475 | ||
476 | /* The cycles histogram is lazily allocated. */ | ||
477 | static int symbol__alloc_hist_cycles(struct symbol *sym) | ||
478 | { | ||
479 | struct annotation *notes = symbol__annotation(sym); | ||
480 | const size_t size = symbol__size(sym); | ||
481 | |||
482 | notes->src->cycles_hist = calloc(size, sizeof(struct cyc_hist)); | ||
483 | if (notes->src->cycles_hist == NULL) | ||
484 | return -1; | ||
485 | return 0; | ||
486 | } | ||
487 | |||
476 | void symbol__annotate_zero_histograms(struct symbol *sym) | 488 | void symbol__annotate_zero_histograms(struct symbol *sym) |
477 | { | 489 | { |
478 | struct annotation *notes = symbol__annotation(sym); | 490 | struct annotation *notes = symbol__annotation(sym); |
479 | 491 | ||
480 | pthread_mutex_lock(¬es->lock); | 492 | pthread_mutex_lock(¬es->lock); |
481 | if (notes->src != NULL) | 493 | if (notes->src != NULL) { |
482 | memset(notes->src->histograms, 0, | 494 | memset(notes->src->histograms, 0, |
483 | notes->src->nr_histograms * notes->src->sizeof_sym_hist); | 495 | notes->src->nr_histograms * notes->src->sizeof_sym_hist); |
496 | if (notes->src->cycles_hist) | ||
497 | memset(notes->src->cycles_hist, 0, | ||
498 | symbol__size(sym) * sizeof(struct cyc_hist)); | ||
499 | } | ||
484 | pthread_mutex_unlock(¬es->lock); | 500 | pthread_mutex_unlock(¬es->lock); |
485 | } | 501 | } |
486 | 502 | ||
503 | static int __symbol__account_cycles(struct annotation *notes, | ||
504 | u64 start, | ||
505 | unsigned offset, unsigned cycles, | ||
506 | unsigned have_start) | ||
507 | { | ||
508 | struct cyc_hist *ch; | ||
509 | |||
510 | ch = notes->src->cycles_hist; | ||
511 | /* | ||
512 | * For now we can only account one basic block per | ||
513 | * final jump. But multiple could be overlapping. | ||
514 | * Always account the longest one. So when | ||
515 | * a shorter one has been already seen throw it away. | ||
516 | * | ||
517 | * We separately always account the full cycles. | ||
518 | */ | ||
519 | ch[offset].num_aggr++; | ||
520 | ch[offset].cycles_aggr += cycles; | ||
521 | |||
522 | if (!have_start && ch[offset].have_start) | ||
523 | return 0; | ||
524 | if (ch[offset].num) { | ||
525 | if (have_start && (!ch[offset].have_start || | ||
526 | ch[offset].start > start)) { | ||
527 | ch[offset].have_start = 0; | ||
528 | ch[offset].cycles = 0; | ||
529 | ch[offset].num = 0; | ||
530 | if (ch[offset].reset < 0xffff) | ||
531 | ch[offset].reset++; | ||
532 | } else if (have_start && | ||
533 | ch[offset].start < start) | ||
534 | return 0; | ||
535 | } | ||
536 | ch[offset].have_start = have_start; | ||
537 | ch[offset].start = start; | ||
538 | ch[offset].cycles += cycles; | ||
539 | ch[offset].num++; | ||
540 | return 0; | ||
541 | } | ||
542 | |||
487 | static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, | 543 | static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, |
488 | struct annotation *notes, int evidx, u64 addr) | 544 | struct annotation *notes, int evidx, u64 addr) |
489 | { | 545 | { |
@@ -506,7 +562,7 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, | |||
506 | return 0; | 562 | return 0; |
507 | } | 563 | } |
508 | 564 | ||
509 | static struct annotation *symbol__get_annotation(struct symbol *sym) | 565 | static struct annotation *symbol__get_annotation(struct symbol *sym, bool cycles) |
510 | { | 566 | { |
511 | struct annotation *notes = symbol__annotation(sym); | 567 | struct annotation *notes = symbol__annotation(sym); |
512 | 568 | ||
@@ -514,6 +570,10 @@ static struct annotation *symbol__get_annotation(struct symbol *sym) | |||
514 | if (symbol__alloc_hist(sym) < 0) | 570 | if (symbol__alloc_hist(sym) < 0) |
515 | return NULL; | 571 | return NULL; |
516 | } | 572 | } |
573 | if (!notes->src->cycles_hist && cycles) { | ||
574 | if (symbol__alloc_hist_cycles(sym) < 0) | ||
575 | return NULL; | ||
576 | } | ||
517 | return notes; | 577 | return notes; |
518 | } | 578 | } |
519 | 579 | ||
@@ -524,12 +584,73 @@ static int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | |||
524 | 584 | ||
525 | if (sym == NULL) | 585 | if (sym == NULL) |
526 | return 0; | 586 | return 0; |
527 | notes = symbol__get_annotation(sym); | 587 | notes = symbol__get_annotation(sym, false); |
528 | if (notes == NULL) | 588 | if (notes == NULL) |
529 | return -ENOMEM; | 589 | return -ENOMEM; |
530 | return __symbol__inc_addr_samples(sym, map, notes, evidx, addr); | 590 | return __symbol__inc_addr_samples(sym, map, notes, evidx, addr); |
531 | } | 591 | } |
532 | 592 | ||
593 | static int symbol__account_cycles(u64 addr, u64 start, | ||
594 | struct symbol *sym, unsigned cycles) | ||
595 | { | ||
596 | struct annotation *notes; | ||
597 | unsigned offset; | ||
598 | |||
599 | if (sym == NULL) | ||
600 | return 0; | ||
601 | notes = symbol__get_annotation(sym, true); | ||
602 | if (notes == NULL) | ||
603 | return -ENOMEM; | ||
604 | if (addr < sym->start || addr >= sym->end) | ||
605 | return -ERANGE; | ||
606 | |||
607 | if (start) { | ||
608 | if (start < sym->start || start >= sym->end) | ||
609 | return -ERANGE; | ||
610 | if (start >= addr) | ||
611 | start = 0; | ||
612 | } | ||
613 | offset = addr - sym->start; | ||
614 | return __symbol__account_cycles(notes, | ||
615 | start ? start - sym->start : 0, | ||
616 | offset, cycles, | ||
617 | !!start); | ||
618 | } | ||
619 | |||
620 | int addr_map_symbol__account_cycles(struct addr_map_symbol *ams, | ||
621 | struct addr_map_symbol *start, | ||
622 | unsigned cycles) | ||
623 | { | ||
624 | u64 saddr = 0; | ||
625 | int err; | ||
626 | |||
627 | if (!cycles) | ||
628 | return 0; | ||
629 | |||
630 | /* | ||
631 | * Only set start when IPC can be computed. We can only | ||
632 | * compute it when the basic block is completely in a single | ||
633 | * function. | ||
634 | * Special case the case when the jump is elsewhere, but | ||
635 | * it starts on the function start. | ||
636 | */ | ||
637 | if (start && | ||
638 | (start->sym == ams->sym || | ||
639 | (ams->sym && | ||
640 | start->addr == ams->sym->start + ams->map->start))) | ||
641 | saddr = start->al_addr; | ||
642 | if (saddr == 0) | ||
643 | pr_debug2("BB with bad start: addr %"PRIx64" start %"PRIx64" sym %"PRIx64" saddr %"PRIx64"\n", | ||
644 | ams->addr, | ||
645 | start ? start->addr : 0, | ||
646 | ams->sym ? ams->sym->start + ams->map->start : 0, | ||
647 | saddr); | ||
648 | err = symbol__account_cycles(ams->al_addr, saddr, ams->sym, cycles); | ||
649 | if (err) | ||
650 | pr_debug2("account_cycles failed %d\n", err); | ||
651 | return err; | ||
652 | } | ||
653 | |||
533 | int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx) | 654 | int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx) |
534 | { | 655 | { |
535 | return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr); | 656 | return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr); |
@@ -1005,6 +1126,7 @@ fallback: | |||
1005 | dso->annotate_warned = 1; | 1126 | dso->annotate_warned = 1; |
1006 | pr_err("Can't annotate %s:\n\n" | 1127 | pr_err("Can't annotate %s:\n\n" |
1007 | "No vmlinux file%s\nwas found in the path.\n\n" | 1128 | "No vmlinux file%s\nwas found in the path.\n\n" |
1129 | "Note that annotation using /proc/kcore requires CAP_SYS_RAWIO capability.\n\n" | ||
1008 | "Please use:\n\n" | 1130 | "Please use:\n\n" |
1009 | " perf buildid-cache -vu vmlinux\n\n" | 1131 | " perf buildid-cache -vu vmlinux\n\n" |
1010 | "or:\n\n" | 1132 | "or:\n\n" |