.. | .. |
---|
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 | +} |
---|