| .. | .. |
|---|
| 16 | 16 | #include <stdbool.h> |
|---|
| 17 | 17 | #include <errno.h> |
|---|
| 18 | 18 | #include <math.h> |
|---|
| 19 | +#include <linux/string.h> |
|---|
| 20 | +#include <linux/zalloc.h> |
|---|
| 19 | 21 | |
|---|
| 20 | 22 | #include "asm/bug.h" |
|---|
| 21 | 23 | |
|---|
| 24 | +#include "debug.h" |
|---|
| 25 | +#include "dso.h" |
|---|
| 26 | +#include "event.h" |
|---|
| 22 | 27 | #include "hist.h" |
|---|
| 23 | | -#include "util.h" |
|---|
| 24 | 28 | #include "sort.h" |
|---|
| 25 | 29 | #include "machine.h" |
|---|
| 30 | +#include "map.h" |
|---|
| 26 | 31 | #include "callchain.h" |
|---|
| 27 | 32 | #include "branch.h" |
|---|
| 33 | +#include "symbol.h" |
|---|
| 34 | +#include "../perf.h" |
|---|
| 28 | 35 | |
|---|
| 29 | 36 | #define CALLCHAIN_PARAM_DEFAULT \ |
|---|
| 30 | 37 | .mode = CHAIN_GRAPH_ABS, \ |
|---|
| .. | .. |
|---|
| 575 | 582 | return -1; |
|---|
| 576 | 583 | } |
|---|
| 577 | 584 | call->ip = cursor_node->ip; |
|---|
| 578 | | - call->ms.sym = cursor_node->sym; |
|---|
| 579 | | - call->ms.map = map__get(cursor_node->map); |
|---|
| 585 | + call->ms = cursor_node->ms; |
|---|
| 586 | + map__get(call->ms.map); |
|---|
| 580 | 587 | call->srcline = cursor_node->srcline; |
|---|
| 581 | 588 | |
|---|
| 582 | 589 | if (cursor_node->branch) { |
|---|
| .. | .. |
|---|
| 634 | 641 | struct callchain_list *call, *tmp; |
|---|
| 635 | 642 | |
|---|
| 636 | 643 | list_for_each_entry_safe(call, tmp, &new->val, list) { |
|---|
| 637 | | - list_del(&call->list); |
|---|
| 644 | + list_del_init(&call->list); |
|---|
| 638 | 645 | map__zput(call->ms.map); |
|---|
| 639 | 646 | free(call); |
|---|
| 640 | 647 | } |
|---|
| .. | .. |
|---|
| 713 | 720 | /* otherwise fall-back to symbol-based comparison below */ |
|---|
| 714 | 721 | __fallthrough; |
|---|
| 715 | 722 | case CCKEY_FUNCTION: |
|---|
| 716 | | - if (node->sym && cnode->ms.sym) { |
|---|
| 723 | + if (node->ms.sym && cnode->ms.sym) { |
|---|
| 717 | 724 | /* |
|---|
| 718 | 725 | * Compare inlined frames based on their symbol name |
|---|
| 719 | 726 | * because different inlined frames will have the same |
|---|
| 720 | 727 | * symbol start. Otherwise do a faster comparison based |
|---|
| 721 | 728 | * on the symbol start address. |
|---|
| 722 | 729 | */ |
|---|
| 723 | | - if (cnode->ms.sym->inlined || node->sym->inlined) { |
|---|
| 730 | + if (cnode->ms.sym->inlined || node->ms.sym->inlined) { |
|---|
| 724 | 731 | match = match_chain_strings(cnode->ms.sym->name, |
|---|
| 725 | | - node->sym->name); |
|---|
| 732 | + node->ms.sym->name); |
|---|
| 726 | 733 | if (match != MATCH_ERROR) |
|---|
| 727 | 734 | break; |
|---|
| 728 | 735 | } else { |
|---|
| 729 | 736 | match = match_chain_dso_addresses(cnode->ms.map, cnode->ms.sym->start, |
|---|
| 730 | | - node->map, node->sym->start); |
|---|
| 737 | + node->ms.map, node->ms.sym->start); |
|---|
| 731 | 738 | break; |
|---|
| 732 | 739 | } |
|---|
| 733 | 740 | } |
|---|
| .. | .. |
|---|
| 735 | 742 | __fallthrough; |
|---|
| 736 | 743 | case CCKEY_ADDRESS: |
|---|
| 737 | 744 | default: |
|---|
| 738 | | - match = match_chain_dso_addresses(cnode->ms.map, cnode->ip, node->map, node->ip); |
|---|
| 745 | + match = match_chain_dso_addresses(cnode->ms.map, cnode->ip, node->ms.map, node->ip); |
|---|
| 739 | 746 | break; |
|---|
| 740 | 747 | } |
|---|
| 741 | 748 | |
|---|
| .. | .. |
|---|
| 997 | 1004 | int err = 0; |
|---|
| 998 | 1005 | |
|---|
| 999 | 1006 | list_for_each_entry_safe(list, next_list, &src->val, list) { |
|---|
| 1000 | | - callchain_cursor_append(cursor, list->ip, |
|---|
| 1001 | | - list->ms.map, list->ms.sym, |
|---|
| 1007 | + callchain_cursor_append(cursor, list->ip, &list->ms, |
|---|
| 1002 | 1008 | false, NULL, 0, 0, 0, list->srcline); |
|---|
| 1003 | | - list_del(&list->list); |
|---|
| 1009 | + list_del_init(&list->list); |
|---|
| 1004 | 1010 | map__zput(list->ms.map); |
|---|
| 1005 | 1011 | free(list); |
|---|
| 1006 | 1012 | } |
|---|
| .. | .. |
|---|
| 1037 | 1043 | } |
|---|
| 1038 | 1044 | |
|---|
| 1039 | 1045 | int callchain_cursor_append(struct callchain_cursor *cursor, |
|---|
| 1040 | | - u64 ip, struct map *map, struct symbol *sym, |
|---|
| 1046 | + u64 ip, struct map_symbol *ms, |
|---|
| 1041 | 1047 | bool branch, struct branch_flags *flags, |
|---|
| 1042 | 1048 | int nr_loop_iter, u64 iter_cycles, u64 branch_from, |
|---|
| 1043 | 1049 | const char *srcline) |
|---|
| .. | .. |
|---|
| 1053 | 1059 | } |
|---|
| 1054 | 1060 | |
|---|
| 1055 | 1061 | node->ip = ip; |
|---|
| 1056 | | - map__zput(node->map); |
|---|
| 1057 | | - node->map = map__get(map); |
|---|
| 1058 | | - node->sym = sym; |
|---|
| 1062 | + map__zput(node->ms.map); |
|---|
| 1063 | + node->ms = *ms; |
|---|
| 1064 | + map__get(node->ms.map); |
|---|
| 1059 | 1065 | node->branch = branch; |
|---|
| 1060 | 1066 | node->nr_loop_iter = nr_loop_iter; |
|---|
| 1061 | 1067 | node->iter_cycles = iter_cycles; |
|---|
| .. | .. |
|---|
| 1075 | 1081 | |
|---|
| 1076 | 1082 | int sample__resolve_callchain(struct perf_sample *sample, |
|---|
| 1077 | 1083 | struct callchain_cursor *cursor, struct symbol **parent, |
|---|
| 1078 | | - struct perf_evsel *evsel, struct addr_location *al, |
|---|
| 1084 | + struct evsel *evsel, struct addr_location *al, |
|---|
| 1079 | 1085 | int max_stack) |
|---|
| 1080 | 1086 | { |
|---|
| 1081 | 1087 | if (sample->callchain == NULL && !symbol_conf.show_branchflag_count) |
|---|
| .. | .. |
|---|
| 1100 | 1106 | int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *node, |
|---|
| 1101 | 1107 | bool hide_unresolved) |
|---|
| 1102 | 1108 | { |
|---|
| 1103 | | - al->map = node->map; |
|---|
| 1104 | | - al->sym = node->sym; |
|---|
| 1109 | + al->maps = node->ms.maps; |
|---|
| 1110 | + al->map = node->ms.map; |
|---|
| 1111 | + al->sym = node->ms.sym; |
|---|
| 1105 | 1112 | al->srcline = node->srcline; |
|---|
| 1106 | 1113 | al->addr = node->ip; |
|---|
| 1107 | 1114 | |
|---|
| .. | .. |
|---|
| 1112 | 1119 | goto out; |
|---|
| 1113 | 1120 | } |
|---|
| 1114 | 1121 | |
|---|
| 1115 | | - if (al->map->groups == &al->machine->kmaps) { |
|---|
| 1116 | | - if (machine__is_host(al->machine)) { |
|---|
| 1122 | + if (al->maps == &al->maps->machine->kmaps) { |
|---|
| 1123 | + if (machine__is_host(al->maps->machine)) { |
|---|
| 1117 | 1124 | al->cpumode = PERF_RECORD_MISC_KERNEL; |
|---|
| 1118 | 1125 | al->level = 'k'; |
|---|
| 1119 | 1126 | } else { |
|---|
| .. | .. |
|---|
| 1121 | 1128 | al->level = 'g'; |
|---|
| 1122 | 1129 | } |
|---|
| 1123 | 1130 | } else { |
|---|
| 1124 | | - if (machine__is_host(al->machine)) { |
|---|
| 1131 | + if (machine__is_host(al->maps->machine)) { |
|---|
| 1125 | 1132 | al->cpumode = PERF_RECORD_MISC_USER; |
|---|
| 1126 | 1133 | al->level = '.'; |
|---|
| 1127 | 1134 | } else if (perf_guest) { |
|---|
| .. | .. |
|---|
| 1451 | 1458 | struct rb_node *n; |
|---|
| 1452 | 1459 | |
|---|
| 1453 | 1460 | list_for_each_entry_safe(list, tmp, &node->parent_val, list) { |
|---|
| 1454 | | - list_del(&list->list); |
|---|
| 1461 | + list_del_init(&list->list); |
|---|
| 1455 | 1462 | map__zput(list->ms.map); |
|---|
| 1456 | 1463 | free(list); |
|---|
| 1457 | 1464 | } |
|---|
| 1458 | 1465 | |
|---|
| 1459 | 1466 | list_for_each_entry_safe(list, tmp, &node->val, list) { |
|---|
| 1460 | | - list_del(&list->list); |
|---|
| 1467 | + list_del_init(&list->list); |
|---|
| 1461 | 1468 | map__zput(list->ms.map); |
|---|
| 1462 | 1469 | free(list); |
|---|
| 1463 | 1470 | } |
|---|
| .. | .. |
|---|
| 1542 | 1549 | |
|---|
| 1543 | 1550 | out: |
|---|
| 1544 | 1551 | list_for_each_entry_safe(chain, new, &head, list) { |
|---|
| 1545 | | - list_del(&chain->list); |
|---|
| 1552 | + list_del_init(&chain->list); |
|---|
| 1546 | 1553 | map__zput(chain->ms.map); |
|---|
| 1547 | 1554 | free(chain); |
|---|
| 1548 | 1555 | } |
|---|
| .. | .. |
|---|
| 1564 | 1571 | if (node == NULL) |
|---|
| 1565 | 1572 | break; |
|---|
| 1566 | 1573 | |
|---|
| 1567 | | - rc = callchain_cursor_append(dst, node->ip, node->map, node->sym, |
|---|
| 1574 | + rc = callchain_cursor_append(dst, node->ip, &node->ms, |
|---|
| 1568 | 1575 | node->branch, &node->branch_flags, |
|---|
| 1569 | 1576 | node->nr_loop_iter, |
|---|
| 1570 | 1577 | node->iter_cycles, |
|---|
| .. | .. |
|---|
| 1577 | 1584 | |
|---|
| 1578 | 1585 | return rc; |
|---|
| 1579 | 1586 | } |
|---|
| 1587 | + |
|---|
| 1588 | +/* |
|---|
| 1589 | + * Initialize a cursor before adding entries inside, but keep |
|---|
| 1590 | + * the previously allocated entries as a cache. |
|---|
| 1591 | + */ |
|---|
| 1592 | +void callchain_cursor_reset(struct callchain_cursor *cursor) |
|---|
| 1593 | +{ |
|---|
| 1594 | + struct callchain_cursor_node *node; |
|---|
| 1595 | + |
|---|
| 1596 | + cursor->nr = 0; |
|---|
| 1597 | + cursor->last = &cursor->first; |
|---|
| 1598 | + |
|---|
| 1599 | + for (node = cursor->first; node != NULL; node = node->next) |
|---|
| 1600 | + map__zput(node->ms.map); |
|---|
| 1601 | +} |
|---|
| 1602 | + |
|---|
| 1603 | +void callchain_param_setup(u64 sample_type) |
|---|
| 1604 | +{ |
|---|
| 1605 | + if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { |
|---|
| 1606 | + if ((sample_type & PERF_SAMPLE_REGS_USER) && |
|---|
| 1607 | + (sample_type & PERF_SAMPLE_STACK_USER)) { |
|---|
| 1608 | + callchain_param.record_mode = CALLCHAIN_DWARF; |
|---|
| 1609 | + dwarf_callchain_users = true; |
|---|
| 1610 | + } else if (sample_type & PERF_SAMPLE_BRANCH_STACK) |
|---|
| 1611 | + callchain_param.record_mode = CALLCHAIN_LBR; |
|---|
| 1612 | + else |
|---|
| 1613 | + callchain_param.record_mode = CALLCHAIN_FP; |
|---|
| 1614 | + } |
|---|
| 1615 | +} |
|---|
| 1616 | + |
|---|
| 1617 | +static bool chain_match(struct callchain_list *base_chain, |
|---|
| 1618 | + struct callchain_list *pair_chain) |
|---|
| 1619 | +{ |
|---|
| 1620 | + enum match_result match; |
|---|
| 1621 | + |
|---|
| 1622 | + match = match_chain_strings(base_chain->srcline, |
|---|
| 1623 | + pair_chain->srcline); |
|---|
| 1624 | + if (match != MATCH_ERROR) |
|---|
| 1625 | + return match == MATCH_EQ; |
|---|
| 1626 | + |
|---|
| 1627 | + match = match_chain_dso_addresses(base_chain->ms.map, |
|---|
| 1628 | + base_chain->ip, |
|---|
| 1629 | + pair_chain->ms.map, |
|---|
| 1630 | + pair_chain->ip); |
|---|
| 1631 | + |
|---|
| 1632 | + return match == MATCH_EQ; |
|---|
| 1633 | +} |
|---|
| 1634 | + |
|---|
| 1635 | +bool callchain_cnode_matched(struct callchain_node *base_cnode, |
|---|
| 1636 | + struct callchain_node *pair_cnode) |
|---|
| 1637 | +{ |
|---|
| 1638 | + struct callchain_list *base_chain, *pair_chain; |
|---|
| 1639 | + bool match = false; |
|---|
| 1640 | + |
|---|
| 1641 | + pair_chain = list_first_entry(&pair_cnode->val, |
|---|
| 1642 | + struct callchain_list, |
|---|
| 1643 | + list); |
|---|
| 1644 | + |
|---|
| 1645 | + list_for_each_entry(base_chain, &base_cnode->val, list) { |
|---|
| 1646 | + if (&pair_chain->list == &pair_cnode->val) |
|---|
| 1647 | + return false; |
|---|
| 1648 | + |
|---|
| 1649 | + if (!base_chain->srcline || !pair_chain->srcline) { |
|---|
| 1650 | + pair_chain = list_next_entry(pair_chain, list); |
|---|
| 1651 | + continue; |
|---|
| 1652 | + } |
|---|
| 1653 | + |
|---|
| 1654 | + match = chain_match(base_chain, pair_chain); |
|---|
| 1655 | + if (!match) |
|---|
| 1656 | + return false; |
|---|
| 1657 | + |
|---|
| 1658 | + pair_chain = list_next_entry(pair_chain, list); |
|---|
| 1659 | + } |
|---|
| 1660 | + |
|---|
| 1661 | + /* |
|---|
| 1662 | + * Say chain1 is ABC, chain2 is ABCD, we consider they are |
|---|
| 1663 | + * not fully matched. |
|---|
| 1664 | + */ |
|---|
| 1665 | + if (pair_chain && (&pair_chain->list != &pair_cnode->val)) |
|---|
| 1666 | + return false; |
|---|
| 1667 | + |
|---|
| 1668 | + return match; |
|---|
| 1669 | +} |
|---|
| 1670 | + |
|---|
| 1671 | +static u64 count_callchain_hits(struct hist_entry *he) |
|---|
| 1672 | +{ |
|---|
| 1673 | + struct rb_root *root = &he->sorted_chain; |
|---|
| 1674 | + struct rb_node *rb_node = rb_first(root); |
|---|
| 1675 | + struct callchain_node *node; |
|---|
| 1676 | + u64 chain_hits = 0; |
|---|
| 1677 | + |
|---|
| 1678 | + while (rb_node) { |
|---|
| 1679 | + node = rb_entry(rb_node, struct callchain_node, rb_node); |
|---|
| 1680 | + chain_hits += node->hit; |
|---|
| 1681 | + rb_node = rb_next(rb_node); |
|---|
| 1682 | + } |
|---|
| 1683 | + |
|---|
| 1684 | + return chain_hits; |
|---|
| 1685 | +} |
|---|
| 1686 | + |
|---|
| 1687 | +u64 callchain_total_hits(struct hists *hists) |
|---|
| 1688 | +{ |
|---|
| 1689 | + struct rb_node *next = rb_first_cached(&hists->entries); |
|---|
| 1690 | + u64 chain_hits = 0; |
|---|
| 1691 | + |
|---|
| 1692 | + while (next) { |
|---|
| 1693 | + struct hist_entry *he = rb_entry(next, struct hist_entry, |
|---|
| 1694 | + rb_node); |
|---|
| 1695 | + |
|---|
| 1696 | + chain_hits += count_callchain_hits(he); |
|---|
| 1697 | + next = rb_next(&he->rb_node); |
|---|
| 1698 | + } |
|---|
| 1699 | + |
|---|
| 1700 | + return chain_hits; |
|---|
| 1701 | +} |
|---|
| 1702 | + |
|---|
| 1703 | +s64 callchain_avg_cycles(struct callchain_node *cnode) |
|---|
| 1704 | +{ |
|---|
| 1705 | + struct callchain_list *chain; |
|---|
| 1706 | + s64 cycles = 0; |
|---|
| 1707 | + |
|---|
| 1708 | + list_for_each_entry(chain, &cnode->val, list) { |
|---|
| 1709 | + if (chain->srcline && chain->branch_count) |
|---|
| 1710 | + cycles += chain->cycles_count / chain->branch_count; |
|---|
| 1711 | + } |
|---|
| 1712 | + |
|---|
| 1713 | + return cycles; |
|---|
| 1714 | +} |
|---|