diff options
-rw-r--r-- | arch/x86/include/asm/perf_counter.h | 10 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/perf_counter.c | 325 | ||||
-rw-r--r-- | tools/perf/Documentation/perf-report.txt | 3 | ||||
-rw-r--r-- | tools/perf/Makefile | 4 | ||||
-rw-r--r-- | tools/perf/builtin-annotate.c | 209 | ||||
-rw-r--r-- | tools/perf/builtin-record.c | 20 | ||||
-rw-r--r-- | tools/perf/builtin-report.c | 308 | ||||
-rw-r--r-- | tools/perf/builtin-stat.c | 1 | ||||
-rw-r--r-- | tools/perf/builtin-top.c | 24 | ||||
-rw-r--r-- | tools/perf/builtin.h | 1 | ||||
-rw-r--r-- | tools/perf/perf.h | 1 | ||||
-rw-r--r-- | tools/perf/util/callchain.h | 1 | ||||
-rw-r--r-- | tools/perf/util/debug.c | 22 | ||||
-rw-r--r-- | tools/perf/util/debug.h | 5 | ||||
-rw-r--r-- | tools/perf/util/event.h | 90 | ||||
-rw-r--r-- | tools/perf/util/map.c | 97 | ||||
-rw-r--r-- | tools/perf/util/symbol.c | 97 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 12 | ||||
-rw-r--r-- | tools/perf/util/util.h | 1 | ||||
-rw-r--r-- | tools/perf/util/values.c | 231 | ||||
-rw-r--r-- | tools/perf/util/values.h | 27 |
21 files changed, 962 insertions, 527 deletions
diff --git a/arch/x86/include/asm/perf_counter.h b/arch/x86/include/asm/perf_counter.h index fa64e401589d..e7b7c938ae27 100644 --- a/arch/x86/include/asm/perf_counter.h +++ b/arch/x86/include/asm/perf_counter.h | |||
@@ -84,6 +84,16 @@ union cpuid10_edx { | |||
84 | #define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b | 84 | #define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b |
85 | #define X86_PMC_IDX_FIXED_BUS_CYCLES (X86_PMC_IDX_FIXED + 2) | 85 | #define X86_PMC_IDX_FIXED_BUS_CYCLES (X86_PMC_IDX_FIXED + 2) |
86 | 86 | ||
87 | /* | ||
88 | * We model BTS tracing as another fixed-mode PMC. | ||
89 | * | ||
90 | * We choose a value in the middle of the fixed counter range, since lower | ||
91 | * values are used by actual fixed counters and higher values are used | ||
92 | * to indicate other overflow conditions in the PERF_GLOBAL_STATUS msr. | ||
93 | */ | ||
94 | #define X86_PMC_IDX_FIXED_BTS (X86_PMC_IDX_FIXED + 16) | ||
95 | |||
96 | |||
87 | #ifdef CONFIG_PERF_COUNTERS | 97 | #ifdef CONFIG_PERF_COUNTERS |
88 | extern void init_hw_perf_counters(void); | 98 | extern void init_hw_perf_counters(void); |
89 | extern void perf_counters_lapic_init(void); | 99 | extern void perf_counters_lapic_init(void); |
diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c index 900332b800f8..396e35db7058 100644 --- a/arch/x86/kernel/cpu/perf_counter.c +++ b/arch/x86/kernel/cpu/perf_counter.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Copyright (C) 2009 Jaswinder Singh Rajput | 6 | * Copyright (C) 2009 Jaswinder Singh Rajput |
7 | * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter | 7 | * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter |
8 | * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com> | 8 | * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com> |
9 | * Copyright (C) 2009 Intel Corporation, <markus.t.metzger@intel.com> | ||
9 | * | 10 | * |
10 | * For licencing details see kernel-base/COPYING | 11 | * For licencing details see kernel-base/COPYING |
11 | */ | 12 | */ |
@@ -20,6 +21,7 @@ | |||
20 | #include <linux/sched.h> | 21 | #include <linux/sched.h> |
21 | #include <linux/uaccess.h> | 22 | #include <linux/uaccess.h> |
22 | #include <linux/highmem.h> | 23 | #include <linux/highmem.h> |
24 | #include <linux/cpu.h> | ||
23 | 25 | ||
24 | #include <asm/apic.h> | 26 | #include <asm/apic.h> |
25 | #include <asm/stacktrace.h> | 27 | #include <asm/stacktrace.h> |
@@ -27,12 +29,52 @@ | |||
27 | 29 | ||
28 | static u64 perf_counter_mask __read_mostly; | 30 | static u64 perf_counter_mask __read_mostly; |
29 | 31 | ||
32 | /* The maximal number of PEBS counters: */ | ||
33 | #define MAX_PEBS_COUNTERS 4 | ||
34 | |||
35 | /* The size of a BTS record in bytes: */ | ||
36 | #define BTS_RECORD_SIZE 24 | ||
37 | |||
38 | /* The size of a per-cpu BTS buffer in bytes: */ | ||
39 | #define BTS_BUFFER_SIZE (BTS_RECORD_SIZE * 1024) | ||
40 | |||
41 | /* The BTS overflow threshold in bytes from the end of the buffer: */ | ||
42 | #define BTS_OVFL_TH (BTS_RECORD_SIZE * 64) | ||
43 | |||
44 | |||
45 | /* | ||
46 | * Bits in the debugctlmsr controlling branch tracing. | ||
47 | */ | ||
48 | #define X86_DEBUGCTL_TR (1 << 6) | ||
49 | #define X86_DEBUGCTL_BTS (1 << 7) | ||
50 | #define X86_DEBUGCTL_BTINT (1 << 8) | ||
51 | #define X86_DEBUGCTL_BTS_OFF_OS (1 << 9) | ||
52 | #define X86_DEBUGCTL_BTS_OFF_USR (1 << 10) | ||
53 | |||
54 | /* | ||
55 | * A debug store configuration. | ||
56 | * | ||
57 | * We only support architectures that use 64bit fields. | ||
58 | */ | ||
59 | struct debug_store { | ||
60 | u64 bts_buffer_base; | ||
61 | u64 bts_index; | ||
62 | u64 bts_absolute_maximum; | ||
63 | u64 bts_interrupt_threshold; | ||
64 | u64 pebs_buffer_base; | ||
65 | u64 pebs_index; | ||
66 | u64 pebs_absolute_maximum; | ||
67 | u64 pebs_interrupt_threshold; | ||
68 | u64 pebs_counter_reset[MAX_PEBS_COUNTERS]; | ||
69 | }; | ||
70 | |||
30 | struct cpu_hw_counters { | 71 | struct cpu_hw_counters { |
31 | struct perf_counter *counters[X86_PMC_IDX_MAX]; | 72 | struct perf_counter *counters[X86_PMC_IDX_MAX]; |
32 | unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | 73 | unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; |
33 | unsigned long active_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; | 74 | unsigned long active_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; |
34 | unsigned long interrupts; | 75 | unsigned long interrupts; |
35 | int enabled; | 76 | int enabled; |
77 | struct debug_store *ds; | ||
36 | }; | 78 | }; |
37 | 79 | ||
38 | /* | 80 | /* |
@@ -58,6 +100,8 @@ struct x86_pmu { | |||
58 | int apic; | 100 | int apic; |
59 | u64 max_period; | 101 | u64 max_period; |
60 | u64 intel_ctrl; | 102 | u64 intel_ctrl; |
103 | void (*enable_bts)(u64 config); | ||
104 | void (*disable_bts)(void); | ||
61 | }; | 105 | }; |
62 | 106 | ||
63 | static struct x86_pmu x86_pmu __read_mostly; | 107 | static struct x86_pmu x86_pmu __read_mostly; |
@@ -577,6 +621,9 @@ x86_perf_counter_update(struct perf_counter *counter, | |||
577 | u64 prev_raw_count, new_raw_count; | 621 | u64 prev_raw_count, new_raw_count; |
578 | s64 delta; | 622 | s64 delta; |
579 | 623 | ||
624 | if (idx == X86_PMC_IDX_FIXED_BTS) | ||
625 | return 0; | ||
626 | |||
580 | /* | 627 | /* |
581 | * Careful: an NMI might modify the previous counter value. | 628 | * Careful: an NMI might modify the previous counter value. |
582 | * | 629 | * |
@@ -666,10 +713,109 @@ static void release_pmc_hardware(void) | |||
666 | #endif | 713 | #endif |
667 | } | 714 | } |
668 | 715 | ||
716 | static inline bool bts_available(void) | ||
717 | { | ||
718 | return x86_pmu.enable_bts != NULL; | ||
719 | } | ||
720 | |||
721 | static inline void init_debug_store_on_cpu(int cpu) | ||
722 | { | ||
723 | struct debug_store *ds = per_cpu(cpu_hw_counters, cpu).ds; | ||
724 | |||
725 | if (!ds) | ||
726 | return; | ||
727 | |||
728 | wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, | ||
729 | (u32)((u64)(long)ds), (u32)((u64)(long)ds >> 32)); | ||
730 | } | ||
731 | |||
732 | static inline void fini_debug_store_on_cpu(int cpu) | ||
733 | { | ||
734 | if (!per_cpu(cpu_hw_counters, cpu).ds) | ||
735 | return; | ||
736 | |||
737 | wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, 0, 0); | ||
738 | } | ||
739 | |||
740 | static void release_bts_hardware(void) | ||
741 | { | ||
742 | int cpu; | ||
743 | |||
744 | if (!bts_available()) | ||
745 | return; | ||
746 | |||
747 | get_online_cpus(); | ||
748 | |||
749 | for_each_online_cpu(cpu) | ||
750 | fini_debug_store_on_cpu(cpu); | ||
751 | |||
752 | for_each_possible_cpu(cpu) { | ||
753 | struct debug_store *ds = per_cpu(cpu_hw_counters, cpu).ds; | ||
754 | |||
755 | if (!ds) | ||
756 | continue; | ||
757 | |||
758 | per_cpu(cpu_hw_counters, cpu).ds = NULL; | ||
759 | |||
760 | kfree((void *)(long)ds->bts_buffer_base); | ||
761 | kfree(ds); | ||
762 | } | ||
763 | |||
764 | put_online_cpus(); | ||
765 | } | ||
766 | |||
767 | static int reserve_bts_hardware(void) | ||
768 | { | ||
769 | int cpu, err = 0; | ||
770 | |||
771 | if (!bts_available()) | ||
772 | return -EOPNOTSUPP; | ||
773 | |||
774 | get_online_cpus(); | ||
775 | |||
776 | for_each_possible_cpu(cpu) { | ||
777 | struct debug_store *ds; | ||
778 | void *buffer; | ||
779 | |||
780 | err = -ENOMEM; | ||
781 | buffer = kzalloc(BTS_BUFFER_SIZE, GFP_KERNEL); | ||
782 | if (unlikely(!buffer)) | ||
783 | break; | ||
784 | |||
785 | ds = kzalloc(sizeof(*ds), GFP_KERNEL); | ||
786 | if (unlikely(!ds)) { | ||
787 | kfree(buffer); | ||
788 | break; | ||
789 | } | ||
790 | |||
791 | ds->bts_buffer_base = (u64)(long)buffer; | ||
792 | ds->bts_index = ds->bts_buffer_base; | ||
793 | ds->bts_absolute_maximum = | ||
794 | ds->bts_buffer_base + BTS_BUFFER_SIZE; | ||
795 | ds->bts_interrupt_threshold = | ||
796 | ds->bts_absolute_maximum - BTS_OVFL_TH; | ||
797 | |||
798 | per_cpu(cpu_hw_counters, cpu).ds = ds; | ||
799 | err = 0; | ||
800 | } | ||
801 | |||
802 | if (err) | ||
803 | release_bts_hardware(); | ||
804 | else { | ||
805 | for_each_online_cpu(cpu) | ||
806 | init_debug_store_on_cpu(cpu); | ||
807 | } | ||
808 | |||
809 | put_online_cpus(); | ||
810 | |||
811 | return err; | ||
812 | } | ||
813 | |||
669 | static void hw_perf_counter_destroy(struct perf_counter *counter) | 814 | static void hw_perf_counter_destroy(struct perf_counter *counter) |
670 | { | 815 | { |
671 | if (atomic_dec_and_mutex_lock(&active_counters, &pmc_reserve_mutex)) { | 816 | if (atomic_dec_and_mutex_lock(&active_counters, &pmc_reserve_mutex)) { |
672 | release_pmc_hardware(); | 817 | release_pmc_hardware(); |
818 | release_bts_hardware(); | ||
673 | mutex_unlock(&pmc_reserve_mutex); | 819 | mutex_unlock(&pmc_reserve_mutex); |
674 | } | 820 | } |
675 | } | 821 | } |
@@ -712,6 +858,42 @@ set_ext_hw_attr(struct hw_perf_counter *hwc, struct perf_counter_attr *attr) | |||
712 | return 0; | 858 | return 0; |
713 | } | 859 | } |
714 | 860 | ||
861 | static void intel_pmu_enable_bts(u64 config) | ||
862 | { | ||
863 | unsigned long debugctlmsr; | ||
864 | |||
865 | debugctlmsr = get_debugctlmsr(); | ||
866 | |||
867 | debugctlmsr |= X86_DEBUGCTL_TR; | ||
868 | debugctlmsr |= X86_DEBUGCTL_BTS; | ||
869 | debugctlmsr |= X86_DEBUGCTL_BTINT; | ||
870 | |||
871 | if (!(config & ARCH_PERFMON_EVENTSEL_OS)) | ||
872 | debugctlmsr |= X86_DEBUGCTL_BTS_OFF_OS; | ||
873 | |||
874 | if (!(config & ARCH_PERFMON_EVENTSEL_USR)) | ||
875 | debugctlmsr |= X86_DEBUGCTL_BTS_OFF_USR; | ||
876 | |||
877 | update_debugctlmsr(debugctlmsr); | ||
878 | } | ||
879 | |||
880 | static void intel_pmu_disable_bts(void) | ||
881 | { | ||
882 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | ||
883 | unsigned long debugctlmsr; | ||
884 | |||
885 | if (!cpuc->ds) | ||
886 | return; | ||
887 | |||
888 | debugctlmsr = get_debugctlmsr(); | ||
889 | |||
890 | debugctlmsr &= | ||
891 | ~(X86_DEBUGCTL_TR | X86_DEBUGCTL_BTS | X86_DEBUGCTL_BTINT | | ||
892 | X86_DEBUGCTL_BTS_OFF_OS | X86_DEBUGCTL_BTS_OFF_USR); | ||
893 | |||
894 | update_debugctlmsr(debugctlmsr); | ||
895 | } | ||
896 | |||
715 | /* | 897 | /* |
716 | * Setup the hardware configuration for a given attr_type | 898 | * Setup the hardware configuration for a given attr_type |
717 | */ | 899 | */ |
@@ -728,9 +910,13 @@ static int __hw_perf_counter_init(struct perf_counter *counter) | |||
728 | err = 0; | 910 | err = 0; |
729 | if (!atomic_inc_not_zero(&active_counters)) { | 911 | if (!atomic_inc_not_zero(&active_counters)) { |
730 | mutex_lock(&pmc_reserve_mutex); | 912 | mutex_lock(&pmc_reserve_mutex); |
731 | if (atomic_read(&active_counters) == 0 && !reserve_pmc_hardware()) | 913 | if (atomic_read(&active_counters) == 0) { |
732 | err = -EBUSY; | 914 | if (!reserve_pmc_hardware()) |
733 | else | 915 | err = -EBUSY; |
916 | else | ||
917 | reserve_bts_hardware(); | ||
918 | } | ||
919 | if (!err) | ||
734 | atomic_inc(&active_counters); | 920 | atomic_inc(&active_counters); |
735 | mutex_unlock(&pmc_reserve_mutex); | 921 | mutex_unlock(&pmc_reserve_mutex); |
736 | } | 922 | } |
@@ -817,7 +1003,18 @@ static void p6_pmu_disable_all(void) | |||
817 | 1003 | ||
818 | static void intel_pmu_disable_all(void) | 1004 | static void intel_pmu_disable_all(void) |
819 | { | 1005 | { |
1006 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | ||
1007 | |||
1008 | if (!cpuc->enabled) | ||
1009 | return; | ||
1010 | |||
1011 | cpuc->enabled = 0; | ||
1012 | barrier(); | ||
1013 | |||
820 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); | 1014 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); |
1015 | |||
1016 | if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) | ||
1017 | intel_pmu_disable_bts(); | ||
821 | } | 1018 | } |
822 | 1019 | ||
823 | static void amd_pmu_disable_all(void) | 1020 | static void amd_pmu_disable_all(void) |
@@ -875,7 +1072,25 @@ static void p6_pmu_enable_all(void) | |||
875 | 1072 | ||
876 | static void intel_pmu_enable_all(void) | 1073 | static void intel_pmu_enable_all(void) |
877 | { | 1074 | { |
1075 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | ||
1076 | |||
1077 | if (cpuc->enabled) | ||
1078 | return; | ||
1079 | |||
1080 | cpuc->enabled = 1; | ||
1081 | barrier(); | ||
1082 | |||
878 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); | 1083 | wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); |
1084 | |||
1085 | if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { | ||
1086 | struct perf_counter *counter = | ||
1087 | cpuc->counters[X86_PMC_IDX_FIXED_BTS]; | ||
1088 | |||
1089 | if (WARN_ON_ONCE(!counter)) | ||
1090 | return; | ||
1091 | |||
1092 | intel_pmu_enable_bts(counter->hw.config); | ||
1093 | } | ||
879 | } | 1094 | } |
880 | 1095 | ||
881 | static void amd_pmu_enable_all(void) | 1096 | static void amd_pmu_enable_all(void) |
@@ -962,6 +1177,11 @@ p6_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) | |||
962 | static inline void | 1177 | static inline void |
963 | intel_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) | 1178 | intel_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) |
964 | { | 1179 | { |
1180 | if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) { | ||
1181 | intel_pmu_disable_bts(); | ||
1182 | return; | ||
1183 | } | ||
1184 | |||
965 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { | 1185 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { |
966 | intel_pmu_disable_fixed(hwc, idx); | 1186 | intel_pmu_disable_fixed(hwc, idx); |
967 | return; | 1187 | return; |
@@ -990,6 +1210,9 @@ x86_perf_counter_set_period(struct perf_counter *counter, | |||
990 | s64 period = hwc->sample_period; | 1210 | s64 period = hwc->sample_period; |
991 | int err, ret = 0; | 1211 | int err, ret = 0; |
992 | 1212 | ||
1213 | if (idx == X86_PMC_IDX_FIXED_BTS) | ||
1214 | return 0; | ||
1215 | |||
993 | /* | 1216 | /* |
994 | * If we are way outside a reasoable range then just skip forward: | 1217 | * If we are way outside a reasoable range then just skip forward: |
995 | */ | 1218 | */ |
@@ -1072,6 +1295,14 @@ static void p6_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) | |||
1072 | 1295 | ||
1073 | static void intel_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) | 1296 | static void intel_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) |
1074 | { | 1297 | { |
1298 | if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) { | ||
1299 | if (!__get_cpu_var(cpu_hw_counters).enabled) | ||
1300 | return; | ||
1301 | |||
1302 | intel_pmu_enable_bts(hwc->config); | ||
1303 | return; | ||
1304 | } | ||
1305 | |||
1075 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { | 1306 | if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { |
1076 | intel_pmu_enable_fixed(hwc, idx); | 1307 | intel_pmu_enable_fixed(hwc, idx); |
1077 | return; | 1308 | return; |
@@ -1093,11 +1324,16 @@ fixed_mode_idx(struct perf_counter *counter, struct hw_perf_counter *hwc) | |||
1093 | { | 1324 | { |
1094 | unsigned int event; | 1325 | unsigned int event; |
1095 | 1326 | ||
1327 | event = hwc->config & ARCH_PERFMON_EVENT_MASK; | ||
1328 | |||
1329 | if (unlikely((event == | ||
1330 | x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS)) && | ||
1331 | (hwc->sample_period == 1))) | ||
1332 | return X86_PMC_IDX_FIXED_BTS; | ||
1333 | |||
1096 | if (!x86_pmu.num_counters_fixed) | 1334 | if (!x86_pmu.num_counters_fixed) |
1097 | return -1; | 1335 | return -1; |
1098 | 1336 | ||
1099 | event = hwc->config & ARCH_PERFMON_EVENT_MASK; | ||
1100 | |||
1101 | if (unlikely(event == x86_pmu.event_map(PERF_COUNT_HW_INSTRUCTIONS))) | 1337 | if (unlikely(event == x86_pmu.event_map(PERF_COUNT_HW_INSTRUCTIONS))) |
1102 | return X86_PMC_IDX_FIXED_INSTRUCTIONS; | 1338 | return X86_PMC_IDX_FIXED_INSTRUCTIONS; |
1103 | if (unlikely(event == x86_pmu.event_map(PERF_COUNT_HW_CPU_CYCLES))) | 1339 | if (unlikely(event == x86_pmu.event_map(PERF_COUNT_HW_CPU_CYCLES))) |
@@ -1118,7 +1354,25 @@ static int x86_pmu_enable(struct perf_counter *counter) | |||
1118 | int idx; | 1354 | int idx; |
1119 | 1355 | ||
1120 | idx = fixed_mode_idx(counter, hwc); | 1356 | idx = fixed_mode_idx(counter, hwc); |
1121 | if (idx >= 0) { | 1357 | if (idx == X86_PMC_IDX_FIXED_BTS) { |
1358 | /* | ||
1359 | * Try to use BTS for branch tracing. If that is not | ||
1360 | * available, try to get a generic counter. | ||
1361 | */ | ||
1362 | if (unlikely(!cpuc->ds)) | ||
1363 | goto try_generic; | ||
1364 | |||
1365 | /* | ||
1366 | * Try to get the fixed counter, if that is already taken | ||
1367 | * then try to get a generic counter: | ||
1368 | */ | ||
1369 | if (test_and_set_bit(idx, cpuc->used_mask)) | ||
1370 | goto try_generic; | ||
1371 | |||
1372 | hwc->config_base = 0; | ||
1373 | hwc->counter_base = 0; | ||
1374 | hwc->idx = idx; | ||
1375 | } else if (idx >= 0) { | ||
1122 | /* | 1376 | /* |
1123 | * Try to get the fixed counter, if that is already taken | 1377 | * Try to get the fixed counter, if that is already taken |
1124 | * then try to get a generic counter: | 1378 | * then try to get a generic counter: |
@@ -1229,6 +1483,45 @@ void perf_counter_print_debug(void) | |||
1229 | local_irq_restore(flags); | 1483 | local_irq_restore(flags); |
1230 | } | 1484 | } |
1231 | 1485 | ||
1486 | static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc, | ||
1487 | struct perf_sample_data *data) | ||
1488 | { | ||
1489 | struct debug_store *ds = cpuc->ds; | ||
1490 | struct bts_record { | ||
1491 | u64 from; | ||
1492 | u64 to; | ||
1493 | u64 flags; | ||
1494 | }; | ||
1495 | struct perf_counter *counter = cpuc->counters[X86_PMC_IDX_FIXED_BTS]; | ||
1496 | unsigned long orig_ip = data->regs->ip; | ||
1497 | u64 at; | ||
1498 | |||
1499 | if (!counter) | ||
1500 | return; | ||
1501 | |||
1502 | if (!ds) | ||
1503 | return; | ||
1504 | |||
1505 | for (at = ds->bts_buffer_base; | ||
1506 | at < ds->bts_index; | ||
1507 | at += sizeof(struct bts_record)) { | ||
1508 | struct bts_record *rec = (struct bts_record *)(long)at; | ||
1509 | |||
1510 | data->regs->ip = rec->from; | ||
1511 | data->addr = rec->to; | ||
1512 | |||
1513 | perf_counter_output(counter, 1, data); | ||
1514 | } | ||
1515 | |||
1516 | ds->bts_index = ds->bts_buffer_base; | ||
1517 | |||
1518 | data->regs->ip = orig_ip; | ||
1519 | data->addr = 0; | ||
1520 | |||
1521 | /* There's new data available. */ | ||
1522 | counter->pending_kill = POLL_IN; | ||
1523 | } | ||
1524 | |||
1232 | static void x86_pmu_disable(struct perf_counter *counter) | 1525 | static void x86_pmu_disable(struct perf_counter *counter) |
1233 | { | 1526 | { |
1234 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); | 1527 | struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); |
@@ -1253,6 +1546,15 @@ static void x86_pmu_disable(struct perf_counter *counter) | |||
1253 | * that we are disabling: | 1546 | * that we are disabling: |
1254 | */ | 1547 | */ |
1255 | x86_perf_counter_update(counter, hwc, idx); | 1548 | x86_perf_counter_update(counter, hwc, idx); |
1549 | |||
1550 | /* Drain the remaining BTS records. */ | ||
1551 | if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) { | ||
1552 | struct perf_sample_data data; | ||
1553 | struct pt_regs regs; | ||
1554 | |||
1555 | data.regs = ®s; | ||
1556 | intel_pmu_drain_bts_buffer(cpuc, &data); | ||
1557 | } | ||
1256 | cpuc->counters[idx] = NULL; | 1558 | cpuc->counters[idx] = NULL; |
1257 | clear_bit(idx, cpuc->used_mask); | 1559 | clear_bit(idx, cpuc->used_mask); |
1258 | 1560 | ||
@@ -1280,6 +1582,7 @@ static int intel_pmu_save_and_restart(struct perf_counter *counter) | |||
1280 | 1582 | ||
1281 | static void intel_pmu_reset(void) | 1583 | static void intel_pmu_reset(void) |
1282 | { | 1584 | { |
1585 | struct debug_store *ds = __get_cpu_var(cpu_hw_counters).ds; | ||
1283 | unsigned long flags; | 1586 | unsigned long flags; |
1284 | int idx; | 1587 | int idx; |
1285 | 1588 | ||
@@ -1297,6 +1600,8 @@ static void intel_pmu_reset(void) | |||
1297 | for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) { | 1600 | for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) { |
1298 | checking_wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull); | 1601 | checking_wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull); |
1299 | } | 1602 | } |
1603 | if (ds) | ||
1604 | ds->bts_index = ds->bts_buffer_base; | ||
1300 | 1605 | ||
1301 | local_irq_restore(flags); | 1606 | local_irq_restore(flags); |
1302 | } | 1607 | } |
@@ -1362,6 +1667,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) | |||
1362 | cpuc = &__get_cpu_var(cpu_hw_counters); | 1667 | cpuc = &__get_cpu_var(cpu_hw_counters); |
1363 | 1668 | ||
1364 | perf_disable(); | 1669 | perf_disable(); |
1670 | intel_pmu_drain_bts_buffer(cpuc, &data); | ||
1365 | status = intel_pmu_get_status(); | 1671 | status = intel_pmu_get_status(); |
1366 | if (!status) { | 1672 | if (!status) { |
1367 | perf_enable(); | 1673 | perf_enable(); |
@@ -1571,6 +1877,8 @@ static struct x86_pmu intel_pmu = { | |||
1571 | * the generic counter period: | 1877 | * the generic counter period: |
1572 | */ | 1878 | */ |
1573 | .max_period = (1ULL << 31) - 1, | 1879 | .max_period = (1ULL << 31) - 1, |
1880 | .enable_bts = intel_pmu_enable_bts, | ||
1881 | .disable_bts = intel_pmu_disable_bts, | ||
1574 | }; | 1882 | }; |
1575 | 1883 | ||
1576 | static struct x86_pmu amd_pmu = { | 1884 | static struct x86_pmu amd_pmu = { |
@@ -1962,3 +2270,8 @@ struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) | |||
1962 | 2270 | ||
1963 | return entry; | 2271 | return entry; |
1964 | } | 2272 | } |
2273 | |||
2274 | void hw_perf_counter_setup_online(int cpu) | ||
2275 | { | ||
2276 | init_debug_store_on_cpu(cpu); | ||
2277 | } | ||
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index e72e93110782..370344afb5b2 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -27,6 +27,9 @@ OPTIONS | |||
27 | -n | 27 | -n |
28 | --show-nr-samples | 28 | --show-nr-samples |
29 | Show the number of samples for each symbol | 29 | Show the number of samples for each symbol |
30 | -T | ||
31 | --threads | ||
32 | Show per-thread event counters | ||
30 | -C:: | 33 | -C:: |
31 | --comms=:: | 34 | --comms=:: |
32 | Only consider symbols in these comms. CSV that understands | 35 | Only consider symbols in these comms. CSV that understands |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index c045b4271e57..68218cfd38b3 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -310,6 +310,7 @@ LIB_H += util/sigchain.h | |||
310 | LIB_H += util/symbol.h | 310 | LIB_H += util/symbol.h |
311 | LIB_H += util/module.h | 311 | LIB_H += util/module.h |
312 | LIB_H += util/color.h | 312 | LIB_H += util/color.h |
313 | LIB_H += util/values.h | ||
313 | 314 | ||
314 | LIB_OBJS += util/abspath.o | 315 | LIB_OBJS += util/abspath.o |
315 | LIB_OBJS += util/alias.o | 316 | LIB_OBJS += util/alias.o |
@@ -337,6 +338,9 @@ LIB_OBJS += util/color.o | |||
337 | LIB_OBJS += util/pager.o | 338 | LIB_OBJS += util/pager.o |
338 | LIB_OBJS += util/header.o | 339 | LIB_OBJS += util/header.o |
339 | LIB_OBJS += util/callchain.o | 340 | LIB_OBJS += util/callchain.o |
341 | LIB_OBJS += util/values.o | ||
342 | LIB_OBJS += util/debug.o | ||
343 | LIB_OBJS += util/map.o | ||
340 | 344 | ||
341 | BUILTIN_OBJS += builtin-annotate.o | 345 | BUILTIN_OBJS += builtin-annotate.o |
342 | BUILTIN_OBJS += builtin-help.o | 346 | BUILTIN_OBJS += builtin-help.o |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 1dba568e1941..543c4524f8c2 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -26,7 +26,6 @@ | |||
26 | #define SHOW_HV 4 | 26 | #define SHOW_HV 4 |
27 | 27 | ||
28 | static char const *input_name = "perf.data"; | 28 | static char const *input_name = "perf.data"; |
29 | static char *vmlinux = "vmlinux"; | ||
30 | 29 | ||
31 | static char default_sort_order[] = "comm,symbol"; | 30 | static char default_sort_order[] = "comm,symbol"; |
32 | static char *sort_order = default_sort_order; | 31 | static char *sort_order = default_sort_order; |
@@ -37,9 +36,6 @@ static int show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV; | |||
37 | static int dump_trace = 0; | 36 | static int dump_trace = 0; |
38 | #define dprintf(x...) do { if (dump_trace) printf(x); } while (0) | 37 | #define dprintf(x...) do { if (dump_trace) printf(x); } while (0) |
39 | 38 | ||
40 | static int verbose; | ||
41 | |||
42 | static int modules; | ||
43 | 39 | ||
44 | static int full_paths; | 40 | static int full_paths; |
45 | 41 | ||
@@ -48,40 +44,6 @@ static int print_line; | |||
48 | static unsigned long page_size; | 44 | static unsigned long page_size; |
49 | static unsigned long mmap_window = 32; | 45 | static unsigned long mmap_window = 32; |
50 | 46 | ||
51 | struct ip_event { | ||
52 | struct perf_event_header header; | ||
53 | u64 ip; | ||
54 | u32 pid, tid; | ||
55 | }; | ||
56 | |||
57 | struct mmap_event { | ||
58 | struct perf_event_header header; | ||
59 | u32 pid, tid; | ||
60 | u64 start; | ||
61 | u64 len; | ||
62 | u64 pgoff; | ||
63 | char filename[PATH_MAX]; | ||
64 | }; | ||
65 | |||
66 | struct comm_event { | ||
67 | struct perf_event_header header; | ||
68 | u32 pid, tid; | ||
69 | char comm[16]; | ||
70 | }; | ||
71 | |||
72 | struct fork_event { | ||
73 | struct perf_event_header header; | ||
74 | u32 pid, ppid; | ||
75 | }; | ||
76 | |||
77 | typedef union event_union { | ||
78 | struct perf_event_header header; | ||
79 | struct ip_event ip; | ||
80 | struct mmap_event mmap; | ||
81 | struct comm_event comm; | ||
82 | struct fork_event fork; | ||
83 | } event_t; | ||
84 | |||
85 | 47 | ||
86 | struct sym_ext { | 48 | struct sym_ext { |
87 | struct rb_node node; | 49 | struct rb_node node; |
@@ -89,175 +51,6 @@ struct sym_ext { | |||
89 | char *path; | 51 | char *path; |
90 | }; | 52 | }; |
91 | 53 | ||
92 | static LIST_HEAD(dsos); | ||
93 | static struct dso *kernel_dso; | ||
94 | static struct dso *vdso; | ||
95 | |||
96 | |||
97 | static void dsos__add(struct dso *dso) | ||
98 | { | ||
99 | list_add_tail(&dso->node, &dsos); | ||
100 | } | ||
101 | |||
102 | static struct dso *dsos__find(const char *name) | ||
103 | { | ||
104 | struct dso *pos; | ||
105 | |||
106 | list_for_each_entry(pos, &dsos, node) | ||
107 | if (strcmp(pos->name, name) == 0) | ||
108 | return pos; | ||
109 | return NULL; | ||
110 | } | ||
111 | |||
112 | static struct dso *dsos__findnew(const char *name) | ||
113 | { | ||
114 | struct dso *dso = dsos__find(name); | ||
115 | int nr; | ||
116 | |||
117 | if (dso) | ||
118 | return dso; | ||
119 | |||
120 | dso = dso__new(name, 0); | ||
121 | if (!dso) | ||
122 | goto out_delete_dso; | ||
123 | |||
124 | nr = dso__load(dso, NULL, verbose); | ||
125 | if (nr < 0) { | ||
126 | if (verbose) | ||
127 | fprintf(stderr, "Failed to open: %s\n", name); | ||
128 | goto out_delete_dso; | ||
129 | } | ||
130 | if (!nr && verbose) { | ||
131 | fprintf(stderr, | ||
132 | "No symbols found in: %s, maybe install a debug package?\n", | ||
133 | name); | ||
134 | } | ||
135 | |||
136 | dsos__add(dso); | ||
137 | |||
138 | return dso; | ||
139 | |||
140 | out_delete_dso: | ||
141 | dso__delete(dso); | ||
142 | return NULL; | ||
143 | } | ||
144 | |||
145 | static void dsos__fprintf(FILE *fp) | ||
146 | { | ||
147 | struct dso *pos; | ||
148 | |||
149 | list_for_each_entry(pos, &dsos, node) | ||
150 | dso__fprintf(pos, fp); | ||
151 | } | ||
152 | |||
153 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) | ||
154 | { | ||
155 | return dso__find_symbol(dso, ip); | ||
156 | } | ||
157 | |||
158 | static int load_kernel(void) | ||
159 | { | ||
160 | int err; | ||
161 | |||
162 | kernel_dso = dso__new("[kernel]", 0); | ||
163 | if (!kernel_dso) | ||
164 | return -1; | ||
165 | |||
166 | err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules); | ||
167 | if (err <= 0) { | ||
168 | dso__delete(kernel_dso); | ||
169 | kernel_dso = NULL; | ||
170 | } else | ||
171 | dsos__add(kernel_dso); | ||
172 | |||
173 | vdso = dso__new("[vdso]", 0); | ||
174 | if (!vdso) | ||
175 | return -1; | ||
176 | |||
177 | vdso->find_symbol = vdso__find_symbol; | ||
178 | |||
179 | dsos__add(vdso); | ||
180 | |||
181 | return err; | ||
182 | } | ||
183 | |||
184 | struct map { | ||
185 | struct list_head node; | ||
186 | u64 start; | ||
187 | u64 end; | ||
188 | u64 pgoff; | ||
189 | u64 (*map_ip)(struct map *, u64); | ||
190 | struct dso *dso; | ||
191 | }; | ||
192 | |||
193 | static u64 map__map_ip(struct map *map, u64 ip) | ||
194 | { | ||
195 | return ip - map->start + map->pgoff; | ||
196 | } | ||
197 | |||
198 | static u64 vdso__map_ip(struct map *map __used, u64 ip) | ||
199 | { | ||
200 | return ip; | ||
201 | } | ||
202 | |||
203 | static struct map *map__new(struct mmap_event *event) | ||
204 | { | ||
205 | struct map *self = malloc(sizeof(*self)); | ||
206 | |||
207 | if (self != NULL) { | ||
208 | const char *filename = event->filename; | ||
209 | |||
210 | self->start = event->start; | ||
211 | self->end = event->start + event->len; | ||
212 | self->pgoff = event->pgoff; | ||
213 | |||
214 | self->dso = dsos__findnew(filename); | ||
215 | if (self->dso == NULL) | ||
216 | goto out_delete; | ||
217 | |||
218 | if (self->dso == vdso) | ||
219 | self->map_ip = vdso__map_ip; | ||
220 | else | ||
221 | self->map_ip = map__map_ip; | ||
222 | } | ||
223 | return self; | ||
224 | out_delete: | ||
225 | free(self); | ||
226 | return NULL; | ||
227 | } | ||
228 | |||
229 | static struct map *map__clone(struct map *self) | ||
230 | { | ||
231 | struct map *map = malloc(sizeof(*self)); | ||
232 | |||
233 | if (!map) | ||
234 | return NULL; | ||
235 | |||
236 | memcpy(map, self, sizeof(*self)); | ||
237 | |||
238 | return map; | ||
239 | } | ||
240 | |||
241 | static int map__overlap(struct map *l, struct map *r) | ||
242 | { | ||
243 | if (l->start > r->start) { | ||
244 | struct map *t = l; | ||
245 | l = r; | ||
246 | r = t; | ||
247 | } | ||
248 | |||
249 | if (l->end > r->start) | ||
250 | return 1; | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static size_t map__fprintf(struct map *self, FILE *fp) | ||
256 | { | ||
257 | return fprintf(fp, " %Lx-%Lx %Lx %s\n", | ||
258 | self->start, self->end, self->pgoff, self->dso->name); | ||
259 | } | ||
260 | |||
261 | 54 | ||
262 | struct thread { | 55 | struct thread { |
263 | struct rb_node rb_node; | 56 | struct rb_node rb_node; |
@@ -927,7 +720,7 @@ static int | |||
927 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | 720 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) |
928 | { | 721 | { |
929 | struct thread *thread = threads__findnew(event->mmap.pid); | 722 | struct thread *thread = threads__findnew(event->mmap.pid); |
930 | struct map *map = map__new(&event->mmap); | 723 | struct map *map = map__new(&event->mmap, NULL, 0); |
931 | 724 | ||
932 | dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n", | 725 | dprintf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n", |
933 | (void *)(offset + head), | 726 | (void *)(offset + head), |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 89a5ddcd1ded..65b4115e417d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include "util/string.h" | 15 | #include "util/string.h" |
16 | 16 | ||
17 | #include "util/header.h" | 17 | #include "util/header.h" |
18 | #include "util/event.h" | ||
18 | 19 | ||
19 | #include <unistd.h> | 20 | #include <unistd.h> |
20 | #include <sched.h> | 21 | #include <sched.h> |
@@ -42,7 +43,6 @@ static int inherit = 1; | |||
42 | static int force = 0; | 43 | static int force = 0; |
43 | static int append_file = 0; | 44 | static int append_file = 0; |
44 | static int call_graph = 0; | 45 | static int call_graph = 0; |
45 | static int verbose = 0; | ||
46 | static int inherit_stat = 0; | 46 | static int inherit_stat = 0; |
47 | static int no_samples = 0; | 47 | static int no_samples = 0; |
48 | static int sample_address = 0; | 48 | static int sample_address = 0; |
@@ -62,24 +62,6 @@ static int file_new = 1; | |||
62 | 62 | ||
63 | struct perf_header *header; | 63 | struct perf_header *header; |
64 | 64 | ||
65 | struct mmap_event { | ||
66 | struct perf_event_header header; | ||
67 | u32 pid; | ||
68 | u32 tid; | ||
69 | u64 start; | ||
70 | u64 len; | ||
71 | u64 pgoff; | ||
72 | char filename[PATH_MAX]; | ||
73 | }; | ||
74 | |||
75 | struct comm_event { | ||
76 | struct perf_event_header header; | ||
77 | u32 pid; | ||
78 | u32 tid; | ||
79 | char comm[16]; | ||
80 | }; | ||
81 | |||
82 | |||
83 | struct mmap_data { | 65 | struct mmap_data { |
84 | int counter; | 66 | int counter; |
85 | void *base; | 67 | void *base; |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index b53a60fc12de..6321951fe1bf 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "util/string.h" | 17 | #include "util/string.h" |
18 | #include "util/callchain.h" | 18 | #include "util/callchain.h" |
19 | #include "util/strlist.h" | 19 | #include "util/strlist.h" |
20 | #include "util/values.h" | ||
20 | 21 | ||
21 | #include "perf.h" | 22 | #include "perf.h" |
22 | #include "util/header.h" | 23 | #include "util/header.h" |
@@ -29,7 +30,6 @@ | |||
29 | #define SHOW_HV 4 | 30 | #define SHOW_HV 4 |
30 | 31 | ||
31 | static char const *input_name = "perf.data"; | 32 | static char const *input_name = "perf.data"; |
32 | static char *vmlinux = NULL; | ||
33 | 33 | ||
34 | static char default_sort_order[] = "comm,dso,symbol"; | 34 | static char default_sort_order[] = "comm,dso,symbol"; |
35 | static char *sort_order = default_sort_order; | 35 | static char *sort_order = default_sort_order; |
@@ -45,14 +45,15 @@ static int dump_trace = 0; | |||
45 | #define dprintf(x...) do { if (dump_trace) printf(x); } while (0) | 45 | #define dprintf(x...) do { if (dump_trace) printf(x); } while (0) |
46 | #define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0) | 46 | #define cdprintf(x...) do { if (dump_trace) color_fprintf(stdout, color, x); } while (0) |
47 | 47 | ||
48 | static int verbose; | ||
49 | #define eprintf(x...) do { if (verbose) fprintf(stderr, x); } while (0) | ||
50 | |||
51 | static int modules; | ||
52 | |||
53 | static int full_paths; | 48 | static int full_paths; |
54 | static int show_nr_samples; | 49 | static int show_nr_samples; |
55 | 50 | ||
51 | static int show_threads; | ||
52 | static struct perf_read_values show_threads_values; | ||
53 | |||
54 | static char default_pretty_printing_style[] = "normal"; | ||
55 | static char *pretty_printing_style = default_pretty_printing_style; | ||
56 | |||
56 | static unsigned long page_size; | 57 | static unsigned long page_size; |
57 | static unsigned long mmap_window = 32; | 58 | static unsigned long mmap_window = 32; |
58 | 59 | ||
@@ -66,6 +67,10 @@ static char callchain_default_opt[] = "fractal,0.5"; | |||
66 | 67 | ||
67 | static int callchain; | 68 | static int callchain; |
68 | 69 | ||
70 | static char __cwd[PATH_MAX]; | ||
71 | static char *cwd = __cwd; | ||
72 | static int cwdlen; | ||
73 | |||
69 | static | 74 | static |
70 | struct callchain_param callchain_param = { | 75 | struct callchain_param callchain_param = { |
71 | .mode = CHAIN_GRAPH_REL, | 76 | .mode = CHAIN_GRAPH_REL, |
@@ -74,59 +79,6 @@ struct callchain_param callchain_param = { | |||
74 | 79 | ||
75 | static u64 sample_type; | 80 | static u64 sample_type; |
76 | 81 | ||
77 | struct ip_event { | ||
78 | struct perf_event_header header; | ||
79 | u64 ip; | ||
80 | u32 pid, tid; | ||
81 | unsigned char __more_data[]; | ||
82 | }; | ||
83 | |||
84 | struct mmap_event { | ||
85 | struct perf_event_header header; | ||
86 | u32 pid, tid; | ||
87 | u64 start; | ||
88 | u64 len; | ||
89 | u64 pgoff; | ||
90 | char filename[PATH_MAX]; | ||
91 | }; | ||
92 | |||
93 | struct comm_event { | ||
94 | struct perf_event_header header; | ||
95 | u32 pid, tid; | ||
96 | char comm[16]; | ||
97 | }; | ||
98 | |||
99 | struct fork_event { | ||
100 | struct perf_event_header header; | ||
101 | u32 pid, ppid; | ||
102 | u32 tid, ptid; | ||
103 | }; | ||
104 | |||
105 | struct lost_event { | ||
106 | struct perf_event_header header; | ||
107 | u64 id; | ||
108 | u64 lost; | ||
109 | }; | ||
110 | |||
111 | struct read_event { | ||
112 | struct perf_event_header header; | ||
113 | u32 pid,tid; | ||
114 | u64 value; | ||
115 | u64 time_enabled; | ||
116 | u64 time_running; | ||
117 | u64 id; | ||
118 | }; | ||
119 | |||
120 | typedef union event_union { | ||
121 | struct perf_event_header header; | ||
122 | struct ip_event ip; | ||
123 | struct mmap_event mmap; | ||
124 | struct comm_event comm; | ||
125 | struct fork_event fork; | ||
126 | struct lost_event lost; | ||
127 | struct read_event read; | ||
128 | } event_t; | ||
129 | |||
130 | static int repsep_fprintf(FILE *fp, const char *fmt, ...) | 82 | static int repsep_fprintf(FILE *fp, const char *fmt, ...) |
131 | { | 83 | { |
132 | int n; | 84 | int n; |
@@ -154,215 +106,6 @@ static int repsep_fprintf(FILE *fp, const char *fmt, ...) | |||
154 | return n; | 106 | return n; |
155 | } | 107 | } |
156 | 108 | ||
157 | static LIST_HEAD(dsos); | ||
158 | static struct dso *kernel_dso; | ||
159 | static struct dso *vdso; | ||
160 | static struct dso *hypervisor_dso; | ||
161 | |||
162 | static void dsos__add(struct dso *dso) | ||
163 | { | ||
164 | list_add_tail(&dso->node, &dsos); | ||
165 | } | ||
166 | |||
167 | static struct dso *dsos__find(const char *name) | ||
168 | { | ||
169 | struct dso *pos; | ||
170 | |||
171 | list_for_each_entry(pos, &dsos, node) | ||
172 | if (strcmp(pos->name, name) == 0) | ||
173 | return pos; | ||
174 | return NULL; | ||
175 | } | ||
176 | |||
177 | static struct dso *dsos__findnew(const char *name) | ||
178 | { | ||
179 | struct dso *dso = dsos__find(name); | ||
180 | int nr; | ||
181 | |||
182 | if (dso) | ||
183 | return dso; | ||
184 | |||
185 | dso = dso__new(name, 0); | ||
186 | if (!dso) | ||
187 | goto out_delete_dso; | ||
188 | |||
189 | nr = dso__load(dso, NULL, verbose); | ||
190 | if (nr < 0) { | ||
191 | eprintf("Failed to open: %s\n", name); | ||
192 | goto out_delete_dso; | ||
193 | } | ||
194 | if (!nr) | ||
195 | eprintf("No symbols found in: %s, maybe install a debug package?\n", name); | ||
196 | |||
197 | dsos__add(dso); | ||
198 | |||
199 | return dso; | ||
200 | |||
201 | out_delete_dso: | ||
202 | dso__delete(dso); | ||
203 | return NULL; | ||
204 | } | ||
205 | |||
206 | static void dsos__fprintf(FILE *fp) | ||
207 | { | ||
208 | struct dso *pos; | ||
209 | |||
210 | list_for_each_entry(pos, &dsos, node) | ||
211 | dso__fprintf(pos, fp); | ||
212 | } | ||
213 | |||
214 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) | ||
215 | { | ||
216 | return dso__find_symbol(dso, ip); | ||
217 | } | ||
218 | |||
219 | static int load_kernel(void) | ||
220 | { | ||
221 | int err; | ||
222 | |||
223 | kernel_dso = dso__new("[kernel]", 0); | ||
224 | if (!kernel_dso) | ||
225 | return -1; | ||
226 | |||
227 | err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules); | ||
228 | if (err <= 0) { | ||
229 | dso__delete(kernel_dso); | ||
230 | kernel_dso = NULL; | ||
231 | } else | ||
232 | dsos__add(kernel_dso); | ||
233 | |||
234 | vdso = dso__new("[vdso]", 0); | ||
235 | if (!vdso) | ||
236 | return -1; | ||
237 | |||
238 | vdso->find_symbol = vdso__find_symbol; | ||
239 | |||
240 | dsos__add(vdso); | ||
241 | |||
242 | hypervisor_dso = dso__new("[hypervisor]", 0); | ||
243 | if (!hypervisor_dso) | ||
244 | return -1; | ||
245 | dsos__add(hypervisor_dso); | ||
246 | |||
247 | return err; | ||
248 | } | ||
249 | |||
250 | static char __cwd[PATH_MAX]; | ||
251 | static char *cwd = __cwd; | ||
252 | static int cwdlen; | ||
253 | |||
254 | static int strcommon(const char *pathname) | ||
255 | { | ||
256 | int n = 0; | ||
257 | |||
258 | while (n < cwdlen && pathname[n] == cwd[n]) | ||
259 | ++n; | ||
260 | |||
261 | return n; | ||
262 | } | ||
263 | |||
264 | struct map { | ||
265 | struct list_head node; | ||
266 | u64 start; | ||
267 | u64 end; | ||
268 | u64 pgoff; | ||
269 | u64 (*map_ip)(struct map *, u64); | ||
270 | struct dso *dso; | ||
271 | }; | ||
272 | |||
273 | static u64 map__map_ip(struct map *map, u64 ip) | ||
274 | { | ||
275 | return ip - map->start + map->pgoff; | ||
276 | } | ||
277 | |||
278 | static u64 vdso__map_ip(struct map *map __used, u64 ip) | ||
279 | { | ||
280 | return ip; | ||
281 | } | ||
282 | |||
283 | static inline int is_anon_memory(const char *filename) | ||
284 | { | ||
285 | return strcmp(filename, "//anon") == 0; | ||
286 | } | ||
287 | |||
288 | static struct map *map__new(struct mmap_event *event) | ||
289 | { | ||
290 | struct map *self = malloc(sizeof(*self)); | ||
291 | |||
292 | if (self != NULL) { | ||
293 | const char *filename = event->filename; | ||
294 | char newfilename[PATH_MAX]; | ||
295 | int anon; | ||
296 | |||
297 | if (cwd) { | ||
298 | int n = strcommon(filename); | ||
299 | |||
300 | if (n == cwdlen) { | ||
301 | snprintf(newfilename, sizeof(newfilename), | ||
302 | ".%s", filename + n); | ||
303 | filename = newfilename; | ||
304 | } | ||
305 | } | ||
306 | |||
307 | anon = is_anon_memory(filename); | ||
308 | |||
309 | if (anon) { | ||
310 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", event->pid); | ||
311 | filename = newfilename; | ||
312 | } | ||
313 | |||
314 | self->start = event->start; | ||
315 | self->end = event->start + event->len; | ||
316 | self->pgoff = event->pgoff; | ||
317 | |||
318 | self->dso = dsos__findnew(filename); | ||
319 | if (self->dso == NULL) | ||
320 | goto out_delete; | ||
321 | |||
322 | if (self->dso == vdso || anon) | ||
323 | self->map_ip = vdso__map_ip; | ||
324 | else | ||
325 | self->map_ip = map__map_ip; | ||
326 | } | ||
327 | return self; | ||
328 | out_delete: | ||
329 | free(self); | ||
330 | return NULL; | ||
331 | } | ||
332 | |||
333 | static struct map *map__clone(struct map *self) | ||
334 | { | ||
335 | struct map *map = malloc(sizeof(*self)); | ||
336 | |||
337 | if (!map) | ||
338 | return NULL; | ||
339 | |||
340 | memcpy(map, self, sizeof(*self)); | ||
341 | |||
342 | return map; | ||
343 | } | ||
344 | |||
345 | static int map__overlap(struct map *l, struct map *r) | ||
346 | { | ||
347 | if (l->start > r->start) { | ||
348 | struct map *t = l; | ||
349 | l = r; | ||
350 | r = t; | ||
351 | } | ||
352 | |||
353 | if (l->end > r->start) | ||
354 | return 1; | ||
355 | |||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static size_t map__fprintf(struct map *self, FILE *fp) | ||
360 | { | ||
361 | return fprintf(fp, " %Lx-%Lx %Lx %s\n", | ||
362 | self->start, self->end, self->pgoff, self->dso->name); | ||
363 | } | ||
364 | |||
365 | |||
366 | struct thread { | 109 | struct thread { |
367 | struct rb_node rb_node; | 110 | struct rb_node rb_node; |
368 | struct list_head maps; | 111 | struct list_head maps; |
@@ -1397,6 +1140,9 @@ static size_t output__fprintf(FILE *fp, u64 total_samples) | |||
1397 | size_t ret = 0; | 1140 | size_t ret = 0; |
1398 | unsigned int width; | 1141 | unsigned int width; |
1399 | char *col_width = col_width_list_str; | 1142 | char *col_width = col_width_list_str; |
1143 | int raw_printing_style; | ||
1144 | |||
1145 | raw_printing_style = !strcmp(pretty_printing_style, "raw"); | ||
1400 | 1146 | ||
1401 | init_rem_hits(); | 1147 | init_rem_hits(); |
1402 | 1148 | ||
@@ -1473,6 +1219,10 @@ print_entries: | |||
1473 | 1219 | ||
1474 | free(rem_sq_bracket); | 1220 | free(rem_sq_bracket); |
1475 | 1221 | ||
1222 | if (show_threads) | ||
1223 | perf_read_values_display(fp, &show_threads_values, | ||
1224 | raw_printing_style); | ||
1225 | |||
1476 | return ret; | 1226 | return ret; |
1477 | } | 1227 | } |
1478 | 1228 | ||
@@ -1611,7 +1361,7 @@ static int | |||
1611 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) | 1361 | process_mmap_event(event_t *event, unsigned long offset, unsigned long head) |
1612 | { | 1362 | { |
1613 | struct thread *thread = threads__findnew(event->mmap.pid); | 1363 | struct thread *thread = threads__findnew(event->mmap.pid); |
1614 | struct map *map = map__new(&event->mmap); | 1364 | struct map *map = map__new(&event->mmap, cwd, cwdlen); |
1615 | 1365 | ||
1616 | dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", | 1366 | dprintf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", |
1617 | (void *)(offset + head), | 1367 | (void *)(offset + head), |
@@ -1760,6 +1510,16 @@ process_read_event(event_t *event, unsigned long offset, unsigned long head) | |||
1760 | { | 1510 | { |
1761 | struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); | 1511 | struct perf_counter_attr *attr = perf_header__find_attr(event->read.id); |
1762 | 1512 | ||
1513 | if (show_threads) { | ||
1514 | char *name = attr ? __event_name(attr->type, attr->config) | ||
1515 | : "unknown"; | ||
1516 | perf_read_values_add_value(&show_threads_values, | ||
1517 | event->read.pid, event->read.tid, | ||
1518 | event->read.id, | ||
1519 | name, | ||
1520 | event->read.value); | ||
1521 | } | ||
1522 | |||
1763 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", | 1523 | dprintf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", |
1764 | (void *)(offset + head), | 1524 | (void *)(offset + head), |
1765 | (void *)(long)(event->header.size), | 1525 | (void *)(long)(event->header.size), |
@@ -1841,6 +1601,9 @@ static int __cmd_report(void) | |||
1841 | 1601 | ||
1842 | register_idle_thread(); | 1602 | register_idle_thread(); |
1843 | 1603 | ||
1604 | if (show_threads) | ||
1605 | perf_read_values_init(&show_threads_values); | ||
1606 | |||
1844 | input = open(input_name, O_RDONLY); | 1607 | input = open(input_name, O_RDONLY); |
1845 | if (input < 0) { | 1608 | if (input < 0) { |
1846 | fprintf(stderr, " failed to open file: %s", input_name); | 1609 | fprintf(stderr, " failed to open file: %s", input_name); |
@@ -1995,6 +1758,9 @@ done: | |||
1995 | output__resort(total); | 1758 | output__resort(total); |
1996 | output__fprintf(stdout, total); | 1759 | output__fprintf(stdout, total); |
1997 | 1760 | ||
1761 | if (show_threads) | ||
1762 | perf_read_values_destroy(&show_threads_values); | ||
1763 | |||
1998 | return rc; | 1764 | return rc; |
1999 | } | 1765 | } |
2000 | 1766 | ||
@@ -2068,6 +1834,10 @@ static const struct option options[] = { | |||
2068 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 1834 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
2069 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, | 1835 | OPT_BOOLEAN('n', "show-nr-samples", &show_nr_samples, |
2070 | "Show a column with the number of samples"), | 1836 | "Show a column with the number of samples"), |
1837 | OPT_BOOLEAN('T', "threads", &show_threads, | ||
1838 | "Show per-thread event counters"), | ||
1839 | OPT_STRING(0, "pretty", &pretty_printing_style, "key", | ||
1840 | "pretty printing style key: normal raw"), | ||
2071 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 1841 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
2072 | "sort by key(s): pid, comm, dso, symbol, parent"), | 1842 | "sort by key(s): pid, comm, dso, symbol, parent"), |
2073 | OPT_BOOLEAN('P', "full-paths", &full_paths, | 1843 | OPT_BOOLEAN('P', "full-paths", &full_paths, |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index b4b06c7903e1..4b9dd4af61a6 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -63,7 +63,6 @@ static struct perf_counter_attr default_attrs[] = { | |||
63 | #define MAX_RUN 100 | 63 | #define MAX_RUN 100 |
64 | 64 | ||
65 | static int system_wide = 0; | 65 | static int system_wide = 0; |
66 | static int verbose = 0; | ||
67 | static unsigned int nr_cpus = 0; | 66 | static unsigned int nr_cpus = 0; |
68 | static int run_idx = 0; | 67 | static int run_idx = 0; |
69 | 68 | ||
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 7de28ce9ca26..9a6dbbff9a9f 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -68,8 +68,6 @@ static int group = 0; | |||
68 | static unsigned int page_size; | 68 | static unsigned int page_size; |
69 | static unsigned int mmap_pages = 16; | 69 | static unsigned int mmap_pages = 16; |
70 | static int freq = 0; | 70 | static int freq = 0; |
71 | static int verbose = 0; | ||
72 | static char *vmlinux = NULL; | ||
73 | 71 | ||
74 | static int delay_secs = 2; | 72 | static int delay_secs = 2; |
75 | static int zero; | 73 | static int zero; |
@@ -338,8 +336,6 @@ static void show_details(struct sym_entry *syme) | |||
338 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); | 336 | printf("%d lines not displayed, maybe increase display entries [e]\n", more); |
339 | } | 337 | } |
340 | 338 | ||
341 | struct dso *kernel_dso; | ||
342 | |||
343 | /* | 339 | /* |
344 | * Symbols will be added here in record_ip and will get out | 340 | * Symbols will be added here in record_ip and will get out |
345 | * after decayed. | 341 | * after decayed. |
@@ -937,26 +933,6 @@ static void mmap_read_counter(struct mmap_data *md) | |||
937 | last_read = this_read; | 933 | last_read = this_read; |
938 | 934 | ||
939 | for (; old != head;) { | 935 | for (; old != head;) { |
940 | struct ip_event { | ||
941 | struct perf_event_header header; | ||
942 | u64 ip; | ||
943 | u32 pid, target_pid; | ||
944 | }; | ||
945 | struct mmap_event { | ||
946 | struct perf_event_header header; | ||
947 | u32 pid, target_pid; | ||
948 | u64 start; | ||
949 | u64 len; | ||
950 | u64 pgoff; | ||
951 | char filename[PATH_MAX]; | ||
952 | }; | ||
953 | |||
954 | typedef union event_union { | ||
955 | struct perf_event_header header; | ||
956 | struct ip_event ip; | ||
957 | struct mmap_event mmap; | ||
958 | } event_t; | ||
959 | |||
960 | event_t *event = (event_t *)&data[old & md->mask]; | 936 | event_t *event = (event_t *)&data[old & md->mask]; |
961 | 937 | ||
962 | event_t event_copy; | 938 | event_t event_copy; |
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 51d168230ee7..3a63e41fb44e 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -22,5 +22,6 @@ extern int cmd_stat(int argc, const char **argv, const char *prefix); | |||
22 | extern int cmd_top(int argc, const char **argv, const char *prefix); | 22 | extern int cmd_top(int argc, const char **argv, const char *prefix); |
23 | extern int cmd_version(int argc, const char **argv, const char *prefix); | 23 | extern int cmd_version(int argc, const char **argv, const char *prefix); |
24 | extern int cmd_list(int argc, const char **argv, const char *prefix); | 24 | extern int cmd_list(int argc, const char **argv, const char *prefix); |
25 | extern int cmd_trace(int argc, const char **argv, const char *prefix); | ||
25 | 26 | ||
26 | #endif | 27 | #endif |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index e5148e2b6134..f5509213f030 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -48,6 +48,7 @@ | |||
48 | 48 | ||
49 | #include "../../include/linux/perf_counter.h" | 49 | #include "../../include/linux/perf_counter.h" |
50 | #include "util/types.h" | 50 | #include "util/types.h" |
51 | #include "util/debug.h" | ||
51 | 52 | ||
52 | /* | 53 | /* |
53 | * prctl(PR_TASK_PERF_COUNTERS_DISABLE) will (cheaply) disable all | 54 | * prctl(PR_TASK_PERF_COUNTERS_DISABLE) will (cheaply) disable all |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index a926ae4f5a16..43cf3ea9e088 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "../perf.h" | 4 | #include "../perf.h" |
5 | #include <linux/list.h> | 5 | #include <linux/list.h> |
6 | #include <linux/rbtree.h> | 6 | #include <linux/rbtree.h> |
7 | #include "util.h" | ||
7 | #include "symbol.h" | 8 | #include "symbol.h" |
8 | 9 | ||
9 | enum chain_mode { | 10 | enum chain_mode { |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c new file mode 100644 index 000000000000..7cb8464abe61 --- /dev/null +++ b/tools/perf/util/debug.c | |||
@@ -0,0 +1,22 @@ | |||
1 | /* For general debugging purposes */ | ||
2 | |||
3 | #include "../perf.h" | ||
4 | #include <string.h> | ||
5 | #include <stdarg.h> | ||
6 | #include <stdio.h> | ||
7 | |||
8 | int verbose = 0; | ||
9 | |||
10 | int eprintf(const char *fmt, ...) | ||
11 | { | ||
12 | va_list args; | ||
13 | int ret = 0; | ||
14 | |||
15 | if (verbose) { | ||
16 | va_start(args, fmt); | ||
17 | ret = vfprintf(stderr, fmt, args); | ||
18 | va_end(args); | ||
19 | } | ||
20 | |||
21 | return ret; | ||
22 | } | ||
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h new file mode 100644 index 000000000000..2ae9090108d3 --- /dev/null +++ b/tools/perf/util/debug.h | |||
@@ -0,0 +1,5 @@ | |||
1 | /* For debugging general purposes */ | ||
2 | |||
3 | extern int verbose; | ||
4 | |||
5 | int eprintf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h new file mode 100644 index 000000000000..fa7c50b654e1 --- /dev/null +++ b/tools/perf/util/event.h | |||
@@ -0,0 +1,90 @@ | |||
1 | #ifndef __PERF_EVENT_H | ||
2 | #define __PERF_EVENT_H | ||
3 | #include "../perf.h" | ||
4 | #include "util.h" | ||
5 | #include <linux/list.h> | ||
6 | |||
7 | /* | ||
8 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * | ||
9 | */ | ||
10 | struct ip_event { | ||
11 | struct perf_event_header header; | ||
12 | u64 ip; | ||
13 | u32 pid, tid; | ||
14 | unsigned char __more_data[]; | ||
15 | }; | ||
16 | |||
17 | struct mmap_event { | ||
18 | struct perf_event_header header; | ||
19 | u32 pid, tid; | ||
20 | u64 start; | ||
21 | u64 len; | ||
22 | u64 pgoff; | ||
23 | char filename[PATH_MAX]; | ||
24 | }; | ||
25 | |||
26 | struct comm_event { | ||
27 | struct perf_event_header header; | ||
28 | u32 pid, tid; | ||
29 | char comm[16]; | ||
30 | }; | ||
31 | |||
32 | struct fork_event { | ||
33 | struct perf_event_header header; | ||
34 | u32 pid, ppid; | ||
35 | u32 tid, ptid; | ||
36 | }; | ||
37 | |||
38 | struct lost_event { | ||
39 | struct perf_event_header header; | ||
40 | u64 id; | ||
41 | u64 lost; | ||
42 | }; | ||
43 | |||
44 | /* | ||
45 | * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID | ||
46 | */ | ||
47 | struct read_event { | ||
48 | struct perf_event_header header; | ||
49 | u32 pid,tid; | ||
50 | u64 value; | ||
51 | u64 time_enabled; | ||
52 | u64 time_running; | ||
53 | u64 id; | ||
54 | }; | ||
55 | |||
56 | typedef union event_union { | ||
57 | struct perf_event_header header; | ||
58 | struct ip_event ip; | ||
59 | struct mmap_event mmap; | ||
60 | struct comm_event comm; | ||
61 | struct fork_event fork; | ||
62 | struct lost_event lost; | ||
63 | struct read_event read; | ||
64 | } event_t; | ||
65 | |||
66 | struct map { | ||
67 | struct list_head node; | ||
68 | u64 start; | ||
69 | u64 end; | ||
70 | u64 pgoff; | ||
71 | u64 (*map_ip)(struct map *, u64); | ||
72 | struct dso *dso; | ||
73 | }; | ||
74 | |||
75 | static inline u64 map__map_ip(struct map *map, u64 ip) | ||
76 | { | ||
77 | return ip - map->start + map->pgoff; | ||
78 | } | ||
79 | |||
80 | static inline u64 vdso__map_ip(struct map *map __used, u64 ip) | ||
81 | { | ||
82 | return ip; | ||
83 | } | ||
84 | |||
85 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen); | ||
86 | struct map *map__clone(struct map *self); | ||
87 | int map__overlap(struct map *l, struct map *r); | ||
88 | size_t map__fprintf(struct map *self, FILE *fp); | ||
89 | |||
90 | #endif | ||
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c new file mode 100644 index 000000000000..804e02382739 --- /dev/null +++ b/tools/perf/util/map.c | |||
@@ -0,0 +1,97 @@ | |||
1 | #include "event.h" | ||
2 | #include "symbol.h" | ||
3 | #include <stdlib.h> | ||
4 | #include <string.h> | ||
5 | #include <stdio.h> | ||
6 | |||
7 | static inline int is_anon_memory(const char *filename) | ||
8 | { | ||
9 | return strcmp(filename, "//anon") == 0; | ||
10 | } | ||
11 | |||
12 | static int strcommon(const char *pathname, char *cwd, int cwdlen) | ||
13 | { | ||
14 | int n = 0; | ||
15 | |||
16 | while (n < cwdlen && pathname[n] == cwd[n]) | ||
17 | ++n; | ||
18 | |||
19 | return n; | ||
20 | } | ||
21 | |||
22 | struct map *map__new(struct mmap_event *event, char *cwd, int cwdlen) | ||
23 | { | ||
24 | struct map *self = malloc(sizeof(*self)); | ||
25 | |||
26 | if (self != NULL) { | ||
27 | const char *filename = event->filename; | ||
28 | char newfilename[PATH_MAX]; | ||
29 | int anon; | ||
30 | |||
31 | if (cwd) { | ||
32 | int n = strcommon(filename, cwd, cwdlen); | ||
33 | |||
34 | if (n == cwdlen) { | ||
35 | snprintf(newfilename, sizeof(newfilename), | ||
36 | ".%s", filename + n); | ||
37 | filename = newfilename; | ||
38 | } | ||
39 | } | ||
40 | |||
41 | anon = is_anon_memory(filename); | ||
42 | |||
43 | if (anon) { | ||
44 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", event->pid); | ||
45 | filename = newfilename; | ||
46 | } | ||
47 | |||
48 | self->start = event->start; | ||
49 | self->end = event->start + event->len; | ||
50 | self->pgoff = event->pgoff; | ||
51 | |||
52 | self->dso = dsos__findnew(filename); | ||
53 | if (self->dso == NULL) | ||
54 | goto out_delete; | ||
55 | |||
56 | if (self->dso == vdso || anon) | ||
57 | self->map_ip = vdso__map_ip; | ||
58 | else | ||
59 | self->map_ip = map__map_ip; | ||
60 | } | ||
61 | return self; | ||
62 | out_delete: | ||
63 | free(self); | ||
64 | return NULL; | ||
65 | } | ||
66 | |||
67 | struct map *map__clone(struct map *self) | ||
68 | { | ||
69 | struct map *map = malloc(sizeof(*self)); | ||
70 | |||
71 | if (!map) | ||
72 | return NULL; | ||
73 | |||
74 | memcpy(map, self, sizeof(*self)); | ||
75 | |||
76 | return map; | ||
77 | } | ||
78 | |||
79 | int map__overlap(struct map *l, struct map *r) | ||
80 | { | ||
81 | if (l->start > r->start) { | ||
82 | struct map *t = l; | ||
83 | l = r; | ||
84 | r = t; | ||
85 | } | ||
86 | |||
87 | if (l->end > r->start) | ||
88 | return 1; | ||
89 | |||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | size_t map__fprintf(struct map *self, FILE *fp) | ||
94 | { | ||
95 | return fprintf(fp, " %Lx-%Lx %Lx %s\n", | ||
96 | self->start, self->end, self->pgoff, self->dso->name); | ||
97 | } | ||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 5c0f42e6b33b..0b9862351260 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -924,6 +924,103 @@ int dso__load_kernel(struct dso *self, const char *vmlinux, | |||
924 | return err; | 924 | return err; |
925 | } | 925 | } |
926 | 926 | ||
927 | LIST_HEAD(dsos); | ||
928 | struct dso *kernel_dso; | ||
929 | struct dso *vdso; | ||
930 | struct dso *hypervisor_dso; | ||
931 | |||
932 | char *vmlinux = "vmlinux"; | ||
933 | int modules; | ||
934 | |||
935 | static void dsos__add(struct dso *dso) | ||
936 | { | ||
937 | list_add_tail(&dso->node, &dsos); | ||
938 | } | ||
939 | |||
940 | static struct dso *dsos__find(const char *name) | ||
941 | { | ||
942 | struct dso *pos; | ||
943 | |||
944 | list_for_each_entry(pos, &dsos, node) | ||
945 | if (strcmp(pos->name, name) == 0) | ||
946 | return pos; | ||
947 | return NULL; | ||
948 | } | ||
949 | |||
950 | struct dso *dsos__findnew(const char *name) | ||
951 | { | ||
952 | struct dso *dso = dsos__find(name); | ||
953 | int nr; | ||
954 | |||
955 | if (dso) | ||
956 | return dso; | ||
957 | |||
958 | dso = dso__new(name, 0); | ||
959 | if (!dso) | ||
960 | goto out_delete_dso; | ||
961 | |||
962 | nr = dso__load(dso, NULL, verbose); | ||
963 | if (nr < 0) { | ||
964 | eprintf("Failed to open: %s\n", name); | ||
965 | goto out_delete_dso; | ||
966 | } | ||
967 | if (!nr) | ||
968 | eprintf("No symbols found in: %s, maybe install a debug package?\n", name); | ||
969 | |||
970 | dsos__add(dso); | ||
971 | |||
972 | return dso; | ||
973 | |||
974 | out_delete_dso: | ||
975 | dso__delete(dso); | ||
976 | return NULL; | ||
977 | } | ||
978 | |||
979 | void dsos__fprintf(FILE *fp) | ||
980 | { | ||
981 | struct dso *pos; | ||
982 | |||
983 | list_for_each_entry(pos, &dsos, node) | ||
984 | dso__fprintf(pos, fp); | ||
985 | } | ||
986 | |||
987 | static struct symbol *vdso__find_symbol(struct dso *dso, u64 ip) | ||
988 | { | ||
989 | return dso__find_symbol(dso, ip); | ||
990 | } | ||
991 | |||
992 | int load_kernel(void) | ||
993 | { | ||
994 | int err; | ||
995 | |||
996 | kernel_dso = dso__new("[kernel]", 0); | ||
997 | if (!kernel_dso) | ||
998 | return -1; | ||
999 | |||
1000 | err = dso__load_kernel(kernel_dso, vmlinux, NULL, verbose, modules); | ||
1001 | if (err <= 0) { | ||
1002 | dso__delete(kernel_dso); | ||
1003 | kernel_dso = NULL; | ||
1004 | } else | ||
1005 | dsos__add(kernel_dso); | ||
1006 | |||
1007 | vdso = dso__new("[vdso]", 0); | ||
1008 | if (!vdso) | ||
1009 | return -1; | ||
1010 | |||
1011 | vdso->find_symbol = vdso__find_symbol; | ||
1012 | |||
1013 | dsos__add(vdso); | ||
1014 | |||
1015 | hypervisor_dso = dso__new("[hypervisor]", 0); | ||
1016 | if (!hypervisor_dso) | ||
1017 | return -1; | ||
1018 | dsos__add(hypervisor_dso); | ||
1019 | |||
1020 | return err; | ||
1021 | } | ||
1022 | |||
1023 | |||
927 | void symbol__init(void) | 1024 | void symbol__init(void) |
928 | { | 1025 | { |
929 | elf_version(EV_CURRENT); | 1026 | elf_version(EV_CURRENT); |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index b53bf0125c1b..48b8e5759af9 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <linux/list.h> | 6 | #include <linux/list.h> |
7 | #include <linux/rbtree.h> | 7 | #include <linux/rbtree.h> |
8 | #include "module.h" | 8 | #include "module.h" |
9 | #include "event.h" | ||
9 | 10 | ||
10 | #ifdef HAVE_CPLUS_DEMANGLE | 11 | #ifdef HAVE_CPLUS_DEMANGLE |
11 | extern char *cplus_demangle(const char *, int); | 12 | extern char *cplus_demangle(const char *, int); |
@@ -72,9 +73,20 @@ int dso__load_kernel(struct dso *self, const char *vmlinux, | |||
72 | symbol_filter_t filter, int verbose, int modules); | 73 | symbol_filter_t filter, int verbose, int modules); |
73 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose); | 74 | int dso__load_modules(struct dso *self, symbol_filter_t filter, int verbose); |
74 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose); | 75 | int dso__load(struct dso *self, symbol_filter_t filter, int verbose); |
76 | struct dso *dsos__findnew(const char *name); | ||
77 | void dsos__fprintf(FILE *fp); | ||
75 | 78 | ||
76 | size_t dso__fprintf(struct dso *self, FILE *fp); | 79 | size_t dso__fprintf(struct dso *self, FILE *fp); |
77 | char dso__symtab_origin(const struct dso *self); | 80 | char dso__symtab_origin(const struct dso *self); |
78 | 81 | ||
82 | int load_kernel(void); | ||
83 | |||
79 | void symbol__init(void); | 84 | void symbol__init(void); |
85 | |||
86 | extern struct list_head dsos; | ||
87 | extern struct dso *kernel_dso; | ||
88 | extern struct dso *vdso; | ||
89 | extern struct dso *hypervisor_dso; | ||
90 | extern char *vmlinux; | ||
91 | extern int modules; | ||
80 | #endif /* _PERF_SYMBOL_ */ | 92 | #endif /* _PERF_SYMBOL_ */ |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 68fe157d72fb..d61a6f037631 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -83,6 +83,7 @@ | |||
83 | #include <inttypes.h> | 83 | #include <inttypes.h> |
84 | #include "../../../include/linux/magic.h" | 84 | #include "../../../include/linux/magic.h" |
85 | 85 | ||
86 | |||
86 | #ifndef NO_ICONV | 87 | #ifndef NO_ICONV |
87 | #include <iconv.h> | 88 | #include <iconv.h> |
88 | #endif | 89 | #endif |
diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c new file mode 100644 index 000000000000..614cfaf4712a --- /dev/null +++ b/tools/perf/util/values.c | |||
@@ -0,0 +1,231 @@ | |||
1 | #include <stdlib.h> | ||
2 | |||
3 | #include "util.h" | ||
4 | #include "values.h" | ||
5 | |||
6 | void perf_read_values_init(struct perf_read_values *values) | ||
7 | { | ||
8 | values->threads_max = 16; | ||
9 | values->pid = malloc(values->threads_max * sizeof(*values->pid)); | ||
10 | values->tid = malloc(values->threads_max * sizeof(*values->tid)); | ||
11 | values->value = malloc(values->threads_max * sizeof(*values->value)); | ||
12 | if (!values->pid || !values->tid || !values->value) | ||
13 | die("failed to allocate read_values threads arrays"); | ||
14 | values->threads = 0; | ||
15 | |||
16 | values->counters_max = 16; | ||
17 | values->counterrawid = malloc(values->counters_max | ||
18 | * sizeof(*values->counterrawid)); | ||
19 | values->countername = malloc(values->counters_max | ||
20 | * sizeof(*values->countername)); | ||
21 | if (!values->counterrawid || !values->countername) | ||
22 | die("failed to allocate read_values counters arrays"); | ||
23 | values->counters = 0; | ||
24 | } | ||
25 | |||
26 | void perf_read_values_destroy(struct perf_read_values *values) | ||
27 | { | ||
28 | int i; | ||
29 | |||
30 | if (!values->threads_max || !values->counters_max) | ||
31 | return; | ||
32 | |||
33 | for (i = 0; i < values->threads; i++) | ||
34 | free(values->value[i]); | ||
35 | free(values->pid); | ||
36 | free(values->tid); | ||
37 | free(values->counterrawid); | ||
38 | for (i = 0; i < values->counters; i++) | ||
39 | free(values->countername[i]); | ||
40 | free(values->countername); | ||
41 | } | ||
42 | |||
43 | static void perf_read_values__enlarge_threads(struct perf_read_values *values) | ||
44 | { | ||
45 | values->threads_max *= 2; | ||
46 | values->pid = realloc(values->pid, | ||
47 | values->threads_max * sizeof(*values->pid)); | ||
48 | values->tid = realloc(values->tid, | ||
49 | values->threads_max * sizeof(*values->tid)); | ||
50 | values->value = realloc(values->value, | ||
51 | values->threads_max * sizeof(*values->value)); | ||
52 | if (!values->pid || !values->tid || !values->value) | ||
53 | die("failed to enlarge read_values threads arrays"); | ||
54 | } | ||
55 | |||
56 | static int perf_read_values__findnew_thread(struct perf_read_values *values, | ||
57 | u32 pid, u32 tid) | ||
58 | { | ||
59 | int i; | ||
60 | |||
61 | for (i = 0; i < values->threads; i++) | ||
62 | if (values->pid[i] == pid && values->tid[i] == tid) | ||
63 | return i; | ||
64 | |||
65 | if (values->threads == values->threads_max) | ||
66 | perf_read_values__enlarge_threads(values); | ||
67 | |||
68 | i = values->threads++; | ||
69 | values->pid[i] = pid; | ||
70 | values->tid[i] = tid; | ||
71 | values->value[i] = malloc(values->counters_max * sizeof(**values->value)); | ||
72 | if (!values->value[i]) | ||
73 | die("failed to allocate read_values counters array"); | ||
74 | |||
75 | return i; | ||
76 | } | ||
77 | |||
78 | static void perf_read_values__enlarge_counters(struct perf_read_values *values) | ||
79 | { | ||
80 | int i; | ||
81 | |||
82 | values->counters_max *= 2; | ||
83 | values->counterrawid = realloc(values->counterrawid, | ||
84 | values->counters_max * sizeof(*values->counterrawid)); | ||
85 | values->countername = realloc(values->countername, | ||
86 | values->counters_max * sizeof(*values->countername)); | ||
87 | if (!values->counterrawid || !values->countername) | ||
88 | die("failed to enlarge read_values counters arrays"); | ||
89 | |||
90 | for (i = 0; i < values->threads; i++) { | ||
91 | values->value[i] = realloc(values->value[i], | ||
92 | values->counters_max * sizeof(**values->value)); | ||
93 | if (!values->value[i]) | ||
94 | die("failed to enlarge read_values counters arrays"); | ||
95 | } | ||
96 | } | ||
97 | |||
98 | static int perf_read_values__findnew_counter(struct perf_read_values *values, | ||
99 | u64 rawid, char *name) | ||
100 | { | ||
101 | int i; | ||
102 | |||
103 | for (i = 0; i < values->counters; i++) | ||
104 | if (values->counterrawid[i] == rawid) | ||
105 | return i; | ||
106 | |||
107 | if (values->counters == values->counters_max) | ||
108 | perf_read_values__enlarge_counters(values); | ||
109 | |||
110 | i = values->counters++; | ||
111 | values->counterrawid[i] = rawid; | ||
112 | values->countername[i] = strdup(name); | ||
113 | |||
114 | return i; | ||
115 | } | ||
116 | |||
117 | void perf_read_values_add_value(struct perf_read_values *values, | ||
118 | u32 pid, u32 tid, | ||
119 | u64 rawid, char *name, u64 value) | ||
120 | { | ||
121 | int tindex, cindex; | ||
122 | |||
123 | tindex = perf_read_values__findnew_thread(values, pid, tid); | ||
124 | cindex = perf_read_values__findnew_counter(values, rawid, name); | ||
125 | |||
126 | values->value[tindex][cindex] = value; | ||
127 | } | ||
128 | |||
129 | static void perf_read_values__display_pretty(FILE *fp, | ||
130 | struct perf_read_values *values) | ||
131 | { | ||
132 | int i, j; | ||
133 | int pidwidth, tidwidth; | ||
134 | int *counterwidth; | ||
135 | |||
136 | counterwidth = malloc(values->counters * sizeof(*counterwidth)); | ||
137 | if (!counterwidth) | ||
138 | die("failed to allocate counterwidth array"); | ||
139 | tidwidth = 3; | ||
140 | pidwidth = 3; | ||
141 | for (j = 0; j < values->counters; j++) | ||
142 | counterwidth[j] = strlen(values->countername[j]); | ||
143 | for (i = 0; i < values->threads; i++) { | ||
144 | int width; | ||
145 | |||
146 | width = snprintf(NULL, 0, "%d", values->pid[i]); | ||
147 | if (width > pidwidth) | ||
148 | pidwidth = width; | ||
149 | width = snprintf(NULL, 0, "%d", values->tid[i]); | ||
150 | if (width > tidwidth) | ||
151 | tidwidth = width; | ||
152 | for (j = 0; j < values->counters; j++) { | ||
153 | width = snprintf(NULL, 0, "%Lu", values->value[i][j]); | ||
154 | if (width > counterwidth[j]) | ||
155 | counterwidth[j] = width; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | fprintf(fp, "# %*s %*s", pidwidth, "PID", tidwidth, "TID"); | ||
160 | for (j = 0; j < values->counters; j++) | ||
161 | fprintf(fp, " %*s", counterwidth[j], values->countername[j]); | ||
162 | fprintf(fp, "\n"); | ||
163 | |||
164 | for (i = 0; i < values->threads; i++) { | ||
165 | fprintf(fp, " %*d %*d", pidwidth, values->pid[i], | ||
166 | tidwidth, values->tid[i]); | ||
167 | for (j = 0; j < values->counters; j++) | ||
168 | fprintf(fp, " %*Lu", | ||
169 | counterwidth[j], values->value[i][j]); | ||
170 | fprintf(fp, "\n"); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | static void perf_read_values__display_raw(FILE *fp, | ||
175 | struct perf_read_values *values) | ||
176 | { | ||
177 | int width, pidwidth, tidwidth, namewidth, rawwidth, countwidth; | ||
178 | int i, j; | ||
179 | |||
180 | tidwidth = 3; /* TID */ | ||
181 | pidwidth = 3; /* PID */ | ||
182 | namewidth = 4; /* "Name" */ | ||
183 | rawwidth = 3; /* "Raw" */ | ||
184 | countwidth = 5; /* "Count" */ | ||
185 | |||
186 | for (i = 0; i < values->threads; i++) { | ||
187 | width = snprintf(NULL, 0, "%d", values->pid[i]); | ||
188 | if (width > pidwidth) | ||
189 | pidwidth = width; | ||
190 | width = snprintf(NULL, 0, "%d", values->tid[i]); | ||
191 | if (width > tidwidth) | ||
192 | tidwidth = width; | ||
193 | } | ||
194 | for (j = 0; j < values->counters; j++) { | ||
195 | width = strlen(values->countername[j]); | ||
196 | if (width > namewidth) | ||
197 | namewidth = width; | ||
198 | width = snprintf(NULL, 0, "%llx", values->counterrawid[j]); | ||
199 | if (width > rawwidth) | ||
200 | rawwidth = width; | ||
201 | } | ||
202 | for (i = 0; i < values->threads; i++) { | ||
203 | for (j = 0; j < values->counters; j++) { | ||
204 | width = snprintf(NULL, 0, "%Lu", values->value[i][j]); | ||
205 | if (width > countwidth) | ||
206 | countwidth = width; | ||
207 | } | ||
208 | } | ||
209 | |||
210 | fprintf(fp, "# %*s %*s %*s %*s %*s\n", | ||
211 | pidwidth, "PID", tidwidth, "TID", | ||
212 | namewidth, "Name", rawwidth, "Raw", | ||
213 | countwidth, "Count"); | ||
214 | for (i = 0; i < values->threads; i++) | ||
215 | for (j = 0; j < values->counters; j++) | ||
216 | fprintf(fp, " %*d %*d %*s %*llx %*Lu\n", | ||
217 | pidwidth, values->pid[i], | ||
218 | tidwidth, values->tid[i], | ||
219 | namewidth, values->countername[j], | ||
220 | rawwidth, values->counterrawid[j], | ||
221 | countwidth, values->value[i][j]); | ||
222 | } | ||
223 | |||
224 | void perf_read_values_display(FILE *fp, struct perf_read_values *values, | ||
225 | int raw) | ||
226 | { | ||
227 | if (raw) | ||
228 | perf_read_values__display_raw(fp, values); | ||
229 | else | ||
230 | perf_read_values__display_pretty(fp, values); | ||
231 | } | ||
diff --git a/tools/perf/util/values.h b/tools/perf/util/values.h new file mode 100644 index 000000000000..f8960fde0547 --- /dev/null +++ b/tools/perf/util/values.h | |||
@@ -0,0 +1,27 @@ | |||
1 | #ifndef _PERF_VALUES_H | ||
2 | #define _PERF_VALUES_H | ||
3 | |||
4 | #include "types.h" | ||
5 | |||
6 | struct perf_read_values { | ||
7 | int threads; | ||
8 | int threads_max; | ||
9 | u32 *pid, *tid; | ||
10 | int counters; | ||
11 | int counters_max; | ||
12 | u64 *counterrawid; | ||
13 | char **countername; | ||
14 | u64 **value; | ||
15 | }; | ||
16 | |||
17 | void perf_read_values_init(struct perf_read_values *values); | ||
18 | void perf_read_values_destroy(struct perf_read_values *values); | ||
19 | |||
20 | void perf_read_values_add_value(struct perf_read_values *values, | ||
21 | u32 pid, u32 tid, | ||
22 | u64 rawid, char *name, u64 value); | ||
23 | |||
24 | void perf_read_values_display(FILE *fp, struct perf_read_values *values, | ||
25 | int raw); | ||
26 | |||
27 | #endif /* _PERF_VALUES_H */ | ||