diff options
| -rw-r--r-- | arch/x86/kernel/cpu/perf_counter.c | 253 | ||||
| -rw-r--r-- | include/linux/perf_counter.h | 15 | ||||
| -rw-r--r-- | init/Kconfig | 2 | ||||
| -rw-r--r-- | kernel/fork.c | 9 | ||||
| -rw-r--r-- | kernel/perf_counter.c | 185 | ||||
| -rw-r--r-- | tools/perf/Documentation/perf-report.txt | 15 | ||||
| -rw-r--r-- | tools/perf/Makefile | 2 | ||||
| -rw-r--r-- | tools/perf/builtin-annotate.c | 24 | ||||
| -rw-r--r-- | tools/perf/builtin-record.c | 13 | ||||
| -rw-r--r-- | tools/perf/builtin-report.c | 255 | ||||
| -rw-r--r-- | tools/perf/builtin-stat.c | 3 | ||||
| -rw-r--r-- | tools/perf/builtin-top.c | 6 | ||||
| -rw-r--r-- | tools/perf/perf.c | 77 | ||||
| -rw-r--r-- | tools/perf/perf.h | 8 | ||||
| -rw-r--r-- | tools/perf/util/cache.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/header.h | 2 | ||||
| -rw-r--r-- | tools/perf/util/include/linux/kernel.h | 8 | ||||
| -rw-r--r-- | tools/perf/util/parse-events.c | 182 | ||||
| -rw-r--r-- | tools/perf/util/parse-events.h | 5 | ||||
| -rw-r--r-- | tools/perf/util/string.h | 3 | ||||
| -rw-r--r-- | tools/perf/util/strlist.c | 20 | ||||
| -rw-r--r-- | tools/perf/util/strlist.h | 11 | ||||
| -rw-r--r-- | tools/perf/util/symbol.c | 145 | ||||
| -rw-r--r-- | tools/perf/util/symbol.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/util.h | 2 |
25 files changed, 948 insertions, 299 deletions
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index 36c3dc7b8991..a7aa8f900954 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c | |||
| @@ -66,6 +66,52 @@ static DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters) = { | |||
| 66 | }; | 66 | }; |
| 67 | 67 | ||
| 68 | /* | 68 | /* |
| 69 | * Not sure about some of these | ||
| 70 | */ | ||
| 71 | static const u64 p6_perfmon_event_map[] = | ||
| 72 | { | ||
| 73 | [PERF_COUNT_HW_CPU_CYCLES] = 0x0079, | ||
| 74 | [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0, | ||
| 75 | [PERF_COUNT_HW_CACHE_REFERENCES] = 0x0000, | ||
| 76 | [PERF_COUNT_HW_CACHE_MISSES] = 0x0000, | ||
| 77 | [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4, | ||
| 78 | [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5, | ||
| 79 | [PERF_COUNT_HW_BUS_CYCLES] = 0x0062, | ||
| 80 | }; | ||
| 81 | |||
| 82 | static u64 p6_pmu_event_map(int event) | ||
| 83 | { | ||
| 84 | return p6_perfmon_event_map[event]; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* | ||
| 88 | * Counter setting that is specified not to count anything. | ||
| 89 | * We use this to effectively disable a counter. | ||
| 90 | * | ||
| 91 | * L2_RQSTS with 0 MESI unit mask. | ||
| 92 | */ | ||
| 93 | #define P6_NOP_COUNTER 0x0000002EULL | ||
| 94 | |||
| 95 | static u64 p6_pmu_raw_event(u64 event) | ||
| 96 | { | ||
| 97 | #define P6_EVNTSEL_EVENT_MASK 0x000000FFULL | ||
| 98 | #define P6_EVNTSEL_UNIT_MASK 0x0000FF00ULL | ||
| 99 | #define P6_EVNTSEL_EDGE_MASK 0x00040000ULL | ||
| 100 | #define P6_EVNTSEL_INV_MASK 0x00800000ULL | ||
| 101 | #define P6_EVNTSEL_COUNTER_MASK 0xFF000000ULL | ||
| 102 | |||
| 103 | #define P6_EVNTSEL_MASK \ | ||
| 104 | (P6_EVNTSEL_EVENT_MASK | \ | ||
| 105 | P6_EVNTSEL_UNIT_MASK | \ | ||
| 106 | P6_EVNTSEL_EDGE_MASK | \ | ||
| 107 | P6_EVNTSEL_INV_MASK | \ | ||
| 108 | P6_EVNTSEL_COUNTER_MASK) | ||
| 109 | |||
| 110 | return event & P6_EVNTSEL_MASK; | ||
| 111 | } | ||
| 112 | |||
| 113 | |||
| 114 | /* | ||
| 69 | * Intel PerfMon v3. Used on Core2 and later. | 115 | * Intel PerfMon v3. Used on Core2 and later. |
| 70 | */ | 116 | */ |
| 71 | static const u64 intel_perfmon_event_map[] = | 117 | static const u64 intel_perfmon_event_map[] = |
| @@ -666,6 +712,7 @@ static int __hw_perf_counter_init(struct perf_counter *counter) | |||
| 666 | { | 712 | { |
| 667 | struct perf_counter_attr *attr = &counter->attr; | 713 | struct perf_counter_attr *attr = &counter->attr; |
| 668 | struct hw_perf_counter *hwc = &counter->hw; | 714 | struct hw_perf_counter *hwc = &counter->hw; |
| 715 | u64 config; | ||
| 669 | int err; | 716 | int err; |
| 670 | 717 | ||
| 671 | if (!x86_pmu_initialized()) | 718 | if (!x86_pmu_initialized()) |
| @@ -718,14 +765,40 @@ static int __hw_perf_counter_init(struct perf_counter *counter) | |||
| 718 | 765 | ||
| 719 | if (attr->config >= x86_pmu.max_events) | 766 | if (attr->config >= x86_pmu.max_events) |
| 720 | return -EINVAL; | 767 | return -EINVAL; |
| 768 | |||
| 721 | /* | 769 | /* |
| 722 | * The generic map: | 770 | * The generic map: |
| 723 | */ | 771 | */ |
| 724 | hwc->config |= x86_pmu.event_map(attr->config); | 772 | config = x86_pmu.event_map(attr->config); |
| 773 | |||
| 774 | if (config == 0) | ||
| 775 | return -ENOENT; | ||
| 776 | |||
| 777 | if (config == -1LL) | ||
| 778 | return -EINVAL; | ||
| 779 | |||
| 780 | hwc->config |= config; | ||
| 725 | 781 | ||
| 726 | return 0; | 782 | return 0; |
| 727 | } | 783 | } |
| 728 | 784 | ||
| 785 | static void p6_pmu_disable_all(void) | ||
| 786 | { | ||
| 787 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | ||
| 788 | u64 val; | ||
| 789 | |||
| 790 | if (!cpuc->enabled) | ||
| 791 | return; | ||
| 792 | |||
| 793 | cpuc->enabled = 0; | ||
| 794 | barrier(); | ||
| 795 | |||
| 796 | /* p6 only has one enable register */ | ||
| 797 | rdmsrl(MSR_P6_EVNTSEL0, val); | ||
| 798 | val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE; | ||
| 799 | wrmsrl(MSR_P6_EVNTSEL0, val); | ||
| 800 | } | ||
| 801 | |||
| 729 | static void intel_pmu_disable_all(void) | 802 | static void intel_pmu_disable_all(void) |
| 730 | { | 803 | { |
| 731 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); | 804 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); |
| @@ -767,6 +840,23 @@ void hw_perf_disable(void) | |||
| 767 | return x86_pmu.disable_all(); | 840 | return x86_pmu.disable_all(); |
| 768 | } | 841 | } |
| 769 | 842 | ||
| 843 | static void p6_pmu_enable_all(void) | ||
| 844 | { | ||
| 845 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | ||
| 846 | unsigned long val; | ||
| 847 | |||
| 848 | if (cpuc->enabled) | ||
| 849 | return; | ||
| 850 | |||
| 851 | cpuc->enabled = 1; | ||
| 852 | barrier(); | ||
| 853 | |||
| 854 | /* p6 only has one enable register */ | ||
| 855 | rdmsrl(MSR_P6_EVNTSEL0, val); | ||
| 856 | val |= ARCH_PERFMON_EVENTSEL0_ENABLE; | ||
| 857 | wrmsrl(MSR_P6_EVNTSEL0, val); | ||
| 858 | } | ||
| 859 | |||
| 770 | static void intel_pmu_enable_all(void) | 860 | static void intel_pmu_enable_all(void) |
| 771 | { | 861 | { |
| 772 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); | 862 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); |
| @@ -784,13 +874,13 @@ static void amd_pmu_enable_all(void) | |||
| 784 | barrier(); | 874 | barrier(); |
| 785 | 875 | ||
| 786 | for (idx = 0; idx < x86_pmu.num_counters; idx++) { | 876 | for (idx = 0; idx < x86_pmu.num_counters; idx++) { |
| 877 | struct perf_counter *counter = cpuc->counters[idx]; | ||
| 787 | u64 val; | 878 | u64 val; |
| 788 | 879 | ||
| 789 | if (!test_bit(idx, cpuc->active_mask)) | 880 | if (!test_bit(idx, cpuc->active_mask)) |
| 790 | continue; | 881 | continue; |
| 791 | rdmsrl(MSR_K7_EVNTSEL0 + idx, val); | 882 | |
| 792 | if (val & ARCH_PERFMON_EVENTSEL0_ENABLE) | 883 | val = counter->hw.config; |
| 793 | continue; | ||
| 794 | val |= ARCH_PERFMON_EVENTSEL0_ENABLE; | 884 | val |= ARCH_PERFMON_EVENTSEL0_ENABLE; |
| 795 | wrmsrl(MSR_K7_EVNTSEL0 + idx, val); | 885 | wrmsrl(MSR_K7_EVNTSEL0 + idx, val); |
| 796 | } | 886 | } |
| @@ -819,16 +909,13 @@ static inline void intel_pmu_ack_status(u64 ack) | |||
| 819 | 909 | ||
| 820 | static inline void x86_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) | 910 | static inline void x86_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) |
| 821 | { | 911 | { |
| 822 | int err; | 912 | (void)checking_wrmsrl(hwc->config_base + idx, |
| 823 | err = checking_wrmsrl(hwc->config_base + idx, | ||
| 824 | hwc->config | ARCH_PERFMON_EVENTSEL0_ENABLE); | 913 | hwc->config | ARCH_PERFMON_EVENTSEL0_ENABLE); |
| 825 | } | 914 | } |
| 826 | 915 | ||
| 827 | static inline void x86_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) | 916 | static inline void x86_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) |
| 828 | { | 917 | { |
| 829 | int err; | 918 | (void)checking_wrmsrl(hwc->config_base + idx, hwc->config); |
| 830 | err = checking_wrmsrl(hwc->config_base + idx, | ||
| 831 | hwc->config); | ||
| 832 | } | 919 | } |
| 833 | 920 | ||
| 834 | static inline void | 921 | static inline void |
| @@ -836,13 +923,24 @@ intel_pmu_disable_fixed(struct hw_perf_counter *hwc, int __idx) | |||
| 836 | { | 923 | { |
| 837 | int idx = __idx - X86_PMC_IDX_FIXED; | 924 | int idx = __idx - X86_PMC_IDX_FIXED; |
| 838 | u64 ctrl_val, mask; | 925 | u64 ctrl_val, mask; |
| 839 | int err; | ||
| 840 | 926 | ||
| 841 | mask = 0xfULL << (idx * 4); | 927 | mask = 0xfULL << (idx * 4); |
| 842 | 928 | ||
| 843 | rdmsrl(hwc->config_base, ctrl_val); | 929 | rdmsrl(hwc->config_base, ctrl_val); |
| 844 | ctrl_val &= ~mask; | 930 | ctrl_val &= ~mask; |
| 845 | err = checking_wrmsrl(hwc->config_base, ctrl_val); | 931 | (void)checking_wrmsrl(hwc->config_base, ctrl_val); |
| 932 | } | ||
| 933 | |||
| 934 | static inline void | ||
| 935 | p6_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) | ||
| 936 | { | ||
| 937 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | ||
| 938 | u64 val = P6_NOP_COUNTER; | ||
| 939 | |||
| 940 | if (cpuc->enabled) | ||
| 941 | val |= ARCH_PERFMON_EVENTSEL0_ENABLE; | ||
| 942 | |||
| 943 | (void)checking_wrmsrl(hwc->config_base + idx, val); | ||
| 846 | } | 944 | } |
| 847 | 945 | ||
| 848 | static inline void | 946 | static inline void |
| @@ -943,6 +1041,19 @@ intel_pmu_enable_fixed(struct hw_perf_counter *hwc, int __idx) | |||
| 943 | err = checking_wrmsrl(hwc->config_base, ctrl_val); | 1041 | err = checking_wrmsrl(hwc->config_base, ctrl_val); |
| 944 | } | 1042 | } |
| 945 | 1043 | ||
| 1044 | static void p6_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) | ||
| 1045 | { | ||
| 1046 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | ||
| 1047 | u64 val; | ||
| 1048 | |||
| 1049 | val = hwc->config; | ||
| 1050 | if (cpuc->enabled) | ||
| 1051 | val |= ARCH_PERFMON_EVENTSEL0_ENABLE; | ||
| 1052 | |||
| 1053 | (void)checking_wrmsrl(hwc->config_base + idx, val); | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | |||
| 946 | static void intel_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) | 1057 | static void intel_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) |
| 947 | { | 1058 | { |
| 948 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { | 1059 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { |
| @@ -959,8 +1070,6 @@ static void amd_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) | |||
| 959 | 1070 | ||
| 960 | if (cpuc->enabled) | 1071 | if (cpuc->enabled) |
| 961 | x86_pmu_enable_counter(hwc, idx); | 1072 | x86_pmu_enable_counter(hwc, idx); |
| 962 | else | ||
| 963 | x86_pmu_disable_counter(hwc, idx); | ||
| 964 | } | 1073 | } |
| 965 | 1074 | ||
| 966 | static int | 1075 | static int |
| @@ -1176,6 +1285,49 @@ static void intel_pmu_reset(void) | |||
| 1176 | local_irq_restore(flags); | 1285 | local_irq_restore(flags); |
| 1177 | } | 1286 | } |
| 1178 | 1287 | ||
| 1288 | static int p6_pmu_handle_irq(struct pt_regs *regs) | ||
| 1289 | { | ||
| 1290 | struct perf_sample_data data; | ||
| 1291 | struct cpu_hw_counters *cpuc; | ||
| 1292 | struct perf_counter *counter; | ||
| 1293 | struct hw_perf_counter *hwc; | ||
| 1294 | int idx, handled = 0; | ||
| 1295 | u64 val; | ||
| 1296 | |||
| 1297 | data.regs = regs; | ||
| 1298 | data.addr = 0; | ||
| 1299 | |||
| 1300 | cpuc = &__get_cpu_var(cpu_hw_counters); | ||
| 1301 | |||
| 1302 | for (idx = 0; idx < x86_pmu.num_counters; idx++) { | ||
| 1303 | if (!test_bit(idx, cpuc->active_mask)) | ||
| 1304 | continue; | ||
| 1305 | |||
| 1306 | counter = cpuc->counters[idx]; | ||
| 1307 | hwc = &counter->hw; | ||
| 1308 | |||
| 1309 | val = x86_perf_counter_update(counter, hwc, idx); | ||
| 1310 | if (val & (1ULL << (x86_pmu.counter_bits - 1))) | ||
| 1311 | continue; | ||
| 1312 | |||
| 1313 | /* | ||
| 1314 | * counter overflow | ||
| 1315 | */ | ||
| 1316 | handled = 1; | ||
| 1317 | data.period = counter->hw.last_period; | ||
| 1318 | |||
| 1319 | if (!x86_perf_counter_set_period(counter, hwc, idx)) | ||
| 1320 | continue; | ||
| 1321 | |||
| 1322 | if (perf_counter_overflow(counter, 1, &data)) | ||
| 1323 | p6_pmu_disable_counter(hwc, idx); | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | if (handled) | ||
| 1327 | inc_irq_stat(apic_perf_irqs); | ||
| 1328 | |||
| 1329 | return handled; | ||
| 1330 | } | ||
| 1179 | 1331 | ||
| 1180 | /* | 1332 | /* |
| 1181 | * This handler is triggered by the local APIC, so the APIC IRQ handling | 1333 | * This handler is triggered by the local APIC, so the APIC IRQ handling |
| @@ -1185,14 +1337,13 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) | |||
| 1185 | { | 1337 | { |
| 1186 | struct perf_sample_data data; | 1338 | struct perf_sample_data data; |
| 1187 | struct cpu_hw_counters *cpuc; | 1339 | struct cpu_hw_counters *cpuc; |
| 1188 | int bit, cpu, loops; | 1340 | int bit, loops; |
| 1189 | u64 ack, status; | 1341 | u64 ack, status; |
| 1190 | 1342 | ||
| 1191 | data.regs = regs; | 1343 | data.regs = regs; |
| 1192 | data.addr = 0; | 1344 | data.addr = 0; |
| 1193 | 1345 | ||
| 1194 | cpu = smp_processor_id(); | 1346 | cpuc = &__get_cpu_var(cpu_hw_counters); |
| 1195 | cpuc = &per_cpu(cpu_hw_counters, cpu); | ||
| 1196 | 1347 | ||
| 1197 | perf_disable(); | 1348 | perf_disable(); |
| 1198 | status = intel_pmu_get_status(); | 1349 | status = intel_pmu_get_status(); |
| @@ -1249,14 +1400,13 @@ static int amd_pmu_handle_irq(struct pt_regs *regs) | |||
| 1249 | struct cpu_hw_counters *cpuc; | 1400 | struct cpu_hw_counters *cpuc; |
| 1250 | struct perf_counter *counter; | 1401 | struct perf_counter *counter; |
| 1251 | struct hw_perf_counter *hwc; | 1402 | struct hw_perf_counter *hwc; |
| 1252 | int cpu, idx, handled = 0; | 1403 | int idx, handled = 0; |
| 1253 | u64 val; | 1404 | u64 val; |
| 1254 | 1405 | ||
| 1255 | data.regs = regs; | 1406 | data.regs = regs; |
| 1256 | data.addr = 0; | 1407 | data.addr = 0; |
| 1257 | 1408 | ||
| 1258 | cpu = smp_processor_id(); | 1409 | cpuc = &__get_cpu_var(cpu_hw_counters); |
| 1259 | cpuc = &per_cpu(cpu_hw_counters, cpu); | ||
| 1260 | 1410 | ||
| 1261 | for (idx = 0; idx < x86_pmu.num_counters; idx++) { | 1411 | for (idx = 0; idx < x86_pmu.num_counters; idx++) { |
| 1262 | if (!test_bit(idx, cpuc->active_mask)) | 1412 | if (!test_bit(idx, cpuc->active_mask)) |
| @@ -1353,6 +1503,32 @@ static __read_mostly struct notifier_block perf_counter_nmi_notifier = { | |||
| 1353 | .priority = 1 | 1503 | .priority = 1 |
| 1354 | }; | 1504 | }; |
| 1355 | 1505 | ||
| 1506 | static struct x86_pmu p6_pmu = { | ||
| 1507 | .name = "p6", | ||
| 1508 | .handle_irq = p6_pmu_handle_irq, | ||
| 1509 | .disable_all = p6_pmu_disable_all, | ||
| 1510 | .enable_all = p6_pmu_enable_all, | ||
| 1511 | .enable = p6_pmu_enable_counter, | ||
| 1512 | .disable = p6_pmu_disable_counter, | ||
| 1513 | .eventsel = MSR_P6_EVNTSEL0, | ||
| 1514 | .perfctr = MSR_P6_PERFCTR0, | ||
| 1515 | .event_map = p6_pmu_event_map, | ||
| 1516 | .raw_event = p6_pmu_raw_event, | ||
| 1517 | .max_events = ARRAY_SIZE(p6_perfmon_event_map), | ||
| 1518 | .max_period = (1ULL << 31) - 1, | ||
| 1519 | .version = 0, | ||
| 1520 | .num_counters = 2, | ||
| 1521 | /* | ||
| 1522 | * Counters have 40 bits implemented. However they are designed such | ||
| 1523 | * that bits [32-39] are sign extensions of bit 31. As such the | ||
| 1524 | * effective width of a counter for P6-like PMU is 32 bits only. | ||
| 1525 | * | ||
| 1526 | * See IA-32 Intel Architecture Software developer manual Vol 3B | ||
| 1527 | */ | ||
| 1528 | .counter_bits = 32, | ||
| 1529 | .counter_mask = (1ULL << 32) - 1, | ||
| 1530 | }; | ||
| 1531 | |||
| 1356 | static struct x86_pmu intel_pmu = { | 1532 | static struct x86_pmu intel_pmu = { |
| 1357 | .name = "Intel", | 1533 | .name = "Intel", |
| 1358 | .handle_irq = intel_pmu_handle_irq, | 1534 | .handle_irq = intel_pmu_handle_irq, |
| @@ -1392,6 +1568,37 @@ static struct x86_pmu amd_pmu = { | |||
| 1392 | .max_period = (1ULL << 47) - 1, | 1568 | .max_period = (1ULL << 47) - 1, |
| 1393 | }; | 1569 | }; |
| 1394 | 1570 | ||
| 1571 | static int p6_pmu_init(void) | ||
| 1572 | { | ||
| 1573 | switch (boot_cpu_data.x86_model) { | ||
| 1574 | case 1: | ||
| 1575 | case 3: /* Pentium Pro */ | ||
| 1576 | case 5: | ||
| 1577 | case 6: /* Pentium II */ | ||
| 1578 | case 7: | ||
| 1579 | case 8: | ||
| 1580 | case 11: /* Pentium III */ | ||
| 1581 | break; | ||
| 1582 | case 9: | ||
| 1583 | case 13: | ||
| 1584 | /* Pentium M */ | ||
| 1585 | break; | ||
| 1586 | default: | ||
| 1587 | pr_cont("unsupported p6 CPU model %d ", | ||
| 1588 | boot_cpu_data.x86_model); | ||
| 1589 | return -ENODEV; | ||
| 1590 | } | ||
| 1591 | |||
| 1592 | if (!cpu_has_apic) { | ||
| 1593 | pr_info("no Local APIC, try rebooting with lapic"); | ||
| 1594 | return -ENODEV; | ||
| 1595 | } | ||
| 1596 | |||
| 1597 | x86_pmu = p6_pmu; | ||
| 1598 | |||
| 1599 | return 0; | ||
| 1600 | } | ||
| 1601 | |||
| 1395 | static int intel_pmu_init(void) | 1602 | static int intel_pmu_init(void) |
| 1396 | { | 1603 | { |
| 1397 | union cpuid10_edx edx; | 1604 | union cpuid10_edx edx; |
| @@ -1400,8 +1607,14 @@ static int intel_pmu_init(void) | |||
| 1400 | unsigned int ebx; | 1607 | unsigned int ebx; |
| 1401 | int version; | 1608 | int version; |
| 1402 | 1609 | ||
| 1403 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) | 1610 | if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) { |
| 1611 | /* check for P6 processor family */ | ||
| 1612 | if (boot_cpu_data.x86 == 6) { | ||
| 1613 | return p6_pmu_init(); | ||
| 1614 | } else { | ||
| 1404 | return -ENODEV; | 1615 | return -ENODEV; |
| 1616 | } | ||
| 1617 | } | ||
| 1405 | 1618 | ||
| 1406 | /* | 1619 | /* |
| 1407 | * Check whether the Architectural PerfMon supports | 1620 | * Check whether the Architectural PerfMon supports |
diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h index 5e970c7d3fd5..bd15d7a5f5ce 100644 --- a/include/linux/perf_counter.h +++ b/include/linux/perf_counter.h | |||
| @@ -120,8 +120,9 @@ enum perf_counter_sample_format { | |||
| 120 | PERF_SAMPLE_ID = 1U << 6, | 120 | PERF_SAMPLE_ID = 1U << 6, |
| 121 | PERF_SAMPLE_CPU = 1U << 7, | 121 | PERF_SAMPLE_CPU = 1U << 7, |
| 122 | PERF_SAMPLE_PERIOD = 1U << 8, | 122 | PERF_SAMPLE_PERIOD = 1U << 8, |
| 123 | PERF_SAMPLE_STREAM_ID = 1U << 9, | ||
| 123 | 124 | ||
| 124 | PERF_SAMPLE_MAX = 1U << 9, /* non-ABI */ | 125 | PERF_SAMPLE_MAX = 1U << 10, /* non-ABI */ |
| 125 | }; | 126 | }; |
| 126 | 127 | ||
| 127 | /* | 128 | /* |
| @@ -312,16 +313,7 @@ enum perf_event_type { | |||
| 312 | * struct perf_event_header header; | 313 | * struct perf_event_header header; |
| 313 | * u64 time; | 314 | * u64 time; |
| 314 | * u64 id; | 315 | * u64 id; |
| 315 | * u64 sample_period; | 316 | * u64 stream_id; |
| 316 | * }; | ||
| 317 | */ | ||
| 318 | PERF_EVENT_PERIOD = 4, | ||
| 319 | |||
| 320 | /* | ||
| 321 | * struct { | ||
| 322 | * struct perf_event_header header; | ||
| 323 | * u64 time; | ||
| 324 | * u64 id; | ||
| 325 | * }; | 317 | * }; |
| 326 | */ | 318 | */ |
| 327 | PERF_EVENT_THROTTLE = 5, | 319 | PERF_EVENT_THROTTLE = 5, |
| @@ -356,6 +348,7 @@ enum perf_event_type { | |||
| 356 | * { u64 time; } && PERF_SAMPLE_TIME | 348 | * { u64 time; } && PERF_SAMPLE_TIME |
| 357 | * { u64 addr; } && PERF_SAMPLE_ADDR | 349 | * { u64 addr; } && PERF_SAMPLE_ADDR |
| 358 | * { u64 id; } && PERF_SAMPLE_ID | 350 | * { u64 id; } && PERF_SAMPLE_ID |
| 351 | * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID | ||
| 359 | * { u32 cpu, res; } && PERF_SAMPLE_CPU | 352 | * { u32 cpu, res; } && PERF_SAMPLE_CPU |
| 360 | * { u64 period; } && PERF_SAMPLE_PERIOD | 353 | * { u64 period; } && PERF_SAMPLE_PERIOD |
| 361 | * | 354 | * |
diff --git a/init/Kconfig b/init/Kconfig index 1ce05a4cb5f6..cb2c09270226 100644 --- a/init/Kconfig +++ b/init/Kconfig | |||
| @@ -962,7 +962,7 @@ config PERF_COUNTERS | |||
| 962 | 962 | ||
| 963 | config EVENT_PROFILE | 963 | config EVENT_PROFILE |
| 964 | bool "Tracepoint profile sources" | 964 | bool "Tracepoint profile sources" |
| 965 | depends on PERF_COUNTERS && EVENT_TRACER | 965 | depends on PERF_COUNTERS && EVENT_TRACING |
| 966 | default y | 966 | default y |
| 967 | 967 | ||
| 968 | endmenu | 968 | endmenu |
diff --git a/kernel/fork.c b/kernel/fork.c index bd2959228871..9b42695f0d14 100644 --- a/kernel/fork.c +++ b/kernel/fork.c | |||
| @@ -1407,14 +1407,11 @@ long do_fork(unsigned long clone_flags, | |||
| 1407 | if (clone_flags & CLONE_VFORK) { | 1407 | if (clone_flags & CLONE_VFORK) { |
| 1408 | p->vfork_done = &vfork; | 1408 | p->vfork_done = &vfork; |
| 1409 | init_completion(&vfork); | 1409 | init_completion(&vfork); |
| 1410 | } else if (!(clone_flags & CLONE_VM)) { | ||
| 1411 | /* | ||
| 1412 | * vfork will do an exec which will call | ||
| 1413 | * set_task_comm() | ||
| 1414 | */ | ||
| 1415 | perf_counter_fork(p); | ||
| 1416 | } | 1410 | } |
| 1417 | 1411 | ||
| 1412 | if (!(clone_flags & CLONE_THREAD)) | ||
| 1413 | perf_counter_fork(p); | ||
| 1414 | |||
| 1418 | audit_finish_fork(p); | 1415 | audit_finish_fork(p); |
| 1419 | tracehook_report_clone(regs, clone_flags, nr, p); | 1416 | tracehook_report_clone(regs, clone_flags, nr, p); |
| 1420 | 1417 | ||
diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c index 7bc888dfd06a..950931041954 100644 --- a/kernel/perf_counter.c +++ b/kernel/perf_counter.c | |||
| @@ -146,6 +146,28 @@ static void put_ctx(struct perf_counter_context *ctx) | |||
| 146 | } | 146 | } |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | static void unclone_ctx(struct perf_counter_context *ctx) | ||
| 150 | { | ||
| 151 | if (ctx->parent_ctx) { | ||
| 152 | put_ctx(ctx->parent_ctx); | ||
| 153 | ctx->parent_ctx = NULL; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | /* | ||
| 158 | * If we inherit counters we want to return the parent counter id | ||
| 159 | * to userspace. | ||
| 160 | */ | ||
| 161 | static u64 primary_counter_id(struct perf_counter *counter) | ||
| 162 | { | ||
| 163 | u64 id = counter->id; | ||
| 164 | |||
| 165 | if (counter->parent) | ||
| 166 | id = counter->parent->id; | ||
| 167 | |||
| 168 | return id; | ||
| 169 | } | ||
| 170 | |||
| 149 | /* | 171 | /* |
| 150 | * Get the perf_counter_context for a task and lock it. | 172 | * Get the perf_counter_context for a task and lock it. |
| 151 | * This has to cope with with the fact that until it is locked, | 173 | * This has to cope with with the fact that until it is locked, |
| @@ -1288,7 +1310,6 @@ static void perf_counter_cpu_sched_in(struct perf_cpu_context *cpuctx, int cpu) | |||
| 1288 | #define MAX_INTERRUPTS (~0ULL) | 1310 | #define MAX_INTERRUPTS (~0ULL) |
| 1289 | 1311 | ||
| 1290 | static void perf_log_throttle(struct perf_counter *counter, int enable); | 1312 | static void perf_log_throttle(struct perf_counter *counter, int enable); |
| 1291 | static void perf_log_period(struct perf_counter *counter, u64 period); | ||
| 1292 | 1313 | ||
| 1293 | static void perf_adjust_period(struct perf_counter *counter, u64 events) | 1314 | static void perf_adjust_period(struct perf_counter *counter, u64 events) |
| 1294 | { | 1315 | { |
| @@ -1307,8 +1328,6 @@ static void perf_adjust_period(struct perf_counter *counter, u64 events) | |||
| 1307 | if (!sample_period) | 1328 | if (!sample_period) |
| 1308 | sample_period = 1; | 1329 | sample_period = 1; |
| 1309 | 1330 | ||
| 1310 | perf_log_period(counter, sample_period); | ||
| 1311 | |||
| 1312 | hwc->sample_period = sample_period; | 1331 | hwc->sample_period = sample_period; |
| 1313 | } | 1332 | } |
| 1314 | 1333 | ||
| @@ -1463,10 +1482,8 @@ static void perf_counter_enable_on_exec(struct task_struct *task) | |||
| 1463 | /* | 1482 | /* |
| 1464 | * Unclone this context if we enabled any counter. | 1483 | * Unclone this context if we enabled any counter. |
| 1465 | */ | 1484 | */ |
| 1466 | if (enabled && ctx->parent_ctx) { | 1485 | if (enabled) |
| 1467 | put_ctx(ctx->parent_ctx); | 1486 | unclone_ctx(ctx); |
| 1468 | ctx->parent_ctx = NULL; | ||
| 1469 | } | ||
| 1470 | 1487 | ||
| 1471 | spin_unlock(&ctx->lock); | 1488 | spin_unlock(&ctx->lock); |
| 1472 | 1489 | ||
| @@ -1526,7 +1543,6 @@ __perf_counter_init_context(struct perf_counter_context *ctx, | |||
| 1526 | 1543 | ||
| 1527 | static struct perf_counter_context *find_get_context(pid_t pid, int cpu) | 1544 | static struct perf_counter_context *find_get_context(pid_t pid, int cpu) |
| 1528 | { | 1545 | { |
| 1529 | struct perf_counter_context *parent_ctx; | ||
| 1530 | struct perf_counter_context *ctx; | 1546 | struct perf_counter_context *ctx; |
| 1531 | struct perf_cpu_context *cpuctx; | 1547 | struct perf_cpu_context *cpuctx; |
| 1532 | struct task_struct *task; | 1548 | struct task_struct *task; |
| @@ -1586,11 +1602,7 @@ static struct perf_counter_context *find_get_context(pid_t pid, int cpu) | |||
| 1586 | retry: | 1602 | retry: |
| 1587 | ctx = perf_lock_task_context(task, &flags); | 1603 | ctx = perf_lock_task_context(task, &flags); |
| 1588 | if (ctx) { | 1604 | if (ctx) { |
| 1589 | parent_ctx = ctx->parent_ctx; | 1605 | unclone_ctx(ctx); |
| 1590 | if (parent_ctx) { | ||
| 1591 | put_ctx(parent_ctx); | ||
| 1592 | ctx->parent_ctx = NULL; /* no longer a clone */ | ||
| 1593 | } | ||
| 1594 | spin_unlock_irqrestore(&ctx->lock, flags); | 1606 | spin_unlock_irqrestore(&ctx->lock, flags); |
| 1595 | } | 1607 | } |
| 1596 | 1608 | ||
| @@ -1704,7 +1716,7 @@ perf_read_hw(struct perf_counter *counter, char __user *buf, size_t count) | |||
| 1704 | values[n++] = counter->total_time_running + | 1716 | values[n++] = counter->total_time_running + |
| 1705 | atomic64_read(&counter->child_total_time_running); | 1717 | atomic64_read(&counter->child_total_time_running); |
| 1706 | if (counter->attr.read_format & PERF_FORMAT_ID) | 1718 | if (counter->attr.read_format & PERF_FORMAT_ID) |
| 1707 | values[n++] = counter->id; | 1719 | values[n++] = primary_counter_id(counter); |
| 1708 | mutex_unlock(&counter->child_mutex); | 1720 | mutex_unlock(&counter->child_mutex); |
| 1709 | 1721 | ||
| 1710 | if (count < n * sizeof(u64)) | 1722 | if (count < n * sizeof(u64)) |
| @@ -1811,8 +1823,6 @@ static int perf_counter_period(struct perf_counter *counter, u64 __user *arg) | |||
| 1811 | 1823 | ||
| 1812 | counter->attr.sample_freq = value; | 1824 | counter->attr.sample_freq = value; |
| 1813 | } else { | 1825 | } else { |
| 1814 | perf_log_period(counter, value); | ||
| 1815 | |||
| 1816 | counter->attr.sample_period = value; | 1826 | counter->attr.sample_period = value; |
| 1817 | counter->hw.sample_period = value; | 1827 | counter->hw.sample_period = value; |
| 1818 | } | 1828 | } |
| @@ -2661,6 +2671,9 @@ static void perf_counter_output(struct perf_counter *counter, int nmi, | |||
| 2661 | if (sample_type & PERF_SAMPLE_ID) | 2671 | if (sample_type & PERF_SAMPLE_ID) |
| 2662 | header.size += sizeof(u64); | 2672 | header.size += sizeof(u64); |
| 2663 | 2673 | ||
| 2674 | if (sample_type & PERF_SAMPLE_STREAM_ID) | ||
| 2675 | header.size += sizeof(u64); | ||
| 2676 | |||
| 2664 | if (sample_type & PERF_SAMPLE_CPU) { | 2677 | if (sample_type & PERF_SAMPLE_CPU) { |
| 2665 | header.size += sizeof(cpu_entry); | 2678 | header.size += sizeof(cpu_entry); |
| 2666 | 2679 | ||
| @@ -2704,7 +2717,13 @@ static void perf_counter_output(struct perf_counter *counter, int nmi, | |||
| 2704 | if (sample_type & PERF_SAMPLE_ADDR) | 2717 | if (sample_type & PERF_SAMPLE_ADDR) |
| 2705 | perf_output_put(&handle, data->addr); | 2718 | perf_output_put(&handle, data->addr); |
| 2706 | 2719 | ||
| 2707 | if (sample_type & PERF_SAMPLE_ID) | 2720 | if (sample_type & PERF_SAMPLE_ID) { |
| 2721 | u64 id = primary_counter_id(counter); | ||
| 2722 | |||
| 2723 | perf_output_put(&handle, id); | ||
| 2724 | } | ||
| 2725 | |||
| 2726 | if (sample_type & PERF_SAMPLE_STREAM_ID) | ||
| 2708 | perf_output_put(&handle, counter->id); | 2727 | perf_output_put(&handle, counter->id); |
| 2709 | 2728 | ||
| 2710 | if (sample_type & PERF_SAMPLE_CPU) | 2729 | if (sample_type & PERF_SAMPLE_CPU) |
| @@ -2727,7 +2746,7 @@ static void perf_counter_output(struct perf_counter *counter, int nmi, | |||
| 2727 | if (sub != counter) | 2746 | if (sub != counter) |
| 2728 | sub->pmu->read(sub); | 2747 | sub->pmu->read(sub); |
| 2729 | 2748 | ||
| 2730 | group_entry.id = sub->id; | 2749 | group_entry.id = primary_counter_id(sub); |
| 2731 | group_entry.counter = atomic64_read(&sub->count); | 2750 | group_entry.counter = atomic64_read(&sub->count); |
| 2732 | 2751 | ||
| 2733 | perf_output_put(&handle, group_entry); | 2752 | perf_output_put(&handle, group_entry); |
| @@ -2787,15 +2806,8 @@ perf_counter_read_event(struct perf_counter *counter, | |||
| 2787 | } | 2806 | } |
| 2788 | 2807 | ||
| 2789 | if (counter->attr.read_format & PERF_FORMAT_ID) { | 2808 | if (counter->attr.read_format & PERF_FORMAT_ID) { |
| 2790 | u64 id; | ||
| 2791 | |||
| 2792 | event.header.size += sizeof(u64); | 2809 | event.header.size += sizeof(u64); |
| 2793 | if (counter->parent) | 2810 | event.format[i++] = primary_counter_id(counter); |
| 2794 | id = counter->parent->id; | ||
| 2795 | else | ||
| 2796 | id = counter->id; | ||
| 2797 | |||
| 2798 | event.format[i++] = id; | ||
| 2799 | } | 2811 | } |
| 2800 | 2812 | ||
| 2801 | ret = perf_output_begin(&handle, counter, event.header.size, 0, 0); | 2813 | ret = perf_output_begin(&handle, counter, event.header.size, 0, 0); |
| @@ -2896,8 +2908,11 @@ void perf_counter_fork(struct task_struct *task) | |||
| 2896 | .event = { | 2908 | .event = { |
| 2897 | .header = { | 2909 | .header = { |
| 2898 | .type = PERF_EVENT_FORK, | 2910 | .type = PERF_EVENT_FORK, |
| 2911 | .misc = 0, | ||
| 2899 | .size = sizeof(fork_event.event), | 2912 | .size = sizeof(fork_event.event), |
| 2900 | }, | 2913 | }, |
| 2914 | /* .pid */ | ||
| 2915 | /* .ppid */ | ||
| 2901 | }, | 2916 | }, |
| 2902 | }; | 2917 | }; |
| 2903 | 2918 | ||
| @@ -2969,8 +2984,10 @@ static void perf_counter_comm_event(struct perf_comm_event *comm_event) | |||
| 2969 | struct perf_cpu_context *cpuctx; | 2984 | struct perf_cpu_context *cpuctx; |
| 2970 | struct perf_counter_context *ctx; | 2985 | struct perf_counter_context *ctx; |
| 2971 | unsigned int size; | 2986 | unsigned int size; |
| 2972 | char *comm = comm_event->task->comm; | 2987 | char comm[TASK_COMM_LEN]; |
| 2973 | 2988 | ||
| 2989 | memset(comm, 0, sizeof(comm)); | ||
| 2990 | strncpy(comm, comm_event->task->comm, sizeof(comm)); | ||
| 2974 | size = ALIGN(strlen(comm)+1, sizeof(u64)); | 2991 | size = ALIGN(strlen(comm)+1, sizeof(u64)); |
| 2975 | 2992 | ||
| 2976 | comm_event->comm = comm; | 2993 | comm_event->comm = comm; |
| @@ -3005,8 +3022,16 @@ void perf_counter_comm(struct task_struct *task) | |||
| 3005 | 3022 | ||
| 3006 | comm_event = (struct perf_comm_event){ | 3023 | comm_event = (struct perf_comm_event){ |
| 3007 | .task = task, | 3024 | .task = task, |
| 3025 | /* .comm */ | ||
| 3026 | /* .comm_size */ | ||
| 3008 | .event = { | 3027 | .event = { |
| 3009 | .header = { .type = PERF_EVENT_COMM, }, | 3028 | .header = { |
| 3029 | .type = PERF_EVENT_COMM, | ||
| 3030 | .misc = 0, | ||
| 3031 | /* .size */ | ||
| 3032 | }, | ||
| 3033 | /* .pid */ | ||
| 3034 | /* .tid */ | ||
| 3010 | }, | 3035 | }, |
| 3011 | }; | 3036 | }; |
| 3012 | 3037 | ||
| @@ -3089,8 +3114,15 @@ static void perf_counter_mmap_event(struct perf_mmap_event *mmap_event) | |||
| 3089 | char *buf = NULL; | 3114 | char *buf = NULL; |
| 3090 | const char *name; | 3115 | const char *name; |
| 3091 | 3116 | ||
| 3117 | memset(tmp, 0, sizeof(tmp)); | ||
| 3118 | |||
| 3092 | if (file) { | 3119 | if (file) { |
| 3093 | buf = kzalloc(PATH_MAX, GFP_KERNEL); | 3120 | /* |
| 3121 | * d_path works from the end of the buffer backwards, so we | ||
| 3122 | * need to add enough zero bytes after the string to handle | ||
| 3123 | * the 64bit alignment we do later. | ||
| 3124 | */ | ||
| 3125 | buf = kzalloc(PATH_MAX + sizeof(u64), GFP_KERNEL); | ||
| 3094 | if (!buf) { | 3126 | if (!buf) { |
| 3095 | name = strncpy(tmp, "//enomem", sizeof(tmp)); | 3127 | name = strncpy(tmp, "//enomem", sizeof(tmp)); |
| 3096 | goto got_name; | 3128 | goto got_name; |
| @@ -3101,9 +3133,11 @@ static void perf_counter_mmap_event(struct perf_mmap_event *mmap_event) | |||
| 3101 | goto got_name; | 3133 | goto got_name; |
| 3102 | } | 3134 | } |
| 3103 | } else { | 3135 | } else { |
| 3104 | name = arch_vma_name(mmap_event->vma); | 3136 | if (arch_vma_name(mmap_event->vma)) { |
| 3105 | if (name) | 3137 | name = strncpy(tmp, arch_vma_name(mmap_event->vma), |
| 3138 | sizeof(tmp)); | ||
| 3106 | goto got_name; | 3139 | goto got_name; |
| 3140 | } | ||
| 3107 | 3141 | ||
| 3108 | if (!vma->vm_mm) { | 3142 | if (!vma->vm_mm) { |
| 3109 | name = strncpy(tmp, "[vdso]", sizeof(tmp)); | 3143 | name = strncpy(tmp, "[vdso]", sizeof(tmp)); |
| @@ -3148,8 +3182,16 @@ void __perf_counter_mmap(struct vm_area_struct *vma) | |||
| 3148 | 3182 | ||
| 3149 | mmap_event = (struct perf_mmap_event){ | 3183 | mmap_event = (struct perf_mmap_event){ |
| 3150 | .vma = vma, | 3184 | .vma = vma, |
| 3185 | /* .file_name */ | ||
| 3186 | /* .file_size */ | ||
| 3151 | .event = { | 3187 | .event = { |
| 3152 | .header = { .type = PERF_EVENT_MMAP, }, | 3188 | .header = { |
| 3189 | .type = PERF_EVENT_MMAP, | ||
| 3190 | .misc = 0, | ||
| 3191 | /* .size */ | ||
| 3192 | }, | ||
| 3193 | /* .pid */ | ||
| 3194 | /* .tid */ | ||
| 3153 | .start = vma->vm_start, | 3195 | .start = vma->vm_start, |
| 3154 | .len = vma->vm_end - vma->vm_start, | 3196 | .len = vma->vm_end - vma->vm_start, |
| 3155 | .pgoff = vma->vm_pgoff, | 3197 | .pgoff = vma->vm_pgoff, |
| @@ -3160,49 +3202,6 @@ void __perf_counter_mmap(struct vm_area_struct *vma) | |||
| 3160 | } | 3202 | } |
| 3161 | 3203 | ||
| 3162 | /* | 3204 | /* |
| 3163 | * Log sample_period changes so that analyzing tools can re-normalize the | ||
| 3164 | * event flow. | ||
| 3165 | */ | ||
| 3166 | |||
| 3167 | struct freq_event { | ||
| 3168 | struct perf_event_header header; | ||
| 3169 | u64 time; | ||
| 3170 | u64 id; | ||
| 3171 | u64 period; | ||
| 3172 | }; | ||
| 3173 | |||
| 3174 | static void perf_log_period(struct perf_counter *counter, u64 period) | ||
| 3175 | { | ||
| 3176 | struct perf_output_handle handle; | ||
| 3177 | struct freq_event event; | ||
| 3178 | int ret; | ||
| 3179 | |||
| 3180 | if (counter->hw.sample_period == period) | ||
| 3181 | return; | ||
| 3182 | |||
| 3183 | if (counter->attr.sample_type & PERF_SAMPLE_PERIOD) | ||
| 3184 | return; | ||
| 3185 | |||
| 3186 | event = (struct freq_event) { | ||
| 3187 | .header = { | ||
| 3188 | .type = PERF_EVENT_PERIOD, | ||
| 3189 | .misc = 0, | ||
| 3190 | .size = sizeof(event), | ||
| 3191 | }, | ||
| 3192 | .time = sched_clock(), | ||
| 3193 | .id = counter->id, | ||
| 3194 | .period = period, | ||
| 3195 | }; | ||
| 3196 | |||
| 3197 | ret = perf_output_begin(&handle, counter, sizeof(event), 1, 0); | ||
| 3198 | if (ret) | ||
| 3199 | return; | ||
| 3200 | |||
| 3201 | perf_output_put(&handle, event); | ||
| 3202 | perf_output_end(&handle); | ||
| 3203 | } | ||
| 3204 | |||
| 3205 | /* | ||
| 3206 | * IRQ throttle logging | 3205 | * IRQ throttle logging |
| 3207 | */ | 3206 | */ |
| 3208 | 3207 | ||
| @@ -3215,16 +3214,21 @@ static void perf_log_throttle(struct perf_counter *counter, int enable) | |||
| 3215 | struct perf_event_header header; | 3214 | struct perf_event_header header; |
| 3216 | u64 time; | 3215 | u64 time; |
| 3217 | u64 id; | 3216 | u64 id; |
| 3217 | u64 stream_id; | ||
| 3218 | } throttle_event = { | 3218 | } throttle_event = { |
| 3219 | .header = { | 3219 | .header = { |
| 3220 | .type = PERF_EVENT_THROTTLE + 1, | 3220 | .type = PERF_EVENT_THROTTLE, |
| 3221 | .misc = 0, | 3221 | .misc = 0, |
| 3222 | .size = sizeof(throttle_event), | 3222 | .size = sizeof(throttle_event), |
| 3223 | }, | 3223 | }, |
| 3224 | .time = sched_clock(), | 3224 | .time = sched_clock(), |
| 3225 | .id = counter->id, | 3225 | .id = primary_counter_id(counter), |
| 3226 | .stream_id = counter->id, | ||
| 3226 | }; | 3227 | }; |
| 3227 | 3228 | ||
| 3229 | if (enable) | ||
| 3230 | throttle_event.header.type = PERF_EVENT_UNTHROTTLE; | ||
| 3231 | |||
| 3228 | ret = perf_output_begin(&handle, counter, sizeof(throttle_event), 1, 0); | 3232 | ret = perf_output_begin(&handle, counter, sizeof(throttle_event), 1, 0); |
| 3229 | if (ret) | 3233 | if (ret) |
| 3230 | return; | 3234 | return; |
| @@ -3672,7 +3676,7 @@ static const struct pmu perf_ops_task_clock = { | |||
| 3672 | void perf_tpcounter_event(int event_id) | 3676 | void perf_tpcounter_event(int event_id) |
| 3673 | { | 3677 | { |
| 3674 | struct perf_sample_data data = { | 3678 | struct perf_sample_data data = { |
| 3675 | .regs = get_irq_regs(); | 3679 | .regs = get_irq_regs(), |
| 3676 | .addr = 0, | 3680 | .addr = 0, |
| 3677 | }; | 3681 | }; |
| 3678 | 3682 | ||
| @@ -3688,16 +3692,12 @@ extern void ftrace_profile_disable(int); | |||
| 3688 | 3692 | ||
| 3689 | static void tp_perf_counter_destroy(struct perf_counter *counter) | 3693 | static void tp_perf_counter_destroy(struct perf_counter *counter) |
| 3690 | { | 3694 | { |
| 3691 | ftrace_profile_disable(perf_event_id(&counter->attr)); | 3695 | ftrace_profile_disable(counter->attr.config); |
| 3692 | } | 3696 | } |
| 3693 | 3697 | ||
| 3694 | static const struct pmu *tp_perf_counter_init(struct perf_counter *counter) | 3698 | static const struct pmu *tp_perf_counter_init(struct perf_counter *counter) |
| 3695 | { | 3699 | { |
| 3696 | int event_id = perf_event_id(&counter->attr); | 3700 | if (ftrace_profile_enable(counter->attr.config)) |
| 3697 | int ret; | ||
| 3698 | |||
| 3699 | ret = ftrace_profile_enable(event_id); | ||
| 3700 | if (ret) | ||
| 3701 | return NULL; | 3701 | return NULL; |
| 3702 | 3702 | ||
| 3703 | counter->destroy = tp_perf_counter_destroy; | 3703 | counter->destroy = tp_perf_counter_destroy; |
| @@ -4256,15 +4256,12 @@ void perf_counter_exit_task(struct task_struct *child) | |||
| 4256 | */ | 4256 | */ |
| 4257 | spin_lock(&child_ctx->lock); | 4257 | spin_lock(&child_ctx->lock); |
| 4258 | child->perf_counter_ctxp = NULL; | 4258 | child->perf_counter_ctxp = NULL; |
| 4259 | if (child_ctx->parent_ctx) { | 4259 | /* |
| 4260 | /* | 4260 | * If this context is a clone; unclone it so it can't get |
| 4261 | * This context is a clone; unclone it so it can't get | 4261 | * swapped to another process while we're removing all |
| 4262 | * swapped to another process while we're removing all | 4262 | * the counters from it. |
| 4263 | * the counters from it. | 4263 | */ |
| 4264 | */ | 4264 | unclone_ctx(child_ctx); |
| 4265 | put_ctx(child_ctx->parent_ctx); | ||
| 4266 | child_ctx->parent_ctx = NULL; | ||
| 4267 | } | ||
| 4268 | spin_unlock(&child_ctx->lock); | 4265 | spin_unlock(&child_ctx->lock); |
| 4269 | local_irq_restore(flags); | 4266 | local_irq_restore(flags); |
| 4270 | 4267 | ||
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 8aa3f8c88707..e72e93110782 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
| @@ -24,6 +24,9 @@ OPTIONS | |||
| 24 | --dsos=:: | 24 | --dsos=:: |
| 25 | Only consider symbols in these dsos. CSV that understands | 25 | Only consider symbols in these dsos. CSV that understands |
| 26 | file://filename entries. | 26 | file://filename entries. |
| 27 | -n | ||
| 28 | --show-nr-samples | ||
| 29 | Show the number of samples for each symbol | ||
| 27 | -C:: | 30 | -C:: |
| 28 | --comms=:: | 31 | --comms=:: |
| 29 | Only consider symbols in these comms. CSV that understands | 32 | Only consider symbols in these comms. CSV that understands |
| @@ -33,6 +36,18 @@ OPTIONS | |||
| 33 | Only consider these symbols. CSV that understands | 36 | Only consider these symbols. CSV that understands |
| 34 | file://filename entries. | 37 | file://filename entries. |
| 35 | 38 | ||
| 39 | -w:: | ||
| 40 | --field-width=:: | ||
| 41 | Force each column width to the provided list, for large terminal | ||
| 42 | readability. | ||
| 43 | |||
| 44 | -t:: | ||
| 45 | --field-separator=:: | ||
| 46 | |||
| 47 | Use a special separator character and don't pad with spaces, replacing | ||
| 48 | all occurances of this separator in symbol names (and other output) | ||
| 49 | with a '.' character, that thus it's the only non valid separator. | ||
| 50 | |||
| 36 | SEE ALSO | 51 | SEE ALSO |
| 37 | -------- | 52 | -------- |
| 38 | linkperf:perf-stat[1] | 53 | linkperf:perf-stat[1] |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 7822b3d6baca..a5e9b876ca09 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
| @@ -345,7 +345,7 @@ BUILTIN_OBJS += builtin-stat.o | |||
| 345 | BUILTIN_OBJS += builtin-top.o | 345 | BUILTIN_OBJS += builtin-top.o |
| 346 | 346 | ||
| 347 | PERFLIBS = $(LIB_FILE) | 347 | PERFLIBS = $(LIB_FILE) |
| 348 | EXTLIBS = | 348 | EXTLIBS = -lbfd |
| 349 | 349 | ||
| 350 | # | 350 | # |
| 351 | # Platform specific tweaks | 351 | # Platform specific tweaks |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 5f9eefecc574..1dba568e1941 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -74,20 +74,12 @@ struct fork_event { | |||
| 74 | u32 pid, ppid; | 74 | u32 pid, ppid; |
| 75 | }; | 75 | }; |
| 76 | 76 | ||
| 77 | struct period_event { | ||
| 78 | struct perf_event_header header; | ||
| 79 | u64 time; | ||
| 80 | u64 id; | ||
| 81 | u64 sample_period; | ||
| 82 | }; | ||
| 83 | |||
| 84 | typedef union event_union { | 77 | typedef union event_union { |
| 85 | struct perf_event_header header; | 78 | struct perf_event_header header; |
| 86 | struct ip_event ip; | 79 | struct ip_event ip; |
| 87 | struct mmap_event mmap; | 80 | struct mmap_event mmap; |
| 88 | struct comm_event comm; | 81 | struct comm_event comm; |
| 89 | struct fork_event fork; | 82 | struct fork_event fork; |
| 90 | struct period_event period; | ||
| 91 | } event_t; | 83 | } event_t; |
| 92 | 84 | ||
| 93 | 85 | ||
| @@ -998,19 +990,6 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 998 | } | 990 | } |
| 999 | 991 | ||
| 1000 | static int | 992 | static int |
| 1001 | process_period_event(event_t *event, unsigned long offset, unsigned long head) | ||
| 1002 | { | ||
| 1003 | dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n", | ||
| 1004 | (void *)(offset + head), | ||
| 1005 | (void *)(long)(event->header.size), | ||
| 1006 | event->period.time, | ||
| 1007 | event->period.id, | ||
| 1008 | event->period.sample_period); | ||
| 1009 | |||
| 1010 | return 0; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | static int | ||
| 1014 | process_event(event_t *event, unsigned long offset, unsigned long head) | 993 | process_event(event_t *event, unsigned long offset, unsigned long head) |
| 1015 | { | 994 | { |
| 1016 | switch (event->header.type) { | 995 | switch (event->header.type) { |
| @@ -1025,9 +1004,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1025 | 1004 | ||
| 1026 | case PERF_EVENT_FORK: | 1005 | case PERF_EVENT_FORK: |
| 1027 | return process_fork_event(event, offset, head); | 1006 | return process_fork_event(event, offset, head); |
| 1028 | |||
| 1029 | case PERF_EVENT_PERIOD: | ||
| 1030 | return process_period_event(event, offset, head); | ||
| 1031 | /* | 1007 | /* |
| 1032 | * We dont process them right now but they are fine: | 1008 | * We dont process them right now but they are fine: |
| 1033 | */ | 1009 | */ |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 4ef78a5e6f32..6da09928130f 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -43,6 +43,7 @@ static int call_graph = 0; | |||
| 43 | static int verbose = 0; | 43 | static int verbose = 0; |
| 44 | static int inherit_stat = 0; | 44 | static int inherit_stat = 0; |
| 45 | static int no_samples = 0; | 45 | static int no_samples = 0; |
| 46 | static int sample_address = 0; | ||
| 46 | 47 | ||
| 47 | static long samples; | 48 | static long samples; |
| 48 | static struct timeval last_read; | 49 | static struct timeval last_read; |
| @@ -313,6 +314,10 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
| 313 | if (*pbf == 'x') { /* vm_exec */ | 314 | if (*pbf == 'x') { /* vm_exec */ |
| 314 | char *execname = strchr(bf, '/'); | 315 | char *execname = strchr(bf, '/'); |
| 315 | 316 | ||
| 317 | /* Catch VDSO */ | ||
| 318 | if (execname == NULL) | ||
| 319 | execname = strstr(bf, "[vdso]"); | ||
| 320 | |||
| 316 | if (execname == NULL) | 321 | if (execname == NULL) |
| 317 | continue; | 322 | continue; |
| 318 | 323 | ||
| @@ -401,6 +406,9 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
| 401 | if (inherit_stat) | 406 | if (inherit_stat) |
| 402 | attr->inherit_stat = 1; | 407 | attr->inherit_stat = 1; |
| 403 | 408 | ||
| 409 | if (sample_address) | ||
| 410 | attr->sample_type |= PERF_SAMPLE_ADDR; | ||
| 411 | |||
| 404 | if (call_graph) | 412 | if (call_graph) |
| 405 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 413 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; |
| 406 | 414 | ||
| @@ -645,6 +653,8 @@ static const struct option options[] = { | |||
| 645 | "be more verbose (show counter open errors, etc)"), | 653 | "be more verbose (show counter open errors, etc)"), |
| 646 | OPT_BOOLEAN('s', "stat", &inherit_stat, | 654 | OPT_BOOLEAN('s', "stat", &inherit_stat, |
| 647 | "per thread counts"), | 655 | "per thread counts"), |
| 656 | OPT_BOOLEAN('d', "data", &sample_address, | ||
| 657 | "Sample addresses"), | ||
| 648 | OPT_BOOLEAN('n', "no-samples", &no_samples, | 658 | OPT_BOOLEAN('n', "no-samples", &no_samples, |
| 649 | "don't sample"), | 659 | "don't sample"), |
| 650 | OPT_END() | 660 | OPT_END() |
| @@ -654,7 +664,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
| 654 | { | 664 | { |
| 655 | int counter; | 665 | int counter; |
| 656 | 666 | ||
| 657 | argc = parse_options(argc, argv, options, record_usage, 0); | 667 | argc = parse_options(argc, argv, options, record_usage, |
| 668 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
| 658 | if (!argc && target_pid == -1 && !system_wide) | 669 | if (!argc && target_pid == -1 && !system_wide) |
| 659 | usage_with_options(record_usage, options); | 670 | usage_with_options(record_usage, options); |
| 660 | 671 | ||
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 4e5cc266311e..b20a4b6e31b7 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -33,8 +33,10 @@ static char *vmlinux = NULL; | |||
| 33 | 33 | ||
| 34 | static char default_sort_order[] = "comm,dso"; | 34 | static char default_sort_order[] = "comm,dso"; |
| 35 | static char *sort_order = default_sort_order; | 35 | static char *sort_order = default_sort_order; |
| 36 | static char *dso_list_str, *comm_list_str, *sym_list_str; | 36 | static char *dso_list_str, *comm_list_str, *sym_list_str, |
| 37 | *col_width_list_str; | ||
| 37 | static struct strlist *dso_list, *comm_list, *sym_list; | 38 | static struct strlist *dso_list, *comm_list, *sym_list; |
| 39 | static char *field_sep; | ||
| 38 | 40 | ||
| 39 | static int input; | 41 | static int input; |
| 40 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | 42 | static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; |
| @@ -49,6 +51,7 @@ static int verbose; | |||
| 49 | static int modules; | 51 | static int modules; |
| 50 | 52 | ||
| 51 | static int full_paths; | 53 | static int full_paths; |
| 54 | static int show_nr_samples; | ||
| 52 | 55 | ||
| 53 | static unsigned long page_size; | 56 | static unsigned long page_size; |
| 54 | static unsigned long mmap_window = 32; | 57 | static unsigned long mmap_window = 32; |
| @@ -98,13 +101,6 @@ struct fork_event { | |||
| 98 | u32 pid, ppid; | 101 | u32 pid, ppid; |
| 99 | }; | 102 | }; |
| 100 | 103 | ||
| 101 | struct period_event { | ||
| 102 | struct perf_event_header header; | ||
| 103 | u64 time; | ||
| 104 | u64 id; | ||
| 105 | u64 sample_period; | ||
| 106 | }; | ||
| 107 | |||
| 108 | struct lost_event { | 104 | struct lost_event { |
| 109 | struct perf_event_header header; | 105 | struct perf_event_header header; |
| 110 | u64 id; | 106 | u64 id; |
| @@ -124,11 +120,37 @@ typedef union event_union { | |||
| 124 | struct mmap_event mmap; | 120 | struct mmap_event mmap; |
| 125 | struct comm_event comm; | 121 | struct comm_event comm; |
| 126 | struct fork_event fork; | 122 | struct fork_event fork; |
| 127 | struct period_event period; | ||
| 128 | struct lost_event lost; | 123 | struct lost_event lost; |
| 129 | struct read_event read; | 124 | struct read_event read; |
| 130 | } event_t; | 125 | } event_t; |
| 131 | 126 | ||
| 127 | static int repsep_fprintf(FILE *fp, const char *fmt, ...) | ||
| 128 | { | ||
| 129 | int n; | ||
| 130 | va_list ap; | ||
| 131 | |||
| 132 | va_start(ap, fmt); | ||
| 133 | if (!field_sep) | ||
| 134 | n = vfprintf(fp, fmt, ap); | ||
| 135 | else { | ||
| 136 | char *bf = NULL; | ||
| 137 | n = vasprintf(&bf, fmt, ap); | ||
| 138 | if (n > 0) { | ||
| 139 | char *sep = bf; | ||
| 140 | while (1) { | ||
| 141 | sep = strchr(sep, *field_sep); | ||
| 142 | if (sep == NULL) | ||
| 143 | break; | ||
| 144 | *sep = '.'; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | fputs(bf, fp); | ||
| 148 | free(bf); | ||
| 149 | } | ||
| 150 | va_end(ap); | ||
| 151 | return n; | ||
| 152 | } | ||
| 153 | |||
| 132 | static LIST_HEAD(dsos); | 154 | static LIST_HEAD(dsos); |
| 133 | static struct dso *kernel_dso; | 155 | static struct dso *kernel_dso; |
| 134 | static struct dso *vdso; | 156 | static struct dso *vdso; |
| @@ -360,12 +382,28 @@ static struct thread *thread__new(pid_t pid) | |||
| 360 | return self; | 382 | return self; |
| 361 | } | 383 | } |
| 362 | 384 | ||
| 385 | static unsigned int dsos__col_width, | ||
| 386 | comms__col_width, | ||
| 387 | threads__col_width; | ||
| 388 | |||
| 363 | static int thread__set_comm(struct thread *self, const char *comm) | 389 | static int thread__set_comm(struct thread *self, const char *comm) |
| 364 | { | 390 | { |
| 365 | if (self->comm) | 391 | if (self->comm) |
| 366 | free(self->comm); | 392 | free(self->comm); |
| 367 | self->comm = strdup(comm); | 393 | self->comm = strdup(comm); |
| 368 | return self->comm ? 0 : -ENOMEM; | 394 | if (!self->comm) |
| 395 | return -ENOMEM; | ||
| 396 | |||
| 397 | if (!col_width_list_str && !field_sep && | ||
| 398 | (!comm_list || strlist__has_entry(comm_list, comm))) { | ||
| 399 | unsigned int slen = strlen(comm); | ||
| 400 | if (slen > comms__col_width) { | ||
| 401 | comms__col_width = slen; | ||
| 402 | threads__col_width = slen + 6; | ||
| 403 | } | ||
| 404 | } | ||
| 405 | |||
| 406 | return 0; | ||
| 369 | } | 407 | } |
| 370 | 408 | ||
| 371 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 409 | static size_t thread__fprintf(struct thread *self, FILE *fp) |
| @@ -536,7 +574,9 @@ struct sort_entry { | |||
| 536 | 574 | ||
| 537 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); | 575 | int64_t (*cmp)(struct hist_entry *, struct hist_entry *); |
| 538 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); | 576 | int64_t (*collapse)(struct hist_entry *, struct hist_entry *); |
| 539 | size_t (*print)(FILE *fp, struct hist_entry *); | 577 | size_t (*print)(FILE *fp, struct hist_entry *, unsigned int width); |
| 578 | unsigned int *width; | ||
| 579 | bool elide; | ||
| 540 | }; | 580 | }; |
| 541 | 581 | ||
| 542 | static int64_t cmp_null(void *l, void *r) | 582 | static int64_t cmp_null(void *l, void *r) |
| @@ -558,15 +598,17 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 558 | } | 598 | } |
| 559 | 599 | ||
| 560 | static size_t | 600 | static size_t |
| 561 | sort__thread_print(FILE *fp, struct hist_entry *self) | 601 | sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width) |
| 562 | { | 602 | { |
| 563 | return fprintf(fp, "%16s:%5d", self->thread->comm ?: "", self->thread->pid); | 603 | return repsep_fprintf(fp, "%*s:%5d", width - 6, |
| 604 | self->thread->comm ?: "", self->thread->pid); | ||
| 564 | } | 605 | } |
| 565 | 606 | ||
| 566 | static struct sort_entry sort_thread = { | 607 | static struct sort_entry sort_thread = { |
| 567 | .header = " Command: Pid", | 608 | .header = "Command: Pid", |
| 568 | .cmp = sort__thread_cmp, | 609 | .cmp = sort__thread_cmp, |
| 569 | .print = sort__thread_print, | 610 | .print = sort__thread_print, |
| 611 | .width = &threads__col_width, | ||
| 570 | }; | 612 | }; |
| 571 | 613 | ||
| 572 | /* --sort comm */ | 614 | /* --sort comm */ |
| @@ -590,16 +632,17 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | |||
| 590 | } | 632 | } |
| 591 | 633 | ||
| 592 | static size_t | 634 | static size_t |
| 593 | sort__comm_print(FILE *fp, struct hist_entry *self) | 635 | sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) |
| 594 | { | 636 | { |
| 595 | return fprintf(fp, "%16s", self->thread->comm); | 637 | return repsep_fprintf(fp, "%*s", width, self->thread->comm); |
| 596 | } | 638 | } |
| 597 | 639 | ||
| 598 | static struct sort_entry sort_comm = { | 640 | static struct sort_entry sort_comm = { |
| 599 | .header = " Command", | 641 | .header = "Command", |
| 600 | .cmp = sort__comm_cmp, | 642 | .cmp = sort__comm_cmp, |
| 601 | .collapse = sort__comm_collapse, | 643 | .collapse = sort__comm_collapse, |
| 602 | .print = sort__comm_print, | 644 | .print = sort__comm_print, |
| 645 | .width = &comms__col_width, | ||
| 603 | }; | 646 | }; |
| 604 | 647 | ||
| 605 | /* --sort dso */ | 648 | /* --sort dso */ |
| @@ -617,18 +660,19 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 617 | } | 660 | } |
| 618 | 661 | ||
| 619 | static size_t | 662 | static size_t |
| 620 | sort__dso_print(FILE *fp, struct hist_entry *self) | 663 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) |
| 621 | { | 664 | { |
| 622 | if (self->dso) | 665 | if (self->dso) |
| 623 | return fprintf(fp, "%-25s", self->dso->name); | 666 | return repsep_fprintf(fp, "%-*s", width, self->dso->name); |
| 624 | 667 | ||
| 625 | return fprintf(fp, "%016llx ", (u64)self->ip); | 668 | return repsep_fprintf(fp, "%*llx", width, (u64)self->ip); |
| 626 | } | 669 | } |
| 627 | 670 | ||
| 628 | static struct sort_entry sort_dso = { | 671 | static struct sort_entry sort_dso = { |
| 629 | .header = "Shared Object ", | 672 | .header = "Shared Object", |
| 630 | .cmp = sort__dso_cmp, | 673 | .cmp = sort__dso_cmp, |
| 631 | .print = sort__dso_print, | 674 | .print = sort__dso_print, |
| 675 | .width = &dsos__col_width, | ||
| 632 | }; | 676 | }; |
| 633 | 677 | ||
| 634 | /* --sort symbol */ | 678 | /* --sort symbol */ |
| @@ -648,22 +692,22 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 648 | } | 692 | } |
| 649 | 693 | ||
| 650 | static size_t | 694 | static size_t |
| 651 | sort__sym_print(FILE *fp, struct hist_entry *self) | 695 | sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) |
| 652 | { | 696 | { |
| 653 | size_t ret = 0; | 697 | size_t ret = 0; |
| 654 | 698 | ||
| 655 | if (verbose) | 699 | if (verbose) |
| 656 | ret += fprintf(fp, "%#018llx ", (u64)self->ip); | 700 | ret += repsep_fprintf(fp, "%#018llx ", (u64)self->ip); |
| 657 | 701 | ||
| 702 | ret += repsep_fprintf(fp, "[%c] ", self->level); | ||
| 658 | if (self->sym) { | 703 | if (self->sym) { |
| 659 | ret += fprintf(fp, "[%c] %s", | 704 | ret += repsep_fprintf(fp, "%s", self->sym->name); |
| 660 | self->dso == kernel_dso ? 'k' : | ||
| 661 | self->dso == hypervisor_dso ? 'h' : '.', self->sym->name); | ||
| 662 | 705 | ||
| 663 | if (self->sym->module) | 706 | if (self->sym->module) |
| 664 | ret += fprintf(fp, "\t[%s]", self->sym->module->name); | 707 | ret += repsep_fprintf(fp, "\t[%s]", |
| 708 | self->sym->module->name); | ||
| 665 | } else { | 709 | } else { |
| 666 | ret += fprintf(fp, "%#016llx", (u64)self->ip); | 710 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); |
| 667 | } | 711 | } |
| 668 | 712 | ||
| 669 | return ret; | 713 | return ret; |
| @@ -690,19 +734,19 @@ sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | |||
| 690 | } | 734 | } |
| 691 | 735 | ||
| 692 | static size_t | 736 | static size_t |
| 693 | sort__parent_print(FILE *fp, struct hist_entry *self) | 737 | sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width) |
| 694 | { | 738 | { |
| 695 | size_t ret = 0; | 739 | return repsep_fprintf(fp, "%-*s", width, |
| 696 | 740 | self->parent ? self->parent->name : "[other]"); | |
| 697 | ret += fprintf(fp, "%-20s", self->parent ? self->parent->name : "[other]"); | ||
| 698 | |||
| 699 | return ret; | ||
| 700 | } | 741 | } |
| 701 | 742 | ||
| 743 | static unsigned int parent_symbol__col_width; | ||
| 744 | |||
| 702 | static struct sort_entry sort_parent = { | 745 | static struct sort_entry sort_parent = { |
| 703 | .header = "Parent symbol ", | 746 | .header = "Parent symbol", |
| 704 | .cmp = sort__parent_cmp, | 747 | .cmp = sort__parent_cmp, |
| 705 | .print = sort__parent_print, | 748 | .print = sort__parent_print, |
| 749 | .width = &parent_symbol__col_width, | ||
| 706 | }; | 750 | }; |
| 707 | 751 | ||
| 708 | static int sort__need_collapse = 0; | 752 | static int sort__need_collapse = 0; |
| @@ -967,17 +1011,25 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
| 967 | return 0; | 1011 | return 0; |
| 968 | 1012 | ||
| 969 | if (total_samples) | 1013 | if (total_samples) |
| 970 | ret = percent_color_fprintf(fp, " %6.2f%%", | 1014 | ret = percent_color_fprintf(fp, |
| 971 | (self->count * 100.0) / total_samples); | 1015 | field_sep ? "%.2f" : " %6.2f%%", |
| 1016 | (self->count * 100.0) / total_samples); | ||
| 972 | else | 1017 | else |
| 973 | ret = fprintf(fp, "%12Ld ", self->count); | 1018 | ret = fprintf(fp, field_sep ? "%lld" : "%12lld ", self->count); |
| 1019 | |||
| 1020 | if (show_nr_samples) { | ||
| 1021 | if (field_sep) | ||
| 1022 | fprintf(fp, "%c%lld", *field_sep, self->count); | ||
| 1023 | else | ||
| 1024 | fprintf(fp, "%11lld", self->count); | ||
| 1025 | } | ||
| 974 | 1026 | ||
| 975 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1027 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 976 | if (exclude_other && (se == &sort_parent)) | 1028 | if (se->elide) |
| 977 | continue; | 1029 | continue; |
| 978 | 1030 | ||
| 979 | fprintf(fp, " "); | 1031 | fprintf(fp, "%s", field_sep ?: " "); |
| 980 | ret += se->print(fp, self); | 1032 | ret += se->print(fp, self, se->width ? *se->width : 0); |
| 981 | } | 1033 | } |
| 982 | 1034 | ||
| 983 | ret += fprintf(fp, "\n"); | 1035 | ret += fprintf(fp, "\n"); |
| @@ -992,6 +1044,18 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples) | |||
| 992 | * | 1044 | * |
| 993 | */ | 1045 | */ |
| 994 | 1046 | ||
| 1047 | static void dso__calc_col_width(struct dso *self) | ||
| 1048 | { | ||
| 1049 | if (!col_width_list_str && !field_sep && | ||
| 1050 | (!dso_list || strlist__has_entry(dso_list, self->name))) { | ||
| 1051 | unsigned int slen = strlen(self->name); | ||
| 1052 | if (slen > dsos__col_width) | ||
| 1053 | dsos__col_width = slen; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | self->slen_calculated = 1; | ||
| 1057 | } | ||
| 1058 | |||
| 995 | static struct symbol * | 1059 | static struct symbol * |
| 996 | resolve_symbol(struct thread *thread, struct map **mapp, | 1060 | resolve_symbol(struct thread *thread, struct map **mapp, |
| 997 | struct dso **dsop, u64 *ipp) | 1061 | struct dso **dsop, u64 *ipp) |
| @@ -1011,6 +1075,14 @@ resolve_symbol(struct thread *thread, struct map **mapp, | |||
| 1011 | 1075 | ||
| 1012 | map = thread__find_map(thread, ip); | 1076 | map = thread__find_map(thread, ip); |
| 1013 | if (map != NULL) { | 1077 | if (map != NULL) { |
| 1078 | /* | ||
| 1079 | * We have to do this here as we may have a dso | ||
| 1080 | * with no symbol hit that has a name longer than | ||
| 1081 | * the ones with symbols sampled. | ||
| 1082 | */ | ||
| 1083 | if (!sort_dso.elide && !map->dso->slen_calculated) | ||
| 1084 | dso__calc_col_width(map->dso); | ||
| 1085 | |||
| 1014 | if (mapp) | 1086 | if (mapp) |
| 1015 | *mapp = map; | 1087 | *mapp = map; |
| 1016 | got_map: | 1088 | got_map: |
| @@ -1282,35 +1354,67 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
| 1282 | struct sort_entry *se; | 1354 | struct sort_entry *se; |
| 1283 | struct rb_node *nd; | 1355 | struct rb_node *nd; |
| 1284 | size_t ret = 0; | 1356 | size_t ret = 0; |
| 1357 | unsigned int width; | ||
| 1358 | char *col_width = col_width_list_str; | ||
| 1285 | 1359 | ||
| 1286 | fprintf(fp, "\n"); | 1360 | fprintf(fp, "# Samples: %Ld\n", (u64)total_samples); |
| 1287 | fprintf(fp, "#\n"); | ||
| 1288 | fprintf(fp, "# (%Ld samples)\n", (u64)total_samples); | ||
| 1289 | fprintf(fp, "#\n"); | 1361 | fprintf(fp, "#\n"); |
| 1290 | 1362 | ||
| 1291 | fprintf(fp, "# Overhead"); | 1363 | fprintf(fp, "# Overhead"); |
| 1364 | if (show_nr_samples) { | ||
| 1365 | if (field_sep) | ||
| 1366 | fprintf(fp, "%cSamples", *field_sep); | ||
| 1367 | else | ||
| 1368 | fputs(" Samples ", fp); | ||
| 1369 | } | ||
| 1292 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1370 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 1293 | if (exclude_other && (se == &sort_parent)) | 1371 | if (se->elide) |
| 1294 | continue; | 1372 | continue; |
| 1295 | fprintf(fp, " %s", se->header); | 1373 | if (field_sep) { |
| 1374 | fprintf(fp, "%c%s", *field_sep, se->header); | ||
| 1375 | continue; | ||
| 1376 | } | ||
| 1377 | width = strlen(se->header); | ||
| 1378 | if (se->width) { | ||
| 1379 | if (col_width_list_str) { | ||
| 1380 | if (col_width) { | ||
| 1381 | *se->width = atoi(col_width); | ||
| 1382 | col_width = strchr(col_width, ','); | ||
| 1383 | if (col_width) | ||
| 1384 | ++col_width; | ||
| 1385 | } | ||
| 1386 | } | ||
| 1387 | width = *se->width = max(*se->width, width); | ||
| 1388 | } | ||
| 1389 | fprintf(fp, " %*s", width, se->header); | ||
| 1296 | } | 1390 | } |
| 1297 | fprintf(fp, "\n"); | 1391 | fprintf(fp, "\n"); |
| 1298 | 1392 | ||
| 1393 | if (field_sep) | ||
| 1394 | goto print_entries; | ||
| 1395 | |||
| 1299 | fprintf(fp, "# ........"); | 1396 | fprintf(fp, "# ........"); |
| 1397 | if (show_nr_samples) | ||
| 1398 | fprintf(fp, " .........."); | ||
| 1300 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 1399 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
| 1301 | unsigned int i; | 1400 | unsigned int i; |
| 1302 | 1401 | ||
| 1303 | if (exclude_other && (se == &sort_parent)) | 1402 | if (se->elide) |
| 1304 | continue; | 1403 | continue; |
| 1305 | 1404 | ||
| 1306 | fprintf(fp, " "); | 1405 | fprintf(fp, " "); |
| 1307 | for (i = 0; i < strlen(se->header); i++) | 1406 | if (se->width) |
| 1407 | width = *se->width; | ||
| 1408 | else | ||
| 1409 | width = strlen(se->header); | ||
| 1410 | for (i = 0; i < width; i++) | ||
| 1308 | fprintf(fp, "."); | 1411 | fprintf(fp, "."); |
| 1309 | } | 1412 | } |
| 1310 | fprintf(fp, "\n"); | 1413 | fprintf(fp, "\n"); |
| 1311 | 1414 | ||
| 1312 | fprintf(fp, "#\n"); | 1415 | fprintf(fp, "#\n"); |
| 1313 | 1416 | ||
| 1417 | print_entries: | ||
| 1314 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { | 1418 | for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) { |
| 1315 | pos = rb_entry(nd, struct hist_entry, rb_node); | 1419 | pos = rb_entry(nd, struct hist_entry, rb_node); |
| 1316 | ret += hist_entry__fprintf(fp, pos, total_samples); | 1420 | ret += hist_entry__fprintf(fp, pos, total_samples); |
| @@ -1524,19 +1628,6 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1524 | } | 1628 | } |
| 1525 | 1629 | ||
| 1526 | static int | 1630 | static int |
| 1527 | process_period_event(event_t *event, unsigned long offset, unsigned long head) | ||
| 1528 | { | ||
| 1529 | dprintf("%p [%p]: PERF_EVENT_PERIOD: time:%Ld, id:%Ld: period:%Ld\n", | ||
| 1530 | (void *)(offset + head), | ||
| 1531 | (void *)(long)(event->header.size), | ||
| 1532 | event->period.time, | ||
| 1533 | event->period.id, | ||
| 1534 | event->period.sample_period); | ||
| 1535 | |||
| 1536 | return 0; | ||
| 1537 | } | ||
| 1538 | |||
| 1539 | static int | ||
| 1540 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) | 1631 | process_lost_event(event_t *event, unsigned long offset, unsigned long head) |
| 1541 | { | 1632 | { |
| 1542 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", | 1633 | dprintf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", |
| @@ -1617,9 +1708,6 @@ process_event(event_t *event, unsigned long offset, unsigned long head) | |||
| 1617 | case PERF_EVENT_FORK: | 1708 | case PERF_EVENT_FORK: |
| 1618 | return process_fork_event(event, offset, head); | 1709 | return process_fork_event(event, offset, head); |
| 1619 | 1710 | ||
| 1620 | case PERF_EVENT_PERIOD: | ||
| 1621 | return process_period_event(event, offset, head); | ||
| 1622 | |||
| 1623 | case PERF_EVENT_LOST: | 1711 | case PERF_EVENT_LOST: |
| 1624 | return process_lost_event(event, offset, head); | 1712 | return process_lost_event(event, offset, head); |
| 1625 | 1713 | ||
| @@ -1883,6 +1971,8 @@ static const struct option options[] = { | |||
| 1883 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), | 1971 | OPT_STRING('k', "vmlinux", &vmlinux, "file", "vmlinux pathname"), |
| 1884 | OPT_BOOLEAN('m', "modules", &modules, | 1972 | OPT_BOOLEAN('m', "modules", &modules, |
| 1885 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 1973 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
| 1974 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, | ||
| 1975 | "Show a column with the number of samples"), | ||
| 1886 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 1976 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
| 1887 | "sort by key(s): pid, comm, dso, symbol, parent"), | 1977 | "sort by key(s): pid, comm, dso, symbol, parent"), |
| 1888 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 1978 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
| @@ -1891,15 +1981,21 @@ static const struct option options[] = { | |||
| 1891 | "regex filter to identify parent, see: '--sort parent'"), | 1981 | "regex filter to identify parent, see: '--sort parent'"), |
| 1892 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, | 1982 | OPT_BOOLEAN('x', "exclude-other", &exclude_other, |
| 1893 | "Only display entries with parent-match"), | 1983 | "Only display entries with parent-match"), |
| 1894 | OPT_CALLBACK_DEFAULT('c', "callchain", NULL, "output_type,min_percent", | 1984 | OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent", |
| 1895 | "Display callchains using output_type and min percent threshold. " | 1985 | "Display callchains using output_type and min percent threshold. " |
| 1896 | "Default: flat,0", &parse_callchain_opt, callchain_default_opt), | 1986 | "Default: fractal,0.5", &parse_callchain_opt, callchain_default_opt), |
| 1897 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", | 1987 | OPT_STRING('d', "dsos", &dso_list_str, "dso[,dso...]", |
| 1898 | "only consider symbols in these dsos"), | 1988 | "only consider symbols in these dsos"), |
| 1899 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", | 1989 | OPT_STRING('C', "comms", &comm_list_str, "comm[,comm...]", |
| 1900 | "only consider symbols in these comms"), | 1990 | "only consider symbols in these comms"), |
| 1901 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", | 1991 | OPT_STRING('S', "symbols", &sym_list_str, "symbol[,symbol...]", |
| 1902 | "only consider these symbols"), | 1992 | "only consider these symbols"), |
| 1993 | OPT_STRING('w', "column-widths", &col_width_list_str, | ||
| 1994 | "width[,width...]", | ||
| 1995 | "don't try to adjust column width, use these fixed values"), | ||
| 1996 | OPT_STRING('t', "field-separator", &field_sep, "separator", | ||
| 1997 | "separator for columns, no spaces will be added between " | ||
| 1998 | "columns '.' is reserved."), | ||
| 1903 | OPT_END() | 1999 | OPT_END() |
| 1904 | }; | 2000 | }; |
| 1905 | 2001 | ||
| @@ -1919,7 +2015,8 @@ static void setup_sorting(void) | |||
| 1919 | } | 2015 | } |
| 1920 | 2016 | ||
| 1921 | static void setup_list(struct strlist **list, const char *list_str, | 2017 | static void setup_list(struct strlist **list, const char *list_str, |
| 1922 | const char *list_name) | 2018 | struct sort_entry *se, const char *list_name, |
| 2019 | FILE *fp) | ||
| 1923 | { | 2020 | { |
| 1924 | if (list_str) { | 2021 | if (list_str) { |
| 1925 | *list = strlist__new(true, list_str); | 2022 | *list = strlist__new(true, list_str); |
| @@ -1928,6 +2025,11 @@ static void setup_list(struct strlist **list, const char *list_str, | |||
| 1928 | list_name); | 2025 | list_name); |
| 1929 | exit(129); | 2026 | exit(129); |
| 1930 | } | 2027 | } |
| 2028 | if (strlist__nr_entries(*list) == 1) { | ||
| 2029 | fprintf(fp, "# %s: %s\n", list_name, | ||
| 2030 | strlist__entry(*list, 0)->s); | ||
| 2031 | se->elide = true; | ||
| 2032 | } | ||
| 1931 | } | 2033 | } |
| 1932 | } | 2034 | } |
| 1933 | 2035 | ||
| @@ -1941,9 +2043,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
| 1941 | 2043 | ||
| 1942 | setup_sorting(); | 2044 | setup_sorting(); |
| 1943 | 2045 | ||
| 1944 | if (parent_pattern != default_parent_pattern) | 2046 | if (parent_pattern != default_parent_pattern) { |
| 1945 | sort_dimension__add("parent"); | 2047 | sort_dimension__add("parent"); |
| 1946 | else | 2048 | sort_parent.elide = 1; |
| 2049 | } else | ||
| 1947 | exclude_other = 0; | 2050 | exclude_other = 0; |
| 1948 | 2051 | ||
| 1949 | /* | 2052 | /* |
| @@ -1952,11 +2055,17 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
| 1952 | if (argc) | 2055 | if (argc) |
| 1953 | usage_with_options(report_usage, options); | 2056 | usage_with_options(report_usage, options); |
| 1954 | 2057 | ||
| 1955 | setup_list(&dso_list, dso_list_str, "dso"); | ||
| 1956 | setup_list(&comm_list, comm_list_str, "comm"); | ||
| 1957 | setup_list(&sym_list, sym_list_str, "symbol"); | ||
| 1958 | |||
| 1959 | setup_pager(); | 2058 | setup_pager(); |
| 1960 | 2059 | ||
| 2060 | setup_list(&dso_list, dso_list_str, &sort_dso, "dso", stdout); | ||
| 2061 | setup_list(&comm_list, comm_list_str, &sort_comm, "comm", stdout); | ||
| 2062 | setup_list(&sym_list, sym_list_str, &sort_sym, "symbol", stdout); | ||
| 2063 | |||
| 2064 | if (field_sep && *field_sep == '.') { | ||
| 2065 | fputs("'.' is the only non valid --field-separator argument\n", | ||
| 2066 | stderr); | ||
| 2067 | exit(129); | ||
| 2068 | } | ||
| 2069 | |||
| 1961 | return __cmd_report(); | 2070 | return __cmd_report(); |
| 1962 | } | 2071 | } |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 27921a8ce1a9..f9510eeeb6c7 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -511,7 +511,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
| 511 | { | 511 | { |
| 512 | int status; | 512 | int status; |
| 513 | 513 | ||
| 514 | argc = parse_options(argc, argv, options, stat_usage, 0); | 514 | argc = parse_options(argc, argv, options, stat_usage, |
| 515 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
| 515 | if (!argc) | 516 | if (!argc) |
| 516 | usage_with_options(stat_usage, options); | 517 | usage_with_options(stat_usage, options); |
| 517 | if (run_count <= 0 || run_count > MAX_RUN) | 518 | if (run_count <= 0 || run_count > MAX_RUN) |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 95d5c0ae375a..c0a423004e15 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -58,6 +58,7 @@ static u64 count_filter = 5; | |||
| 58 | static int print_entries = 15; | 58 | static int print_entries = 15; |
| 59 | 59 | ||
| 60 | static int target_pid = -1; | 60 | static int target_pid = -1; |
| 61 | static int inherit = 0; | ||
| 61 | static int profile_cpu = -1; | 62 | static int profile_cpu = -1; |
| 62 | static int nr_cpus = 0; | 63 | static int nr_cpus = 0; |
| 63 | static unsigned int realtime_prio = 0; | 64 | static unsigned int realtime_prio = 0; |
| @@ -549,7 +550,7 @@ int group_fd; | |||
| 549 | static void start_counter(int i, int counter) | 550 | static void start_counter(int i, int counter) |
| 550 | { | 551 | { |
| 551 | struct perf_counter_attr *attr; | 552 | struct perf_counter_attr *attr; |
| 552 | unsigned int cpu; | 553 | int cpu; |
| 553 | 554 | ||
| 554 | cpu = profile_cpu; | 555 | cpu = profile_cpu; |
| 555 | if (target_pid == -1 && profile_cpu == -1) | 556 | if (target_pid == -1 && profile_cpu == -1) |
| @@ -559,6 +560,7 @@ static void start_counter(int i, int counter) | |||
| 559 | 560 | ||
| 560 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 561 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; |
| 561 | attr->freq = freq; | 562 | attr->freq = freq; |
| 563 | attr->inherit = (cpu < 0) && inherit; | ||
| 562 | 564 | ||
| 563 | try_again: | 565 | try_again: |
| 564 | fd[i][counter] = sys_perf_counter_open(attr, target_pid, cpu, group_fd, 0); | 566 | fd[i][counter] = sys_perf_counter_open(attr, target_pid, cpu, group_fd, 0); |
| @@ -685,6 +687,8 @@ static const struct option options[] = { | |||
| 685 | "only display functions with more events than this"), | 687 | "only display functions with more events than this"), |
| 686 | OPT_BOOLEAN('g', "group", &group, | 688 | OPT_BOOLEAN('g', "group", &group, |
| 687 | "put the counters into a counter group"), | 689 | "put the counters into a counter group"), |
| 690 | OPT_BOOLEAN('i', "inherit", &inherit, | ||
| 691 | "child tasks inherit counters"), | ||
| 688 | OPT_STRING('s', "sym-filter", &sym_filter, "pattern", | 692 | OPT_STRING('s', "sym-filter", &sym_filter, "pattern", |
| 689 | "only display symbols matchig this pattern"), | 693 | "only display symbols matchig this pattern"), |
| 690 | OPT_BOOLEAN('z', "zero", &zero, | 694 | OPT_BOOLEAN('z', "zero", &zero, |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index c5656784c61d..31982ad064b4 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
| @@ -12,6 +12,8 @@ | |||
| 12 | #include "util/cache.h" | 12 | #include "util/cache.h" |
| 13 | #include "util/quote.h" | 13 | #include "util/quote.h" |
| 14 | #include "util/run-command.h" | 14 | #include "util/run-command.h" |
| 15 | #include "util/parse-events.h" | ||
| 16 | #include "util/string.h" | ||
| 15 | 17 | ||
| 16 | const char perf_usage_string[] = | 18 | const char perf_usage_string[] = |
| 17 | "perf [--version] [--help] COMMAND [ARGS]"; | 19 | "perf [--version] [--help] COMMAND [ARGS]"; |
| @@ -25,6 +27,8 @@ struct pager_config { | |||
| 25 | int val; | 27 | int val; |
| 26 | }; | 28 | }; |
| 27 | 29 | ||
| 30 | static char debugfs_mntpt[MAXPATHLEN]; | ||
| 31 | |||
| 28 | static int pager_command_config(const char *var, const char *value, void *data) | 32 | static int pager_command_config(const char *var, const char *value, void *data) |
| 29 | { | 33 | { |
| 30 | struct pager_config *c = data; | 34 | struct pager_config *c = data; |
| @@ -56,6 +60,15 @@ static void commit_pager_choice(void) { | |||
| 56 | } | 60 | } |
| 57 | } | 61 | } |
| 58 | 62 | ||
| 63 | static void set_debugfs_path(void) | ||
| 64 | { | ||
| 65 | char *path; | ||
| 66 | |||
| 67 | path = getenv(PERF_DEBUGFS_ENVIRONMENT); | ||
| 68 | snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt, | ||
| 69 | "tracing/events"); | ||
| 70 | } | ||
| 71 | |||
| 59 | static int handle_options(const char*** argv, int* argc, int* envchanged) | 72 | static int handle_options(const char*** argv, int* argc, int* envchanged) |
| 60 | { | 73 | { |
| 61 | int handled = 0; | 74 | int handled = 0; |
| @@ -122,6 +135,22 @@ static int handle_options(const char*** argv, int* argc, int* envchanged) | |||
| 122 | setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1); | 135 | setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1); |
| 123 | if (envchanged) | 136 | if (envchanged) |
| 124 | *envchanged = 1; | 137 | *envchanged = 1; |
| 138 | } else if (!strcmp(cmd, "--debugfs-dir")) { | ||
| 139 | if (*argc < 2) { | ||
| 140 | fprintf(stderr, "No directory given for --debugfs-dir.\n"); | ||
| 141 | usage(perf_usage_string); | ||
| 142 | } | ||
| 143 | strncpy(debugfs_mntpt, (*argv)[1], MAXPATHLEN); | ||
| 144 | debugfs_mntpt[MAXPATHLEN - 1] = '\0'; | ||
| 145 | if (envchanged) | ||
| 146 | *envchanged = 1; | ||
| 147 | (*argv)++; | ||
| 148 | (*argc)--; | ||
| 149 | } else if (!prefixcmp(cmd, "--debugfs-dir=")) { | ||
| 150 | strncpy(debugfs_mntpt, cmd + 14, MAXPATHLEN); | ||
| 151 | debugfs_mntpt[MAXPATHLEN - 1] = '\0'; | ||
| 152 | if (envchanged) | ||
| 153 | *envchanged = 1; | ||
| 125 | } else { | 154 | } else { |
| 126 | fprintf(stderr, "Unknown option: %s\n", cmd); | 155 | fprintf(stderr, "Unknown option: %s\n", cmd); |
| 127 | usage(perf_usage_string); | 156 | usage(perf_usage_string); |
| @@ -228,6 +257,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
| 228 | if (use_pager == -1 && p->option & USE_PAGER) | 257 | if (use_pager == -1 && p->option & USE_PAGER) |
| 229 | use_pager = 1; | 258 | use_pager = 1; |
| 230 | commit_pager_choice(); | 259 | commit_pager_choice(); |
| 260 | set_debugfs_path(); | ||
| 231 | 261 | ||
| 232 | status = p->fn(argc, argv, prefix); | 262 | status = p->fn(argc, argv, prefix); |
| 233 | if (status) | 263 | if (status) |
| @@ -346,6 +376,49 @@ static int run_argv(int *argcp, const char ***argv) | |||
| 346 | return done_alias; | 376 | return done_alias; |
| 347 | } | 377 | } |
| 348 | 378 | ||
| 379 | /* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */ | ||
| 380 | static void get_debugfs_mntpt(void) | ||
| 381 | { | ||
| 382 | FILE *file; | ||
| 383 | char fs_type[100]; | ||
| 384 | char debugfs[MAXPATHLEN]; | ||
| 385 | |||
| 386 | /* | ||
| 387 | * try the standard location | ||
| 388 | */ | ||
| 389 | if (valid_debugfs_mount("/sys/kernel/debug/") == 0) { | ||
| 390 | strcpy(debugfs_mntpt, "/sys/kernel/debug/"); | ||
| 391 | return; | ||
| 392 | } | ||
| 393 | |||
| 394 | /* | ||
| 395 | * try the sane location | ||
| 396 | */ | ||
| 397 | if (valid_debugfs_mount("/debug/") == 0) { | ||
| 398 | strcpy(debugfs_mntpt, "/debug/"); | ||
| 399 | return; | ||
| 400 | } | ||
| 401 | |||
| 402 | /* | ||
| 403 | * give up and parse /proc/mounts | ||
| 404 | */ | ||
| 405 | file = fopen("/proc/mounts", "r"); | ||
| 406 | if (file == NULL) | ||
| 407 | return; | ||
| 408 | |||
| 409 | while (fscanf(file, "%*s %" | ||
| 410 | STR(MAXPATHLEN) | ||
| 411 | "s %99s %*s %*d %*d\n", | ||
| 412 | debugfs, fs_type) == 2) { | ||
| 413 | if (strcmp(fs_type, "debugfs") == 0) | ||
| 414 | break; | ||
| 415 | } | ||
| 416 | fclose(file); | ||
| 417 | if (strcmp(fs_type, "debugfs") == 0) { | ||
| 418 | strncpy(debugfs_mntpt, debugfs, MAXPATHLEN); | ||
| 419 | debugfs_mntpt[MAXPATHLEN - 1] = '\0'; | ||
| 420 | } | ||
| 421 | } | ||
| 349 | 422 | ||
| 350 | int main(int argc, const char **argv) | 423 | int main(int argc, const char **argv) |
| 351 | { | 424 | { |
| @@ -354,7 +427,8 @@ int main(int argc, const char **argv) | |||
| 354 | cmd = perf_extract_argv0_path(argv[0]); | 427 | cmd = perf_extract_argv0_path(argv[0]); |
| 355 | if (!cmd) | 428 | if (!cmd) |
| 356 | cmd = "perf-help"; | 429 | cmd = "perf-help"; |
| 357 | 430 | /* get debugfs mount point from /proc/mounts */ | |
| 431 | get_debugfs_mntpt(); | ||
| 358 | /* | 432 | /* |
| 359 | * "perf-xxxx" is the same as "perf xxxx", but we obviously: | 433 | * "perf-xxxx" is the same as "perf xxxx", but we obviously: |
| 360 | * | 434 | * |
| @@ -377,6 +451,7 @@ int main(int argc, const char **argv) | |||
| 377 | argc--; | 451 | argc--; |
| 378 | handle_options(&argv, &argc, NULL); | 452 | handle_options(&argv, &argc, NULL); |
| 379 | commit_pager_choice(); | 453 | commit_pager_choice(); |
| 454 | set_debugfs_path(); | ||
| 380 | if (argc > 0) { | 455 | if (argc > 0) { |
| 381 | if (!prefixcmp(argv[0], "--")) | 456 | if (!prefixcmp(argv[0], "--")) |
| 382 | argv[0] += 2; | 457 | argv[0] += 2; |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 63e67cc5487b..e5148e2b6134 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
| @@ -1,7 +1,13 @@ | |||
| 1 | #ifndef _PERF_PERF_H | 1 | #ifndef _PERF_PERF_H |
| 2 | #define _PERF_PERF_H | 2 | #define _PERF_PERF_H |
| 3 | 3 | ||
| 4 | #if defined(__x86_64__) || defined(__i386__) | 4 | #if defined(__i386__) |
| 5 | #include "../../arch/x86/include/asm/unistd.h" | ||
| 6 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | ||
| 7 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | ||
| 8 | #endif | ||
| 9 | |||
| 10 | #if defined(__x86_64__) | ||
| 5 | #include "../../arch/x86/include/asm/unistd.h" | 11 | #include "../../arch/x86/include/asm/unistd.h" |
| 6 | #define rmb() asm volatile("lfence" ::: "memory") | 12 | #define rmb() asm volatile("lfence" ::: "memory") |
| 7 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 13 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 161d5f413e28..4b50c412b9c5 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #define PERFATTRIBUTES_FILE ".perfattributes" | 18 | #define PERFATTRIBUTES_FILE ".perfattributes" |
| 19 | #define INFOATTRIBUTES_FILE "info/attributes" | 19 | #define INFOATTRIBUTES_FILE "info/attributes" |
| 20 | #define ATTRIBUTE_MACRO_PREFIX "[attr]" | 20 | #define ATTRIBUTE_MACRO_PREFIX "[attr]" |
| 21 | #define PERF_DEBUGFS_ENVIRONMENT "PERF_DEBUGFS_DIR" | ||
| 21 | 22 | ||
| 22 | typedef int (*config_fn_t)(const char *, const char *, void *); | 23 | typedef int (*config_fn_t)(const char *, const char *, void *); |
| 23 | extern int perf_default_config(const char *, const char *, void *); | 24 | extern int perf_default_config(const char *, const char *, void *); |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index b5ef53ad4c7a..bf280449fcfd 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
| @@ -16,7 +16,7 @@ struct perf_header { | |||
| 16 | int frozen; | 16 | int frozen; |
| 17 | int attrs, size; | 17 | int attrs, size; |
| 18 | struct perf_header_attr **attr; | 18 | struct perf_header_attr **attr; |
| 19 | off_t attr_offset; | 19 | s64 attr_offset; |
| 20 | u64 data_offset; | 20 | u64 data_offset; |
| 21 | u64 data_size; | 21 | u64 data_size; |
| 22 | }; | 22 | }; |
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index 99c1b3d1edd9..a6b87390cb52 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h | |||
| @@ -18,4 +18,12 @@ | |||
| 18 | (type *)((char *)__mptr - offsetof(type, member)); }) | 18 | (type *)((char *)__mptr - offsetof(type, member)); }) |
| 19 | #endif | 19 | #endif |
| 20 | 20 | ||
| 21 | #ifndef max | ||
| 22 | #define max(x, y) ({ \ | ||
| 23 | typeof(x) _max1 = (x); \ | ||
| 24 | typeof(y) _max2 = (y); \ | ||
| 25 | (void) (&_max1 == &_max2); \ | ||
| 26 | _max1 > _max2 ? _max1 : _max2; }) | ||
| 27 | #endif | ||
| 28 | |||
| 21 | #endif | 29 | #endif |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 5184959e0615..7bdad8df22a6 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "parse-events.h" | 5 | #include "parse-events.h" |
| 6 | #include "exec_cmd.h" | 6 | #include "exec_cmd.h" |
| 7 | #include "string.h" | 7 | #include "string.h" |
| 8 | #include "cache.h" | ||
| 8 | 9 | ||
| 9 | extern char *strcasestr(const char *haystack, const char *needle); | 10 | extern char *strcasestr(const char *haystack, const char *needle); |
| 10 | 11 | ||
| @@ -19,6 +20,8 @@ struct event_symbol { | |||
| 19 | char *alias; | 20 | char *alias; |
| 20 | }; | 21 | }; |
| 21 | 22 | ||
| 23 | char debugfs_path[MAXPATHLEN]; | ||
| 24 | |||
| 22 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x | 25 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x |
| 23 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x | 26 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x |
| 24 | 27 | ||
| @@ -71,8 +74,8 @@ static char *sw_event_names[] = { | |||
| 71 | #define MAX_ALIASES 8 | 74 | #define MAX_ALIASES 8 |
| 72 | 75 | ||
| 73 | static char *hw_cache[][MAX_ALIASES] = { | 76 | static char *hw_cache[][MAX_ALIASES] = { |
| 74 | { "L1-d$", "l1-d", "l1d", "L1-data", }, | 77 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, |
| 75 | { "L1-i$", "l1-i", "l1i", "L1-instruction", }, | 78 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, |
| 76 | { "LLC", "L2" }, | 79 | { "LLC", "L2" }, |
| 77 | { "dTLB", "d-tlb", "Data-TLB", }, | 80 | { "dTLB", "d-tlb", "Data-TLB", }, |
| 78 | { "iTLB", "i-tlb", "Instruction-TLB", }, | 81 | { "iTLB", "i-tlb", "Instruction-TLB", }, |
| @@ -110,6 +113,88 @@ static unsigned long hw_cache_stat[C(MAX)] = { | |||
| 110 | [C(BPU)] = (CACHE_READ), | 113 | [C(BPU)] = (CACHE_READ), |
| 111 | }; | 114 | }; |
| 112 | 115 | ||
| 116 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next, file, st) \ | ||
| 117 | while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ | ||
| 118 | if (snprintf(file, MAXPATHLEN, "%s/%s", debugfs_path, \ | ||
| 119 | sys_dirent.d_name) && \ | ||
| 120 | (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ | ||
| 121 | (strcmp(sys_dirent.d_name, ".")) && \ | ||
| 122 | (strcmp(sys_dirent.d_name, ".."))) | ||
| 123 | |||
| 124 | #define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, file, st) \ | ||
| 125 | while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ | ||
| 126 | if (snprintf(file, MAXPATHLEN, "%s/%s/%s", debugfs_path, \ | ||
| 127 | sys_dirent.d_name, evt_dirent.d_name) && \ | ||
| 128 | (!stat(file, &st)) && (S_ISDIR(st.st_mode)) && \ | ||
| 129 | (strcmp(evt_dirent.d_name, ".")) && \ | ||
| 130 | (strcmp(evt_dirent.d_name, ".."))) | ||
| 131 | |||
| 132 | #define MAX_EVENT_LENGTH 30 | ||
| 133 | |||
| 134 | int valid_debugfs_mount(const char *debugfs) | ||
| 135 | { | ||
| 136 | struct statfs st_fs; | ||
| 137 | |||
| 138 | if (statfs(debugfs, &st_fs) < 0) | ||
| 139 | return -ENOENT; | ||
| 140 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
| 141 | return -ENOENT; | ||
| 142 | return 0; | ||
| 143 | } | ||
| 144 | |||
| 145 | static char *tracepoint_id_to_name(u64 config) | ||
| 146 | { | ||
| 147 | static char tracepoint_name[2 * MAX_EVENT_LENGTH]; | ||
| 148 | DIR *sys_dir, *evt_dir; | ||
| 149 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
| 150 | struct stat st; | ||
| 151 | char id_buf[4]; | ||
| 152 | int fd; | ||
| 153 | u64 id; | ||
| 154 | char evt_path[MAXPATHLEN]; | ||
| 155 | |||
| 156 | if (valid_debugfs_mount(debugfs_path)) | ||
| 157 | return "unkown"; | ||
| 158 | |||
| 159 | sys_dir = opendir(debugfs_path); | ||
| 160 | if (!sys_dir) | ||
| 161 | goto cleanup; | ||
| 162 | |||
| 163 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
| 164 | evt_dir = opendir(evt_path); | ||
| 165 | if (!evt_dir) | ||
| 166 | goto cleanup; | ||
| 167 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
| 168 | evt_path, st) { | ||
| 169 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", | ||
| 170 | debugfs_path, sys_dirent.d_name, | ||
| 171 | evt_dirent.d_name); | ||
| 172 | fd = open(evt_path, O_RDONLY); | ||
| 173 | if (fd < 0) | ||
| 174 | continue; | ||
| 175 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
| 176 | close(fd); | ||
| 177 | continue; | ||
| 178 | } | ||
| 179 | close(fd); | ||
| 180 | id = atoll(id_buf); | ||
| 181 | if (id == config) { | ||
| 182 | closedir(evt_dir); | ||
| 183 | closedir(sys_dir); | ||
| 184 | snprintf(tracepoint_name, 2 * MAX_EVENT_LENGTH, | ||
| 185 | "%s:%s", sys_dirent.d_name, | ||
| 186 | evt_dirent.d_name); | ||
| 187 | return tracepoint_name; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | closedir(evt_dir); | ||
| 191 | } | ||
| 192 | |||
| 193 | cleanup: | ||
| 194 | closedir(sys_dir); | ||
| 195 | return "unkown"; | ||
| 196 | } | ||
| 197 | |||
| 113 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) | 198 | static int is_cache_op_valid(u8 cache_type, u8 cache_op) |
| 114 | { | 199 | { |
| 115 | if (hw_cache_stat[cache_type] & COP(cache_op)) | 200 | if (hw_cache_stat[cache_type] & COP(cache_op)) |
| @@ -177,6 +262,9 @@ char *event_name(int counter) | |||
| 177 | return sw_event_names[config]; | 262 | return sw_event_names[config]; |
| 178 | return "unknown-software"; | 263 | return "unknown-software"; |
| 179 | 264 | ||
| 265 | case PERF_TYPE_TRACEPOINT: | ||
| 266 | return tracepoint_id_to_name(config); | ||
| 267 | |||
| 180 | default: | 268 | default: |
| 181 | break; | 269 | break; |
| 182 | } | 270 | } |
| @@ -265,6 +353,53 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) | |||
| 265 | return 1; | 353 | return 1; |
| 266 | } | 354 | } |
| 267 | 355 | ||
| 356 | static int parse_tracepoint_event(const char **strp, | ||
| 357 | struct perf_counter_attr *attr) | ||
| 358 | { | ||
| 359 | const char *evt_name; | ||
| 360 | char sys_name[MAX_EVENT_LENGTH]; | ||
| 361 | char id_buf[4]; | ||
| 362 | int fd; | ||
| 363 | unsigned int sys_length, evt_length; | ||
| 364 | u64 id; | ||
| 365 | char evt_path[MAXPATHLEN]; | ||
| 366 | |||
| 367 | if (valid_debugfs_mount(debugfs_path)) | ||
| 368 | return 0; | ||
| 369 | |||
| 370 | evt_name = strchr(*strp, ':'); | ||
| 371 | if (!evt_name) | ||
| 372 | return 0; | ||
| 373 | |||
| 374 | sys_length = evt_name - *strp; | ||
| 375 | if (sys_length >= MAX_EVENT_LENGTH) | ||
| 376 | return 0; | ||
| 377 | |||
| 378 | strncpy(sys_name, *strp, sys_length); | ||
| 379 | sys_name[sys_length] = '\0'; | ||
| 380 | evt_name = evt_name + 1; | ||
| 381 | evt_length = strlen(evt_name); | ||
| 382 | if (evt_length >= MAX_EVENT_LENGTH) | ||
| 383 | return 0; | ||
| 384 | |||
| 385 | snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path, | ||
| 386 | sys_name, evt_name); | ||
| 387 | fd = open(evt_path, O_RDONLY); | ||
| 388 | if (fd < 0) | ||
| 389 | return 0; | ||
| 390 | |||
| 391 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | ||
| 392 | close(fd); | ||
| 393 | return 0; | ||
| 394 | } | ||
| 395 | close(fd); | ||
| 396 | id = atoll(id_buf); | ||
| 397 | attr->config = id; | ||
| 398 | attr->type = PERF_TYPE_TRACEPOINT; | ||
| 399 | *strp = evt_name + evt_length; | ||
| 400 | return 1; | ||
| 401 | } | ||
| 402 | |||
| 268 | static int check_events(const char *str, unsigned int i) | 403 | static int check_events(const char *str, unsigned int i) |
| 269 | { | 404 | { |
| 270 | int n; | 405 | int n; |
| @@ -374,7 +509,8 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr) | |||
| 374 | */ | 509 | */ |
| 375 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) | 510 | static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) |
| 376 | { | 511 | { |
| 377 | if (!(parse_raw_event(str, attr) || | 512 | if (!(parse_tracepoint_event(str, attr) || |
| 513 | parse_raw_event(str, attr) || | ||
| 378 | parse_numeric_event(str, attr) || | 514 | parse_numeric_event(str, attr) || |
| 379 | parse_symbolic_event(str, attr) || | 515 | parse_symbolic_event(str, attr) || |
| 380 | parse_generic_hw_event(str, attr))) | 516 | parse_generic_hw_event(str, attr))) |
| @@ -423,6 +559,42 @@ static const char * const event_type_descriptors[] = { | |||
| 423 | }; | 559 | }; |
| 424 | 560 | ||
| 425 | /* | 561 | /* |
| 562 | * Print the events from <debugfs_mount_point>/tracing/events | ||
| 563 | */ | ||
| 564 | |||
| 565 | static void print_tracepoint_events(void) | ||
| 566 | { | ||
| 567 | DIR *sys_dir, *evt_dir; | ||
| 568 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | ||
| 569 | struct stat st; | ||
| 570 | char evt_path[MAXPATHLEN]; | ||
| 571 | |||
| 572 | if (valid_debugfs_mount(debugfs_path)) | ||
| 573 | return; | ||
| 574 | |||
| 575 | sys_dir = opendir(debugfs_path); | ||
| 576 | if (!sys_dir) | ||
| 577 | goto cleanup; | ||
| 578 | |||
| 579 | for_each_subsystem(sys_dir, sys_dirent, sys_next, evt_path, st) { | ||
| 580 | evt_dir = opendir(evt_path); | ||
| 581 | if (!evt_dir) | ||
| 582 | goto cleanup; | ||
| 583 | for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next, | ||
| 584 | evt_path, st) { | ||
| 585 | snprintf(evt_path, MAXPATHLEN, "%s:%s", | ||
| 586 | sys_dirent.d_name, evt_dirent.d_name); | ||
| 587 | fprintf(stderr, " %-40s [%s]\n", evt_path, | ||
| 588 | event_type_descriptors[PERF_TYPE_TRACEPOINT+1]); | ||
| 589 | } | ||
| 590 | closedir(evt_dir); | ||
| 591 | } | ||
| 592 | |||
| 593 | cleanup: | ||
| 594 | closedir(sys_dir); | ||
| 595 | } | ||
| 596 | |||
| 597 | /* | ||
| 426 | * Print the help text for the event symbols: | 598 | * Print the help text for the event symbols: |
| 427 | */ | 599 | */ |
| 428 | void print_events(void) | 600 | void print_events(void) |
| @@ -436,7 +608,7 @@ void print_events(void) | |||
| 436 | 608 | ||
| 437 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { | 609 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++, syms++) { |
| 438 | type = syms->type + 1; | 610 | type = syms->type + 1; |
| 439 | if (type > ARRAY_SIZE(event_type_descriptors)) | 611 | if (type >= ARRAY_SIZE(event_type_descriptors)) |
| 440 | type = 0; | 612 | type = 0; |
| 441 | 613 | ||
| 442 | if (type != prev_type) | 614 | if (type != prev_type) |
| @@ -472,5 +644,7 @@ void print_events(void) | |||
| 472 | "rNNN"); | 644 | "rNNN"); |
| 473 | fprintf(stderr, "\n"); | 645 | fprintf(stderr, "\n"); |
| 474 | 646 | ||
| 647 | print_tracepoint_events(); | ||
| 648 | |||
| 475 | exit(129); | 649 | exit(129); |
| 476 | } | 650 | } |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index e3d552908e60..1ea5d09b6eb1 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
| @@ -3,6 +3,8 @@ | |||
| 3 | * Parse symbolic events/counts passed in as options: | 3 | * Parse symbolic events/counts passed in as options: |
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| 6 | struct option; | ||
| 7 | |||
| 6 | extern int nr_counters; | 8 | extern int nr_counters; |
| 7 | 9 | ||
| 8 | extern struct perf_counter_attr attrs[MAX_COUNTERS]; | 10 | extern struct perf_counter_attr attrs[MAX_COUNTERS]; |
| @@ -15,3 +17,6 @@ extern int parse_events(const struct option *opt, const char *str, int unset); | |||
| 15 | 17 | ||
| 16 | extern void print_events(void); | 18 | extern void print_events(void); |
| 17 | 19 | ||
| 20 | extern char debugfs_path[]; | ||
| 21 | extern int valid_debugfs_mount(const char *debugfs); | ||
| 22 | |||
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index 3dca2f654cd0..bf39dfadfd24 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
| @@ -5,4 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | int hex2u64(const char *ptr, u64 *val); | 6 | int hex2u64(const char *ptr, u64 *val); |
| 7 | 7 | ||
| 8 | #define _STR(x) #x | ||
| 9 | #define STR(x) _STR(x) | ||
| 10 | |||
| 8 | #endif | 11 | #endif |
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 025a78edfffe..7ad38171dc2b 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c | |||
| @@ -64,6 +64,7 @@ int strlist__add(struct strlist *self, const char *new_entry) | |||
| 64 | 64 | ||
| 65 | rb_link_node(&sn->rb_node, parent, p); | 65 | rb_link_node(&sn->rb_node, parent, p); |
| 66 | rb_insert_color(&sn->rb_node, &self->entries); | 66 | rb_insert_color(&sn->rb_node, &self->entries); |
| 67 | ++self->nr_entries; | ||
| 67 | 68 | ||
| 68 | return 0; | 69 | return 0; |
| 69 | } | 70 | } |
| @@ -155,8 +156,9 @@ struct strlist *strlist__new(bool dupstr, const char *slist) | |||
| 155 | struct strlist *self = malloc(sizeof(*self)); | 156 | struct strlist *self = malloc(sizeof(*self)); |
| 156 | 157 | ||
| 157 | if (self != NULL) { | 158 | if (self != NULL) { |
| 158 | self->entries = RB_ROOT; | 159 | self->entries = RB_ROOT; |
| 159 | self->dupstr = dupstr; | 160 | self->dupstr = dupstr; |
| 161 | self->nr_entries = 0; | ||
| 160 | if (slist && strlist__parse_list(self, slist) != 0) | 162 | if (slist && strlist__parse_list(self, slist) != 0) |
| 161 | goto out_error; | 163 | goto out_error; |
| 162 | } | 164 | } |
| @@ -182,3 +184,17 @@ void strlist__delete(struct strlist *self) | |||
| 182 | free(self); | 184 | free(self); |
| 183 | } | 185 | } |
| 184 | } | 186 | } |
| 187 | |||
| 188 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx) | ||
| 189 | { | ||
| 190 | struct rb_node *nd; | ||
| 191 | |||
| 192 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | ||
| 193 | struct str_node *pos = rb_entry(nd, struct str_node, rb_node); | ||
| 194 | |||
| 195 | if (!idx--) | ||
| 196 | return pos; | ||
| 197 | } | ||
| 198 | |||
| 199 | return NULL; | ||
| 200 | } | ||
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index 2fdcfee87586..921818e44a54 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h | |||
| @@ -11,7 +11,8 @@ struct str_node { | |||
| 11 | 11 | ||
| 12 | struct strlist { | 12 | struct strlist { |
| 13 | struct rb_root entries; | 13 | struct rb_root entries; |
| 14 | bool dupstr; | 14 | unsigned int nr_entries; |
| 15 | bool dupstr; | ||
| 15 | }; | 16 | }; |
| 16 | 17 | ||
| 17 | struct strlist *strlist__new(bool dupstr, const char *slist); | 18 | struct strlist *strlist__new(bool dupstr, const char *slist); |
| @@ -21,11 +22,17 @@ void strlist__remove(struct strlist *self, struct str_node *sn); | |||
| 21 | int strlist__load(struct strlist *self, const char *filename); | 22 | int strlist__load(struct strlist *self, const char *filename); |
| 22 | int strlist__add(struct strlist *self, const char *str); | 23 | int strlist__add(struct strlist *self, const char *str); |
| 23 | 24 | ||
| 25 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); | ||
| 24 | bool strlist__has_entry(struct strlist *self, const char *entry); | 26 | bool strlist__has_entry(struct strlist *self, const char *entry); |
| 25 | 27 | ||
| 26 | static inline bool strlist__empty(const struct strlist *self) | 28 | static inline bool strlist__empty(const struct strlist *self) |
| 27 | { | 29 | { |
| 28 | return rb_first(&self->entries) == NULL; | 30 | return self->nr_entries == 0; |
| 31 | } | ||
| 32 | |||
| 33 | static inline unsigned int strlist__nr_entries(const struct strlist *self) | ||
| 34 | { | ||
| 35 | return self->nr_entries; | ||
| 29 | } | 36 | } |
| 30 | 37 | ||
| 31 | int strlist__parse_list(struct strlist *self, const char *s); | 38 | int strlist__parse_list(struct strlist *self, const char *s); |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 4683b67b5ee4..28106059bf12 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -6,9 +6,15 @@ | |||
| 6 | #include <libelf.h> | 6 | #include <libelf.h> |
| 7 | #include <gelf.h> | 7 | #include <gelf.h> |
| 8 | #include <elf.h> | 8 | #include <elf.h> |
| 9 | #include <bfd.h> | ||
| 9 | 10 | ||
| 10 | const char *sym_hist_filter; | 11 | const char *sym_hist_filter; |
| 11 | 12 | ||
| 13 | #ifndef DMGL_PARAMS | ||
| 14 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | ||
| 15 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | ||
| 16 | #endif | ||
| 17 | |||
| 12 | static struct symbol *symbol__new(u64 start, u64 len, | 18 | static struct symbol *symbol__new(u64 start, u64 len, |
| 13 | const char *name, unsigned int priv_size, | 19 | const char *name, unsigned int priv_size, |
| 14 | u64 obj_start, int verbose) | 20 | u64 obj_start, int verbose) |
| @@ -65,6 +71,7 @@ struct dso *dso__new(const char *name, unsigned int sym_priv_size) | |||
| 65 | self->syms = RB_ROOT; | 71 | self->syms = RB_ROOT; |
| 66 | self->sym_priv_size = sym_priv_size; | 72 | self->sym_priv_size = sym_priv_size; |
| 67 | self->find_symbol = dso__find_symbol; | 73 | self->find_symbol = dso__find_symbol; |
| 74 | self->slen_calculated = 0; | ||
| 68 | } | 75 | } |
| 69 | 76 | ||
| 70 | return self; | 77 | return self; |
| @@ -373,36 +380,61 @@ static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | |||
| 373 | idx < nr_entries; \ | 380 | idx < nr_entries; \ |
| 374 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) | 381 | ++idx, pos = gelf_getrela(reldata, idx, &pos_mem)) |
| 375 | 382 | ||
| 376 | static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | 383 | /* |
| 377 | GElf_Ehdr *ehdr, Elf_Scn *scn_dynsym, | 384 | * We need to check if we have a .dynsym, so that we can handle the |
| 378 | GElf_Shdr *shdr_dynsym, | 385 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it |
| 379 | size_t dynsym_idx, int verbose) | 386 | * .dynsym or .symtab). |
| 387 | * And always look at the original dso, not at debuginfo packages, that | ||
| 388 | * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS). | ||
| 389 | */ | ||
| 390 | static int dso__synthesize_plt_symbols(struct dso *self, int verbose) | ||
| 380 | { | 391 | { |
| 381 | uint32_t nr_rel_entries, idx; | 392 | uint32_t nr_rel_entries, idx; |
| 382 | GElf_Sym sym; | 393 | GElf_Sym sym; |
| 383 | u64 plt_offset; | 394 | u64 plt_offset; |
| 384 | GElf_Shdr shdr_plt; | 395 | GElf_Shdr shdr_plt; |
| 385 | struct symbol *f; | 396 | struct symbol *f; |
| 386 | GElf_Shdr shdr_rel_plt; | 397 | GElf_Shdr shdr_rel_plt, shdr_dynsym; |
| 387 | Elf_Data *reldata, *syms, *symstrs; | 398 | Elf_Data *reldata, *syms, *symstrs; |
| 388 | Elf_Scn *scn_plt_rel, *scn_symstrs; | 399 | Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym; |
| 400 | size_t dynsym_idx; | ||
| 401 | GElf_Ehdr ehdr; | ||
| 389 | char sympltname[1024]; | 402 | char sympltname[1024]; |
| 390 | int nr = 0, symidx; | 403 | Elf *elf; |
| 404 | int nr = 0, symidx, fd, err = 0; | ||
| 405 | |||
| 406 | fd = open(self->name, O_RDONLY); | ||
| 407 | if (fd < 0) | ||
| 408 | goto out; | ||
| 409 | |||
| 410 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | ||
| 411 | if (elf == NULL) | ||
| 412 | goto out_close; | ||
| 413 | |||
| 414 | if (gelf_getehdr(elf, &ehdr) == NULL) | ||
| 415 | goto out_elf_end; | ||
| 416 | |||
| 417 | scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym, | ||
| 418 | ".dynsym", &dynsym_idx); | ||
| 419 | if (scn_dynsym == NULL) | ||
| 420 | goto out_elf_end; | ||
| 391 | 421 | ||
| 392 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | 422 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, |
| 393 | ".rela.plt", NULL); | 423 | ".rela.plt", NULL); |
| 394 | if (scn_plt_rel == NULL) { | 424 | if (scn_plt_rel == NULL) { |
| 395 | scn_plt_rel = elf_section_by_name(elf, ehdr, &shdr_rel_plt, | 425 | scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt, |
| 396 | ".rel.plt", NULL); | 426 | ".rel.plt", NULL); |
| 397 | if (scn_plt_rel == NULL) | 427 | if (scn_plt_rel == NULL) |
| 398 | return 0; | 428 | goto out_elf_end; |
| 399 | } | 429 | } |
| 400 | 430 | ||
| 431 | err = -1; | ||
| 432 | |||
| 401 | if (shdr_rel_plt.sh_link != dynsym_idx) | 433 | if (shdr_rel_plt.sh_link != dynsym_idx) |
| 402 | return 0; | 434 | goto out_elf_end; |
| 403 | 435 | ||
| 404 | if (elf_section_by_name(elf, ehdr, &shdr_plt, ".plt", NULL) == NULL) | 436 | if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL) |
| 405 | return 0; | 437 | goto out_elf_end; |
| 406 | 438 | ||
| 407 | /* | 439 | /* |
| 408 | * Fetch the relocation section to find the indexes to the GOT | 440 | * Fetch the relocation section to find the indexes to the GOT |
| @@ -410,19 +442,19 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 410 | */ | 442 | */ |
| 411 | reldata = elf_getdata(scn_plt_rel, NULL); | 443 | reldata = elf_getdata(scn_plt_rel, NULL); |
| 412 | if (reldata == NULL) | 444 | if (reldata == NULL) |
| 413 | return -1; | 445 | goto out_elf_end; |
| 414 | 446 | ||
| 415 | syms = elf_getdata(scn_dynsym, NULL); | 447 | syms = elf_getdata(scn_dynsym, NULL); |
| 416 | if (syms == NULL) | 448 | if (syms == NULL) |
| 417 | return -1; | 449 | goto out_elf_end; |
| 418 | 450 | ||
| 419 | scn_symstrs = elf_getscn(elf, shdr_dynsym->sh_link); | 451 | scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link); |
| 420 | if (scn_symstrs == NULL) | 452 | if (scn_symstrs == NULL) |
| 421 | return -1; | 453 | goto out_elf_end; |
| 422 | 454 | ||
| 423 | symstrs = elf_getdata(scn_symstrs, NULL); | 455 | symstrs = elf_getdata(scn_symstrs, NULL); |
| 424 | if (symstrs == NULL) | 456 | if (symstrs == NULL) |
| 425 | return -1; | 457 | goto out_elf_end; |
| 426 | 458 | ||
| 427 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; | 459 | nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize; |
| 428 | plt_offset = shdr_plt.sh_offset; | 460 | plt_offset = shdr_plt.sh_offset; |
| @@ -441,7 +473,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 441 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 473 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
| 442 | sympltname, self->sym_priv_size, 0, verbose); | 474 | sympltname, self->sym_priv_size, 0, verbose); |
| 443 | if (!f) | 475 | if (!f) |
| 444 | return -1; | 476 | goto out_elf_end; |
| 445 | 477 | ||
| 446 | dso__insert_symbol(self, f); | 478 | dso__insert_symbol(self, f); |
| 447 | ++nr; | 479 | ++nr; |
| @@ -459,19 +491,25 @@ static int dso__synthesize_plt_symbols(struct dso *self, Elf *elf, | |||
| 459 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, | 491 | f = symbol__new(plt_offset, shdr_plt.sh_entsize, |
| 460 | sympltname, self->sym_priv_size, 0, verbose); | 492 | sympltname, self->sym_priv_size, 0, verbose); |
| 461 | if (!f) | 493 | if (!f) |
| 462 | return -1; | 494 | goto out_elf_end; |
| 463 | 495 | ||
| 464 | dso__insert_symbol(self, f); | 496 | dso__insert_symbol(self, f); |
| 465 | ++nr; | 497 | ++nr; |
| 466 | } | 498 | } |
| 467 | } else { | ||
| 468 | /* | ||
| 469 | * TODO: There are still one more shdr_rel_plt.sh_type | ||
| 470 | * I have to investigate, but probably should be ignored. | ||
| 471 | */ | ||
| 472 | } | 499 | } |
| 473 | 500 | ||
| 474 | return nr; | 501 | err = 0; |
| 502 | out_elf_end: | ||
| 503 | elf_end(elf); | ||
| 504 | out_close: | ||
| 505 | close(fd); | ||
| 506 | |||
| 507 | if (err == 0) | ||
| 508 | return nr; | ||
| 509 | out: | ||
| 510 | fprintf(stderr, "%s: problems reading %s PLT info.\n", | ||
| 511 | __func__, self->name); | ||
| 512 | return 0; | ||
| 475 | } | 513 | } |
| 476 | 514 | ||
| 477 | static int dso__load_sym(struct dso *self, int fd, const char *name, | 515 | static int dso__load_sym(struct dso *self, int fd, const char *name, |
| @@ -485,10 +523,9 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 485 | GElf_Shdr shdr; | 523 | GElf_Shdr shdr; |
| 486 | Elf_Data *syms; | 524 | Elf_Data *syms; |
| 487 | GElf_Sym sym; | 525 | GElf_Sym sym; |
| 488 | Elf_Scn *sec, *sec_dynsym, *sec_strndx; | 526 | Elf_Scn *sec, *sec_strndx; |
| 489 | Elf *elf; | 527 | Elf *elf; |
| 490 | size_t dynsym_idx; | 528 | int nr = 0, kernel = !strcmp("[kernel]", self->name); |
| 491 | int nr = 0; | ||
| 492 | 529 | ||
| 493 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); | 530 | elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); |
| 494 | if (elf == NULL) { | 531 | if (elf == NULL) { |
| @@ -504,32 +541,11 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 504 | goto out_elf_end; | 541 | goto out_elf_end; |
| 505 | } | 542 | } |
| 506 | 543 | ||
| 507 | /* | ||
| 508 | * We need to check if we have a .dynsym, so that we can handle the | ||
| 509 | * .plt, synthesizing its symbols, that aren't on the symtabs (be it | ||
| 510 | * .dynsym or .symtab) | ||
| 511 | */ | ||
| 512 | sec_dynsym = elf_section_by_name(elf, &ehdr, &shdr, | ||
| 513 | ".dynsym", &dynsym_idx); | ||
| 514 | if (sec_dynsym != NULL) { | ||
| 515 | nr = dso__synthesize_plt_symbols(self, elf, &ehdr, | ||
| 516 | sec_dynsym, &shdr, | ||
| 517 | dynsym_idx, verbose); | ||
| 518 | if (nr < 0) | ||
| 519 | goto out_elf_end; | ||
| 520 | } | ||
| 521 | |||
| 522 | /* | ||
| 523 | * But if we have a full .symtab (that is a superset of .dynsym) we | ||
| 524 | * should add the symbols not in the .dynsyn | ||
| 525 | */ | ||
| 526 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | 544 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); |
| 527 | if (sec == NULL) { | 545 | if (sec == NULL) { |
| 528 | if (sec_dynsym == NULL) | 546 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); |
| 547 | if (sec == NULL) | ||
| 529 | goto out_elf_end; | 548 | goto out_elf_end; |
| 530 | |||
| 531 | sec = sec_dynsym; | ||
| 532 | gelf_getshdr(sec, &shdr); | ||
| 533 | } | 549 | } |
| 534 | 550 | ||
| 535 | syms = elf_getdata(sec, NULL); | 551 | syms = elf_getdata(sec, NULL); |
| @@ -555,12 +571,17 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 555 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 571 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
| 556 | 572 | ||
| 557 | memset(&sym, 0, sizeof(sym)); | 573 | memset(&sym, 0, sizeof(sym)); |
| 558 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | 574 | if (!kernel) { |
| 575 | self->adjust_symbols = (ehdr.e_type == ET_EXEC || | ||
| 559 | elf_section_by_name(elf, &ehdr, &shdr, | 576 | elf_section_by_name(elf, &ehdr, &shdr, |
| 560 | ".gnu.prelink_undo", | 577 | ".gnu.prelink_undo", |
| 561 | NULL) != NULL); | 578 | NULL) != NULL); |
| 579 | } else self->adjust_symbols = 0; | ||
| 580 | |||
| 562 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { | 581 | elf_symtab__for_each_symbol(syms, nr_syms, index, sym) { |
| 563 | struct symbol *f; | 582 | struct symbol *f; |
| 583 | const char *name; | ||
| 584 | char *demangled; | ||
| 564 | u64 obj_start; | 585 | u64 obj_start; |
| 565 | struct section *section = NULL; | 586 | struct section *section = NULL; |
| 566 | int is_label = elf_sym__is_label(&sym); | 587 | int is_label = elf_sym__is_label(&sym); |
| @@ -599,10 +620,19 @@ static int dso__load_sym(struct dso *self, int fd, const char *name, | |||
| 599 | goto out_elf_end; | 620 | goto out_elf_end; |
| 600 | } | 621 | } |
| 601 | } | 622 | } |
| 623 | /* | ||
| 624 | * We need to figure out if the object was created from C++ sources | ||
| 625 | * DWARF DW_compile_unit has this, but we don't always have access | ||
| 626 | * to it... | ||
| 627 | */ | ||
| 628 | name = elf_sym__name(&sym, symstrs); | ||
| 629 | demangled = bfd_demangle(NULL, name, DMGL_PARAMS | DMGL_ANSI); | ||
| 630 | if (demangled != NULL) | ||
| 631 | name = demangled; | ||
| 602 | 632 | ||
| 603 | f = symbol__new(sym.st_value, sym.st_size, | 633 | f = symbol__new(sym.st_value, sym.st_size, name, |
| 604 | elf_sym__name(&sym, symstrs), | ||
| 605 | self->sym_priv_size, obj_start, verbose); | 634 | self->sym_priv_size, obj_start, verbose); |
| 635 | free(demangled); | ||
| 606 | if (!f) | 636 | if (!f) |
| 607 | goto out_elf_end; | 637 | goto out_elf_end; |
| 608 | 638 | ||
| @@ -668,6 +698,11 @@ more: | |||
| 668 | if (!ret) | 698 | if (!ret) |
| 669 | goto more; | 699 | goto more; |
| 670 | 700 | ||
| 701 | if (ret > 0) { | ||
| 702 | int nr_plt = dso__synthesize_plt_symbols(self, verbose); | ||
| 703 | if (nr_plt > 0) | ||
| 704 | ret += nr_plt; | ||
| 705 | } | ||
| 671 | out: | 706 | out: |
| 672 | free(name); | 707 | free(name); |
| 673 | return ret; | 708 | return ret; |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 7918cffb23cd..2f92b21c712d 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -25,6 +25,7 @@ struct dso { | |||
| 25 | struct symbol *(*find_symbol)(struct dso *, u64 ip); | 25 | struct symbol *(*find_symbol)(struct dso *, u64 ip); |
| 26 | unsigned int sym_priv_size; | 26 | unsigned int sym_priv_size; |
| 27 | unsigned char adjust_symbols; | 27 | unsigned char adjust_symbols; |
| 28 | unsigned char slen_calculated; | ||
| 28 | char name[0]; | 29 | char name[0]; |
| 29 | }; | 30 | }; |
| 30 | 31 | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index b4be6071c105..68fe157d72fb 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
| @@ -50,6 +50,7 @@ | |||
| 50 | #include <unistd.h> | 50 | #include <unistd.h> |
| 51 | #include <stdio.h> | 51 | #include <stdio.h> |
| 52 | #include <sys/stat.h> | 52 | #include <sys/stat.h> |
| 53 | #include <sys/statfs.h> | ||
| 53 | #include <fcntl.h> | 54 | #include <fcntl.h> |
| 54 | #include <stddef.h> | 55 | #include <stddef.h> |
| 55 | #include <stdlib.h> | 56 | #include <stdlib.h> |
| @@ -80,6 +81,7 @@ | |||
| 80 | #include <netdb.h> | 81 | #include <netdb.h> |
| 81 | #include <pwd.h> | 82 | #include <pwd.h> |
| 82 | #include <inttypes.h> | 83 | #include <inttypes.h> |
| 84 | #include "../../../include/linux/magic.h" | ||
| 83 | 85 | ||
| 84 | #ifndef NO_ICONV | 86 | #ifndef NO_ICONV |
| 85 | #include <iconv.h> | 87 | #include <iconv.h> |
